Merge remote-tracking branch 'origin/main' into dev-lavalink

Update branch with main
This commit is contained in:
Mylloon 2021-08-06 12:10:12 +02:00
commit 343818bb7e
26 changed files with 2016 additions and 632 deletions

7
.envexample Normal file
View file

@ -0,0 +1,7 @@
TOKEN_DISCORD=
TOKEN_GENIUS=
TOKEN_REDDIT_CLIENT_ID=
TOKEN_REDDIT_CLIENT_SECRET=
TOKEN_REDDIT_USER_AGENT=
TIMEZONE=
PREFIX=

6
.gitignore vendored
View file

@ -1,5 +1,5 @@
.vscode/
update/
src/cogs/music_old.py
src/cogs/__pycache__/
.envrc
__pycache__/
.env
*.sqlite3

65
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,65 @@
###############################################################
# Setting I use for cleaning up image tags #
# - Running cleanup every week #
# - Keeping 1 tag per image name matching : (?:v.\d+|dev) #
# - Removing tags older than 7 days matching the default : .* #
###############################################################
image: docker:stable
stages:
- build
- push
services:
- docker:dind
before_script:
- echo -n $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY
Build:
stage: build
script:
- docker pull $CI_REGISTRY_IMAGE:dev || true
- >
docker build
--pull
--build-arg VCS_REF=$CI_COMMIT_SHA
--build-arg VCS_URL=$CI_PROJECT_URL
--cache-from $CI_REGISTRY_IMAGE:dev
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
.
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
Push latest:
variables:
GIT_STRATEGY: none
stage: push
only:
- main
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
Push dev:
variables:
GIT_STRATEGY: none
stage: push
only:
- dev
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:dev
- docker push $CI_REGISTRY_IMAGE:dev
Push tag:
variables:
GIT_STRATEGY: none
stage: push
only:
- tags
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME

View file

@ -0,0 +1,13 @@
## Quick Information
- **Python version:** <!-- python3 --version !-->
- **Bot version:** <!-- Command info give you that !-->
- **Client device where the bug occurred:** Computer / Mobile <!-- If needed !-->
## What Happened?
...
## Expected result
...
## Steps to reproduce
...

View file

@ -8,6 +8,7 @@ RUN pip install -r requirements.txt
RUN apt update && \
apt install -y --no-install-recommends ffmpeg
COPY README.md .
COPY src .
CMD [ "python", "-u", "./main.py" ]

201
LICENSE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,36 +1,46 @@
# Bot developed with [discord.py](https://github.com/Rapptz/discord.py) (rewrite) - FRENCH
[![Version](https://img.shields.io/badge/version-1.0-green?style=for-the-badge)](https://github.com/Confrerie-du-Kassoulait/KassouBot/releases/latest)
[![Docker Stars](https://img.shields.io/docker/stars/mylloon/kassoubot?style=for-the-badge)](https://hub.docker.com/repository/docker/mylloon/kassoubot)
[![Docker Pulls](https://img.shields.io/docker/pulls/mylloon/kassoubot?style=for-the-badge)](https://hub.docker.com/repository/docker/mylloon/kassoubot)
[![Github stars](https://img.shields.io/github/stars/Confrerie-du-Kassoulait/kassoubot?label=Github%20Stars&style=for-the-badge)](https://github.com/Confrerie-du-Kassoulait/KassouBot/stargazers)
[![Github forks](https://img.shields.io/github/forks/Confrerie-du-Kassoulait/KassouBot?label=Github%20Forks&style=for-the-badge)](https://github.com/Confrerie-du-Kassoulait/KassouBot/network)
[![Version](https://img.shields.io/badge/version-1.5-green?style=for-the-badge)](https://gitlab.com/ConfrerieDuKassoulait/KassouBot/-/releases)
[![Build](https://img.shields.io/gitlab/pipeline/ConfrerieDuKassoulait/KassouBot/dev?style=for-the-badge)](https://gitlab.com/ConfrerieDuKassoulait/KassouBot/container_registry)
## __Setting up__
You have to replace `TOKEN_DISCORD`, `TOKEN_GENIUS`, `TOKEN_REDDIT_CLIENT_ID`, `TOKEN_REDDIT_CLIENT_SECRET` and `TOKEN_REDDIT_USER_AGENT` with your desired values.
With a [docker-compose](https://github.com/Confrerie-du-Kassoulait/KassouBot/blob/master/docker-compose.yml) or in command line:
You have to replace `TOKEN_DISCORD`, `PREFIX`, `TOKEN_GENIUS`, `TOKEN_REDDIT_CLIENT_ID`, `TOKEN_REDDIT_CLIENT_SECRET`, `TOKEN_REDDIT_USER_AGENT` and [`TIMEZONE`](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) with your desired values. You must also specify a path to the folder where the database will be.\
With a [docker-compose](https://gitlab.com/ConfrerieDuKassoulait/KassouBot/-/blob/main/docker-compose.yml) or in command line:
```
docker run -d \
--name="kassoubot" \
mylloon/kassoubot \
--TOKEN_DISCORD="yourValue" \
--TOKEN_GENIUS="yourValue" \
--TOKEN_REDDIT_CLIENT_ID="yourValue" \
--TOKEN_REDDIT_CLIENT_SECRET="yourValue" \
--TOKEN_REDDIT_USER_AGENT="yourValue"
--name="KassouBot" \
registry.gitlab.com/confreriedukassoulait/kassoubot \
--TOKEN_DISCORD="yourTokenDiscord" \
--TOKEN_GENIUS="yourTokenGenius" \
--TOKEN_REDDIT_CLIENT_ID="yourRedditClientID" \
--TOKEN_REDDIT_CLIENT_SECRET="yourRedditClientSecret" \
--TOKEN_REDDIT_USER_AGENT="yourRedditUserAgent" \
--TIMEZONE="yourTimezone" \
--PREFIX="yourPrefix" \
-v /here/your/path/:/db
```
To find reddit tokens, go to [this site](https://www.reddit.com/prefs/apps) and here are the instructions: ![instructions](https://i.imgur.com/tEzYKDA.png)
You can add the environment variable `DEACTIVATE` to disable some cogs (separate the cogs with commas and no spaces between).
To find reddit tokens, go to [this site](https://www.reddit.com/prefs/apps) and here are the instructions: ![instructions](https://i.imgur.com/tEzYKDA.png)
*redirection uri (for copy/paste) : http://localhost:8080*
To find Genius token, go to [this site](https://genius.com/api-clients), `login to your account` and on the left select `New API Client`. Fill the field with what you want then click `Save`. Now your token is the `CLIENT ACCESS TOKEN`.
## __Add the bot to your server__
- [This site](https://discordapi.com/permissions.html) allows you to choose which permissions to add by default to the bot.
- Choose *Administrator* so you don't get in over your head.
- Copy and paste the ID of your bot in *Client ID* found [here](https://discord.com/developers/applications) and go to the link at the bottom of the page.
- In the [Discord Dev Portal](https://discord.com/developers/applications) create an application, and make sure it's a `Bot` (third tab).
- To invite it, go to the `OAuth2` (second tab) tab, select the scopes `bot` (required) and `applications.commands` (for the slashs commands) and in the bot permissions select `Administrator` (You can select manually at your own risk).
- You have the link to copy above between the two blocks `scopes` and `permissions`.
- If you need help, you can [join my Discord](https://discord.gg/Z5ePxH4).
## __Features__
Everything is explained by doing `.help`
- Everything is explained by doing the `help` command.
- Using SQLite for the database.
## __Launching locally__
If you want to run it without Docker, create an .env file to store variables in the root folder (there is an example [here](https://gitlab.com/ConfrerieDuKassoulait/KassouBot/-/blob/main/.envexample)).
Install ffmpeg by doing `sudo apt install ffmpeg` and all the requirements by doing `python3 -m pip install -r requirements.txt`
Simply run `python3 main.py` inside the `src` folder to launch the bot in the repo folder.

View file

@ -1,12 +1,16 @@
version: "2.1"
services:
kassoubot:
image: mylloon/kassoubot
image: registry.gitlab.com/confreriedukassoulait/kassoubot
container_name: KassouBot
environment:
- TOKEN_DISCORD=your-token-discord
- TOKEN_GENIUS=your-token-genius
- TOKEN_REDDIT_CLIENT_ID=your-reddit-client-id
- TOKEN_REDDIT_CLIENT_SECRET=your-reddit-client-secret
- TOKEN_REDDIT_USER_AGENT=your-reddit-user-agent
- TOKEN_DISCORD=yourTokenDiscord
- TOKEN_GENIUS=yourTokenGenius
- TOKEN_REDDIT_CLIENT_ID=yourRedditClientID
- TOKEN_REDDIT_CLIENT_SECRET=yourRedditClientSecret
- TOKEN_REDDIT_USER_AGENT=yourRedditUserAgent
- TIMEZONE=yourTimezone # More info here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
- PREFIX=yourPrefix
volumes:
- /here/your/path/:/db
restart: unless-stopped

View file

@ -1,6 +1,8 @@
discord.py[voice]==1.6.0 # discord
discord.py[voice]==1.7.3 # discord
pytz==2021.1 # timezone
praw==7.1.4 # reddit
youtube-dl==2021.2.22 # music
lyricsgenius==3.0.0 # lyrics
feedparser==6.0.2 # rss feed (news)
asyncpraw==7.3.0 # reddit
youtube-dl==2021.6.6 # music
lyricsgenius==3.0.1 # lyrics
feedparser==6.0.8 # rss feed (news)
discord-py-slash-command==2.0.1 # slash commands
python_dotenv==0.19.0 # using .env file

View file

@ -1,15 +0,0 @@
from discord.ext import commands
def setup(client):
client.add_cog(Autopublish(client))
class Autopublish(commands.Cog):
"""Autopublish."""
def __init__(self, client):
self.client = client
@commands.Cog.listener()
async def on_message(self, message):
if message.author.id == 786897204816117771 and message.author.name == "GitHub" and message.author.bot:
await message.publish()

91
src/cogs/citation.py Normal file
View file

@ -0,0 +1,91 @@
import discord
from re import findall
from discord.ext import commands
from utils.core import userOrNick
from utils.time import timestampScreen
def setup(client):
client.add_cog(Citation(client))
class Citation(commands.Cog):
"""Gère le système de citation."""
def __init__(self, client):
self.client = client
@commands.Cog.listener()
async def on_message(self, message):
urls = findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', message.content)
httpsString = "https://"
channelsString = "discord.com/channels/"
for i in range(len(urls)):
if urls[i].startswith(f"{httpsString}{channelsString}") or urls[i].startswith(f"{httpsString}ptb.{channelsString}") or urls[i].startswith(f"{httpsString}canary.{channelsString}"):
link = urls[i]
linkURL = link
if link.startswith(f"{httpsString}{channelsString}"):
link = f'000{link}'
if link.startswith(f"{httpsString}ptb.{channelsString}"):
link = link[1:]
if link.startswith(f"{httpsString}canary.{channelsString}"):
link = link[4:]
if "@me" in urls[i]:
return await message.channel.send("Je ne cite pas les messages privés.", delete_after = 5)
try:
if int(link[32:-38]) == message.guild.id:
msgID = await self.client.get_channel(int(link[51:-19])).fetch_message(int(link[70:]))
couleur = 0x2f3136
msgFiles = msgID.attachments
imageExtensions = ["jpg", "jpeg", "png", "webp", "gif"]
desc = msgID.content
if len(msgFiles) > 1:
listOfFiles = ""
for i in range(0, len(msgFiles)):
listOfFiles = f"{listOfFiles}, {msgFiles[i].filename}"
listOfFiles = listOfFiles[2:]
if len(msgID.content) > 0:
desc = f"{msgID.content}\n\nIl y a plusieurs fichiers dans ce message : {listOfFiles}"
else:
desc = f"Il y a plusieurs fichiers dans ce message : {listOfFiles}"
else:
if len(msgFiles) == 1:
if msgFiles[0].filename[-4:].split('.')[1] in imageExtensions:
if not len(msgID.content) > 0:
desc = f"Une image jointe : {msgFiles[0].filename}"
else:
linkFile = msgFiles[0].url
if not len(msgID.content) > 0:
desc = f"Un fichier joint : {msgFiles[0].filename}"
embed = discord.Embed(description = desc, colour = couleur)
auteur = "Auteur"
if message.author == msgID.author:
auteur = "Auteur & Citateur"
embed.add_field(name = auteur, value = msgID.author.mention, inline=True)
try:
if len(msgFiles) == 1:
if msgFiles[0].filename[-4:].split('.')[1] in imageExtensions:
embed.set_image(url=msgFiles[0].url)
else:
embed.add_field(name = "Fichier", value = f"[Lien]({linkFile})", inline=True)
except:
pass
embed.add_field(name = "Message", value = f"{msgID.channel.mention} - [Lien Message]({linkURL})", inline=True)
embed.set_author(name = "Citation", icon_url = msgID.author.avatar_url)
icon_url = message.author.avatar_url
if msgID.edited_at:
edit = f" et modifié le {timestampScreen(msgID.edited_at)}"
else:
edit = ""
messageDuBas = f"Posté le {timestampScreen(msgID.created_at)}{edit}"
if auteur == "Auteur":
messageDuBas = messageDuBas + f"\nCité par {userOrNick(message.author)} le {timestampScreen(message.created_at)}"
embed.set_footer(icon_url = icon_url, text = messageDuBas)
if message.content == linkURL.replace(' ',''):
await message.channel.send(embed = embed)
await message.delete()
else:
await message.reply(embed = embed, mention_author = False)
except Exception as e:
e = str(e)
if not "invalid literal for int() with base 10:" in e or not "404 Not Found (error code: 10008)" in e: # faute de frappe / message supprimé
print(e)

View file

@ -0,0 +1,106 @@
import discord
from re import findall
from discord.ext import commands
from random import choice
from utils.core import userOrNick
from utils.time import nowCustom, intToDatetime, nowUTC, timestampScreen
from cogs.internet import Internet
def setup(client):
client.add_cog(ConfrerieDuKassoulait(client))
class ConfrerieDuKassoulait(commands.Cog):
"""Unique pour le serveur Discord "La confrérie du Kassoulait"."""
def __init__(self, client):
self.client = client
@commands.Cog.listener()
async def on_member_join(self, member):
if member.guild.id == 441208120644075520: # Confrérie du Kassoulait
if member.bot == True:
role = discord.utils.get(member.guild.roles, name = "Bot")
else:
role = discord.utils.get(member.guild.roles, name = "Copain")
await member.add_roles(role)
try: # DM possiblement fermé
await member.send(f"Coucou **{member.name}** dans **{member.guild.name}** ! 🥰\n\nJ'te donne le rôle de **{role}** 💖!")
except:
pass
channel = self.client.get_channel(741639570172674120) # salons des arrivées
msgBienvenue = [
f"Bienvenue, {member.mention}. On espère que tu as apporté de la pizza.",
f"C'est un plaisir de te voir, {member.mention}.",
f"{member.mention} vient juste d'arriver !",
f"{member.mention} vient juste d'atterrir.",
f"{member.mention} vient de se glisser dans le serveur.",
f"{member.mention} a bondi dans le serveur.",
f"Contents de te voir, {member.mention}.",
f"{member.mention} est arrivé(e).",
f"Tout le monde, accueillez comme il se doit {member.mention} !",
f"Youhou, tu as réussi, {member.mention} !",
f"{member.mention} a rejoint le groupe."
]
message = await channel.send("...") # évite d'envoyer une notification
await message.edit(content = choice(msgBienvenue))
@commands.Cog.listener()
async def on_member_remove(self, member):
if member.guild.id == 441208120644075520: # Confrérie du Kassoulait
channel = self.client.get_channel(741639570172674120) # salons des arrivées
await channel.send(f"{member.mention} ({member.name}) vient de quitter le serveur.")
@commands.Cog.listener()
async def on_message_delete(self, message):
try:
if message.author.guild.id == 441208120644075520: # Confrérie du Kassoulait
prefix = await self.client.get_prefix(message)
if not (
message.content.startswith(f"{prefix}note") or
message.content.startswith(f"{prefix}memo") or
len(findall(".com/channels/", message.content)) != 0 or
self.client.user.id is message.author.id
):
user_suppressed = None
async for entry in message.guild.audit_logs(limit=1):
if (intToDatetime(nowCustom()) - entry.created_at).seconds < 5 and str(entry.action) == 'AuditLogAction.message_delete':
user_suppressed = entry.user
channel = self.client.get_channel(742588187456831659)
embed = discord.Embed(description = f"{message.content}")
embed.set_author(name = userOrNick(message.author), icon_url = message.author.avatar_url)
if not user_suppressed:
embed.set_footer(text = f"Channel: #{message.channel.name} | Date : {timestampScreen(message.created_at)}\nSupprimé le {timestampScreen(intToDatetime(nowUTC()))}")
else:
embed.set_footer(icon_url = user_suppressed.avatar_url, text = f"Channel: #{message.channel.name} | Date : {timestampScreen(message.created_at)}\nSupprimé par {userOrNick(user_suppressed)} le {timestampScreen(intToDatetime(nowUTC()))}")
await channel.send(embed = embed)
# ne fonctionne pas quand un message a été supprimé avant que le bot ai démarré
# info sur la personne qui a supprimé ne fonctionne pas si il a supprimé un message auparavant (les logs se rajoute a un log deja existant)
except: # ignore error when deleting webhook message
pass
@commands.Cog.listener()
async def on_message(self, message):
if message.author.id == 869726667294248970 and message.author.bot: # Autopublish
await message.publish()
# autre serveur
if message.channel.id == 770805818487865404 or message.channel.id == 772239638240165928: # Le groupe de l'amour ❤❤ -- channel chien/chat
chiens = ["dog", "chien", "potichien", "doggo"]
chats = ["kat", "mace", "kater", "katze", "sinta", "minoos", "cat", "qitt", "besseh", "katu", "caun", "kazh",
"bisig", "moggy", "kotka", "maow", "gat", "we'sa", "guigna", "kodkod", "mao", "koyangi", "ghjattu", "míw", "pussi",
"gato", "gata", "kato", "kass", "domadh", "demmat", "kissa", "chat", "minou", "piscín", "cath", "k'at'a", "muca", "gali",
"gatos", "popoki", "kike", "chatul", "chatula", "billa", "kat poes", "macska", "cica", "kutjing", "kucing", "köttur",
"gatto", "gattina", "neko", "chma", "pising", "feles", "felix", "kakis", "katé", "qattus", "qattusa", "ngeru", "miz", "felino",
"felina", "muur", "katt", "shimii", "billi", "gorbe", "pusa", "kot", "giat", "pisica", "koshka", "pusi", "macka", "mizhu",
"kotsur", "bisad", "büsi", "chatz", "paka", "muc", "poonai", "puunay", "kocour", "kocka", "maa-oh", "kedi", "kit", "con mêo",
"tchèt", "mouss", "ologbo", "kats", "", "кот", "고양이", "poticha", "😼", "ʇɐɥɔ", "chaton"]
if message.content.lower() in chiens:
for _ in range(0, 2):
await Internet(self.client)._dog(self, await self.client.get_context(message))
if message.content.lower() in chats:
for _ in range(0, 2):
await Internet(self.client)._cat(self, await self.client.get_context(message))

View file

@ -1,47 +1,59 @@
import discord, re
import discord
from re import findall
from discord.ext import commands
from random import randint, choice
from datetime import timedelta
from discord_slash import cog_ext
from utils.core import retirerDoublons, mentionToUser, isSlash, mySendHidden
from utils.time import intToDatetime
def setup(client):
client.add_cog(Fun(client))
class Fun(commands.Cog):
"""Commandes plutôt fun."""
def __init__(self, client):
self.client = client
@commands.command(name='iq')
async def _iq(self, ctx, *, user = '0'):
"""Calcule ton IQ.\n ➡ Syntaxe: .iq [user]"""
if user == '0':
@commands.command(name='iq', aliases=["qi"])
async def _iq(self, ctx, *user):
"""Calcule ton QI.\n ➡ Syntaxe: {PREFIX}iq [user]"""
_, fromSlash, user = isSlash(user)
if len(user) == 0:
user = ctx.author
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"T'as {randint(randint(-100,0),220)} IQ {user.mention} !")
return await ctx.send(f"T'as {randint(randint(-100, 0), 220)} de QI {user.mention} !")
else:
user = user[0]
try:
user2 = user
user2 = user2[2:-1]
user2 = user2.replace("!","")
user2 = int(user2)
user2 = self.client.get_user(user2)
KassouBot = self.client.get_user(740140888373854269)
if user2.id == KassouBot.id:
user2 = self.client.get_user(mentionToUser(user))
if user2.id == self.client.user.id:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"Bah... pas ouf... j'ai juste 100000 IQ :/")
return await ctx.send(f"Bah euh... j'ai que (au moins) 100 000 de QI :/")
else:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
message = await ctx.send("...")
return await message.edit(content = f"{user2.mention} a {randint(randint(-100,0),220)} IQ !")
return await message.edit(content = f"{user2.mention} a {randint(randint(-100,0),220)} de QI !")
except:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
message = await ctx.send("...")
return await message.edit(content = f"{user} a {randint(randint(-100,0),220)} IQ !")
return await message.edit(content = f"{user} a {randint(randint(-100, 0), 220)} de QI !")
@cog_ext.cog_slash(name="iq", description = "Calcule ton QI.")
async def __iq(self, ctx, user = None):
if user == None:
return await self._iq(ctx, True)
else:
return await self._iq(ctx, user, True)
@commands.command(name='love')
async def _love(self, ctx, *users: discord.Member):
"""Découvre la probabilité que ces deux personnes se mettent en couple.\n ➡ Syntaxe: .love <User1> <User2>"""
"""Découvre la probabilité que ces deux personnes se mettent en couple.\n ➡ Syntaxe: {PREFIX}love <User1> <User2>"""
_, fromSlash, users = isSlash(users)
if len(users) == 2 or len(users) == 1:
UneDemande = False
if len(users) == 1:
@ -51,8 +63,9 @@ class Fun(commands.Cog):
users.append(ctx.author)
UneDemande = True
if users[0] == users[1]:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send("Je suis sûr que cette personne s'aime ! :angry:")
return await mySendHidden(ctx, fromSlash, "Je suis sûr que cette personne s'aime ! :angry:")
if users[0].nick:
user1 = list(users[0].nick)
else:
@ -61,8 +74,8 @@ class Fun(commands.Cog):
user2 = list(users[1].nick)
else:
user2 = list(users[1].name)
user1_CALC = self._retirerDoublons([x.lower() for x in user1])
user2_CALC = self._retirerDoublons([x.lower() for x in user2])
user1_CALC = retirerDoublons([x.lower() for x in user1])
user2_CALC = retirerDoublons([x.lower() for x in user2])
coef_amour = 0
if len(user1_CALC) > len(user2_CALC):
taille_du_pls_grand = len(user1_CALC)
@ -70,52 +83,56 @@ class Fun(commands.Cog):
else:
taille_du_pls_grand = len(user2_CALC)
taille_du_ms_grand = len(user1_CALC)
coef_amour = round(float(len(list(set(user1_CALC).intersection(user2_CALC))) / taille_du_pls_grand),1) * 100 + ((taille_du_pls_grand-taille_du_ms_grand) * 1.5) * 1.7
coef_amour = round(float(len(list(set(user1_CALC).intersection(user2_CALC))) / taille_du_pls_grand), 1) * 100 + ((taille_du_pls_grand-taille_du_ms_grand) * 1.5) * 1.7
if coef_amour > 100:
coef_amour = 100
if UneDemande == True:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"Tu as {coef_amour}% de chance de te mettre en couple avec {''.join(user1)}")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(f"{''.join(user1)} et {''.join(user2)} ont {coef_amour}% de chance de se mettre en couple !")
else:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send("Erreur! Syntaxe : `.love <User1> [User2]`\n")
def _retirerDoublons(self, liste):
Newliste = []
for element in liste:
if element not in Newliste:
Newliste.append(element)
return Newliste
await ctx.send(f"Erreur! Syntaxe : `{ctx.prefix}love <User1> [User2]`\n")
@_love.error
async def _love_error(self, ctx, error):
await ctx.send(str(error).replace('Member "', "Le membre **").replace('" not found', "** n'as pas été trouvé."))
@cog_ext.cog_slash(name="love", description = "Découvre la probabilité que ces deux personnes se mettent en couple.")
async def __love(self, ctx, user1: discord.Member, user2: discord.Member = None):
if user2 != None:
return await self._love(ctx, user1, user2, True)
else:
return await self._love(ctx, user1, True)
@commands.command(name='8ball', aliases=['8b', '8balls'])
async def _8ball(self, ctx, *, question):
"""Répond à ta question 🔮.\n ➡ Syntaxe: .8ball/8b <question>"""
async def _8ball(self, ctx, fromSlash = None):
"""Répond à ta question 🔮.\n ➡ Syntaxe: {PREFIX}8ball/8b"""
if fromSlash != True:
fromSlash = False
reponses=["c'est sûr.","il en est décidément ainsi.","incontestablement.","oui sans aucun doute.","tu peux t'y fier.","tel que je le vois, oui.","c'est le plus probable.",
"cela montre de bonnes perspectives.","certes.","les signes indiquent que oui.","ma réponse est oui.","ta question est trop floue, réessaie.","redemandes plus tard stp.",
"je ferais mieux de pas te le dire maintenant...","je ne peux pas le prédire actuellement :/","concentre-toi et redemande.","n'y comptes pas trop.","ma réponse est non.",
"mes sources disent que non.", "les perspectives ne sont pas si bonnes...","c'est très douteux."]
await ctx.send(f"{ctx.author.mention}, {choice(reponses)}")
if fromSlash != True:
if fromSlash != True: await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"{ctx.author.mention}, {choice(reponses)}")
@_8ball.error
async def _8ball_error(self, ctx, error):
if str(error) == "question is a required argument that is missing.":
await ctx.send("Mauvaise syntaxe : `.8ball/8b/8balls <question>`.")
@commands.command(name='pileouface', aliases=['pf'])
async def _pileouface(self, ctx):
"""Pile ou face.\n ➡ Syntaxe: .pileouface/pf"""
await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"{'Pile' if randint(0,1) == 1 else 'Face'} !")
await ctx.send(f"Mauvaise syntaxe : `{ctx.prefix}8ball/8b/8balls <question>`.")
@cog_ext.cog_slash(name="8ball", description = "Répond à ta question 🔮.")
async def __8ball(self, ctx, question):
await self._8ball(ctx, True)
@commands.command(name='mock')
async def _mock(self, ctx):
"""Se moque du message précédent"""
"""Se moque du message précédent. - Slash command not available"""
first = 0
suite_auteur = None
temps_limite = (await ctx.message.channel.history(limit = 2).flatten())[1].created_at - timedelta(minutes = 5)
temps_limite = intToDatetime((await ctx.message.channel.history(limit = 2).flatten())[1].created_at.timestamp() - 300) # on retire 5 minutes (5 x 60 secondes)
final_message = ""
async for message in ctx.message.channel.history(limit = 20, after = temps_limite, oldest_first = False):
if first == 0:
@ -134,7 +151,7 @@ class Fun(commands.Cog):
final_message = message.content
suite_auteur = message.author
urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', final_message)
urls = findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', final_message)
for i in range (0, len(urls)):
final_message = final_message.replace(urls[i], '')
@ -150,3 +167,30 @@ class Fun(commands.Cog):
return await ctx.send("".join(message).replace("\\N", "\n").replace("\\n", "\n"))
else:
return await ctx.send("Le message ne contient aucun texte.", delete_after = 5)
@commands.command(name='random', aliases=['randint'])
async def _random(self, ctx, *n):
"""Tire au hasard un chiffre entre 1 et n (par défaut n=10)\n ➡ Syntaxe: {PREFIX}random/randint [n]"""
n, fromSlash, _ = isSlash(n)
if n:
try:
n = int(n)
except:
return await mySendHidden(ctx, fromSlash, "Veuillez renseigner un chiffre valide.")
else:
n = 10
x = 1
if x > n:
x, n = n, x
resultat = randint(x, n)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(embed = discord.Embed().set_author(name = f"Tu as tiré le chiffre {resultat} !"))
@cog_ext.cog_slash(name="random", description = "Tire au hasard un chiffre entre 1 et n (par défaut n=10)")
async def __random(self, ctx, n: int = None):
if n == None:
await self._random(ctx, True)
else:
await self._random(ctx, n, True)

View file

@ -1,66 +1,74 @@
import discord
import asyncio
from discord.ext import commands
from random import randint, choice
import asyncio
from discord_slash import cog_ext
from utils.core import isSlash, mySendHidden
def setup(client):
client.add_cog(Games(client))
class Games(commands.Cog):
"""Commandes relatives aux jeux."""
def __init__(self, client):
self.client = client
self.guessing_game = {}
@commands.command(name='chifumi', aliases = ["shifumi", "ppc"])
async def _chifumi(self, ctx, *, choix):
"""Un simple Chifumi contre le bot.\n ➡ Syntaxe: .chifumi/shifumi/ppc <pierre/papier/ciseaux>"""
async def _chifumi(self, ctx, *choix):
"""Un simple Chifumi contre le bot.\n ➡ Syntaxe: {PREFIX}chifumi/shifumi/ppc <pierre/papier/ciseaux>"""
choix, fromSlash, _ = isSlash(choix)
if choix == None:
raise ModuleNotFoundError
choix_jeu = ["Pierre ✊", "Papier 🧻", "Ciseaux ✂"]
orditxt = choice(choix_jeu)
ordi = choix_jeu.index(orditxt)
PIERRE = 0
PAPIER = 1
CISEAUX = 2
pierre = 0
papier = 1
ciseaux = 2
choix = choix.lower()
if choix == "pierre":
choix = PIERRE
if choix == "papier" or choix == "feuille":
choix = PAPIER
if choix == "ciseaux" or choix == "ciseau":
choix = CISEAUX
choix = pierre
elif choix == "papier" or choix == "feuille":
choix = papier
elif choix == "ciseaux" or choix == "ciseau":
choix = ciseaux
else:
return await mySendHidden(ctx, fromSlash, "Je n'ai pas compris ce que tu as joué, réessaie.")
description = (f"{choix_jeu[choix][:-1]} VS {choix_jeu[ordi][:-1]}\n\n**"
f"{('Égalité !', 'Tu as perdu !', 'Tu as gagné !')[(choix != ordi) + ((choix > ordi and ordi +1 == choix) or (choix < ordi and choix + ordi == 2))]}**")
embed = discord.Embed(title = f"{choix_jeu[choix][-1:]}VS {choix_jeu[ordi][-1:]}", description = description)
embed.set_author(name = ctx.author.name, icon_url = ctx.author.avatar_url)
await ctx.send(embed = embed)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(embed = embed)
@_chifumi.error
async def _chifumi_error(self, ctx, error):
await ctx.send("Mauvaise syntaxe : `.chifumi/shifumi/ppc <pierre/papier/ciseaux>`.")
await ctx.send(f"Mauvaise syntaxe : `{ctx.prefix}chifumi/shifumi/ppc <pierre/papier/ciseaux>`.")
@cog_ext.cog_slash(name="chifumi", description = "Un simple Chifumi contre le bot. <pierre/papier/ciseaux>")
async def __chifumi(self, ctx, choix):
return await self._chifumi(ctx, choix, True)
@commands.command(name='plusoumoins', aliases = ['+ou-', '+-'])
async def _plusoumoins(self, ctx):
"""Un plus ou moins entre 1 et 100.\n ➡ Syntaxe: .plusoumoins/+ou-/+-"""
async def _plusoumoins(self, ctx, fromSlash = None):
"""Un plus ou moins entre 1 et 100.\n ➡ Syntaxe: {PREFIX}plusoumoins/+ou-/+-"""
if fromSlash != True:
fromSlash = False
if str(ctx.author.id) in self.guessing_game:
return await ctx.send("Tu es déjà en partie.")
return await mySendHidden(ctx, fromSlash, "Tu es déjà en partie.")
guess = 5
self.guessing_game[str(ctx.author.id)] = guess
number = randint(1,100)
number = randint(1, 100)
message = f"Choisis un nombre entre 1 et 100 {ctx.author.mention}."
await ctx.send(message)
while self.guessing_game[str(ctx.author.id)] != 0:
try:
def check(message):
if message.author.bot == False:
return str(message.author.id) in self.guessing_game
msg = await self.client.wait_for('message', check = check, timeout = 30)
msg = await self.client.wait_for('message', check = lambda message: str(message.author.id) in self.guessing_game if message.author.bot == False else None, timeout = 30)
except asyncio.TimeoutError:
del self.guessing_game[str(ctx.author.id)]
return await ctx.send(f"Tu as mis trop de temps a répondre {ctx.author.mention}. La réponse était {number}.")
@ -91,3 +99,18 @@ class Games(commands.Cog):
await ctx.send(f"Erreur dans la réponse {ctx.author.mention}, merci de n'écrire qu'un nombre. Tapez `stop` pour arrêter le jeu.")
del self.guessing_game[str(ctx.author.id)]
await ctx.send(f"T'as pas trouvé {ctx.author.mention}... dommage, c'était {number}.")
@cog_ext.cog_slash(name="plusoumoins", description = "Un plus ou moins entre 1 et 100.")
async def __plusoumoins(self, ctx):
await self._plusoumoins(ctx, True)
@commands.command(name='pileouface', aliases=['pf'])
async def _pileouface(self, ctx, fromSlash = None):
"""Pile ou face.\n ➡ Syntaxe: {PREFIX}pileouface/pf"""
if fromSlash != True:
fromSlash = False
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"{'Pile' if randint(0, 1) == 1 else 'Face'} !")
@cog_ext.cog_slash(name="pileouface", description = "Pile ou face.")
async def __pileouface(self, ctx):
await self._pileouface(ctx, True)

View file

@ -1,22 +1,25 @@
import discord
from discord.ext import commands
from discord_slash import cog_ext
from utils.core import isSlash, mySendHidden
def setup(client):
client.add_cog(Help(client))
class Help(commands.Cog):
"""Listes des commandes et/ou catégories."""
"""Commandes relatives à l'aide utilisateur."""
def __init__(self, client):
self.client = client
self.client.remove_command("help")
@commands.command(name='help')
async def _help(self, ctx, *cog):
"""Affiche toutes les commandes du bot.\n ➡ Syntaxe: .help [catégorie]"""
if not cog:
"""Liste des Cog"""
halp=discord.Embed(title = 'Liste des catégories et commandes sans catégorie',
"""Affiche toutes les commandes du bot.\n ➡ Syntaxe: {PREFIX}help [catégorie]"""
_, fromSlash, cog = isSlash(cog)
erreur = False
if not cog: # Liste des Cog
halp = discord.Embed(title = 'Liste des catégories et commandes sans catégorie',
description = f'Utilisez `{ctx.prefix}help [catégorie]` pour en savoir plus sur elles et leur commande.',
color = discord.Colour.random())
for name_cog in self.client.cogs:
@ -27,7 +30,7 @@ class Help(commands.Cog):
liste_cmds += f", `{ctx.prefix}{cmds.name}`"
nb_cmds += 1
if name_cog != "Help" and nb_cmds > 0:
halp.add_field(name = f'**{name_cog} {nb_cmds}**', value = liste_cmds[2:], inline = False)
halp.add_field(name = f'**{name_cog} ({nb_cmds}) — {self.client.cogs[name_cog].__doc__}**', value = liste_cmds[2:], inline = False)
cmds_desc = ''
for y in self.client.walk_commands():
if not y.cog_name and not y.hidden:
@ -35,15 +38,15 @@ class Help(commands.Cog):
if len(cmds_desc) > 1:
halp.add_field(name = 'Commandes sans catégorie', value = cmds_desc[0:len(cmds_desc)-1], inline = False)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(embed = halp)
else:
"""Avertissement si il y a trop d'arguments dans la variable cog"""
else: # Avertissement si il y a trop d'arguments dans la variable cog
if len(cog) > 1:
await ctx.message.add_reaction(emoji = '')
halp = discord.Embed(title = 'Erreur !', description = "Tu as renseigné trop d'arguments !", color = 0xC41B1B)
await ctx.send(embed = halp)
else:
"""Liste des commandes avec cogs."""
else: # Liste des commandes avec cogs.
cog = [item.capitalize() for item in cog]
found = False
for x in self.client.cogs:
@ -52,15 +55,37 @@ class Help(commands.Cog):
halp = discord.Embed(title = f'{cog[0]} - Liste des commandes', description = self.client.cogs[cog[0]].__doc__, color = discord.Colour.random())
for c in self.client.get_cog(y).get_commands():
if not c.hidden:
cmds_help = str(c.help).split("\n")
cmds_help = str(c.help).replace("{PREFIX}", ctx.prefix).split("\n")
del cmds_help[0]
backslash = '\n'
halp.add_field(name = f"`{ctx.prefix}{c.name}` - {str(c.help).split(backslash)[0]}", value = f"{''.join(cmds_help)}\u200b", inline = False)
found = True
if not found:
"""Rappel si le cog n'existe pas."""
if not found: # Rappel si le cog n'existe pas.
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
halp = discord.Embed(title = 'Erreur !', description = f"Qu'est ce que {cog[0]} ?", color = 0xC41B1B)
erreur = True
else:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send('', embed = halp)
await mySendHidden(ctx, erreur, embed = halp)
@cog_ext.cog_slash(name="help", description = "Affiche toutes les commandes du bot.")
async def __help(self, ctx, cog = None):
ctx.prefix = "/"
if cog == None:
return await self._help(ctx, True)
else:
return await self._help(ctx, cog, True)
@commands.command(name='invite')
async def _invite(self, ctx, fromSlash = None):
"""Invite ce bot sur ton serveur !"""
if fromSlash != True:
fromSlash = False
embed = discord.Embed(description = f"[Lien vers l'invitation Discord](https://discord.com/api/oauth2/authorize?client_id={self.client.user.id}&permissions=8&scope=bot%20applications.commands)").set_author(name="Invite moi !")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(embed = embed)
@cog_ext.cog_slash(name="invite", description = "Invite ce bot sur ton serveur !")
async def __invite(self, ctx):
return await self._invite(ctx, True)

View file

@ -1,145 +1,148 @@
import discord, praw, json, requests, time, feedparser, os
import discord
from feedparser import parse
from discord.ext import commands
from random import randint, choice
from random import choice
from asyncpraw import Reddit
from discord_slash import cog_ext
from utils.core import randomImage, isSlash, mySendHidden, load
def setup(client):
client.add_cog(Internet(client))
class Internet(commands.Cog):
"""Commandes relatives à ce qui provient d'internet."""
def __init__(self, client):
self.client = client
@commands.Cog.listener()
async def on_message(self, message):
if message.channel.id == 770805818487865404 or message.channel.id == 772239638240165928: # Le groupe de l'amour ❤❤ -- channel chien/chat
chiens = ["dog", "chien", "potichien"]
chats = ["kat", "mace", "kater", "katze", "sinta", "minoos", "cat", "qitt", "besseh", "katu", "caun", "kazh",
"bisig", "moggy", "kotka", "maow", "gat", "we'sa", "guigna", "kodkod", "mao", "koyangi", "ghjattu", "míw", "pussi",
"gato", "gata", "kato", "kass", "domadh", "demmat", "kissa", "chat", "minou", "piscín", "cath", "k'at'a", "muca", "gali",
"gatos", "popoki", "kike", "chatul", "chatula", "billa", "kat poes", "macska", "cica", "kutjing", "kucing", "köttur",
"gatto", "gattina", "neko", "chma", "pising", "feles", "felix", "kakis", "katé", "qattus", "qattusa", "ngeru", "miz", "felino",
"felina", "muur", "katt", "shimii", "billi", "gorbe", "pusa", "kot", "giat", "pisica", "koshka", "pusi", "macka", "mizhu",
"kotsur", "bisad", "büsi", "chatz", "paka", "muc", "poonai", "puunay", "kocour", "kocka", "maa-oh", "kedi", "kit", "con mêo",
"tchèt", "mouss", "ologbo", "kats", "", "кот", "고양이", "poticha", "😼", "ʇɐɥɔ"]
if message.content.lower() in chiens:
await self._dog(await self.client.get_context(message))
if message.content.lower() in chats:
await self._cat(await self.client.get_context(message))
self.keys = load(["TOKEN_REDDIT_CLIENT_ID", "TOKEN_REDDIT_CLIENT_SECRET", "TOKEN_REDDIT_USER_AGENT"])
@commands.command(name='memes', aliases = ['meme'])
async def _memes(self, ctx, *, args = ""):
"""Envois un meme de reddit.\n ➡ Syntaxe: .memes/meme [subreddit]"""
try:
reddit = praw.Reddit(client_id = os.environ['TOKEN_REDDIT_CLIENT_ID'], client_secret = os.environ['TOKEN_REDDIT_CLIENT_SECRET'], user_agent = f"disreddit /u/{os.environ['TOKEN_REDDIT_USER_AGENT']}, http://localhost:8080")
async def _memes(self, ctx, *args):
"""Envoie un meme de reddit.\n ➡ Syntaxe: {PREFIX}memes/meme [subreddit]"""
args, fromSlash, _ = isSlash(args)
if args != "": # si il y a un arg différent d'un meme
if args: # s'il y a un subreddit de défini
subredditchoix = args
else: # si il n'y a pas d'arguments
subredditchoix = choice(['memes', 'anime_irl', 'goodanimemes', 'BikiniclienttomTwitter', 'dankmemes', 'DeepFriedMemes',
else: # s'il n'y en a pas
subredditchoix = choice(['memes', 'goodanimemes', 'dankmemes', 'DeepFried',
'educationalmemes', 'funny', 'marvelmemes', 'me_irl', 'meme', 'MemeEconomy', 'Memes_Of_The_Dank', 'MinecraftMemes',
'physicsmemes', 'reactiongifs', 'blackpeopletwitter', 'metal_me_irl', 'bee_irl', '195',
'shittyadviceanimals', 'meirl', '2meirl4meirl', 'AdviceAnimals', 'weirdmemes'])
'physicsmemes', 'blackpeopletwitter', 'metal_me_irl', '195', 'shittyadviceanimals', 'meirl',
'2meirl4meirl', 'AdviceAnimals', 'weirdmemes', 'LeagueOfMemes'])
memes_submissions = reddit.subreddit(subredditchoix).hot()
post_to_pick = randint(1, 10)
for i in range(0, post_to_pick): # i pas important
i = i #retire l'erreur sur vscode
submission = next(x for x in memes_submissions if not x.stickied)
if fromSlash != None and subredditchoix == "nsfw": # demande de nsfw sans passé par la commande appropriée
return await mySendHidden(ctx, fromSlash, f"Désolé, tu demandes du nsfw... Fais plutôt **{ctx.prefix}sexe**.")
image = ["png", "jpg", "jpeg", "bmp", "gif"]
try:
async with Reddit(client_id = self.keys['TOKEN_REDDIT_CLIENT_ID'], client_secret = self.keys['TOKEN_REDDIT_CLIENT_SECRET'], user_agent = f"disreddit /u/{self.keys['TOKEN_REDDIT_USER_AGENT']}, http://localhost:8080") as reddit:
subreddit = await reddit.subreddit(subredditchoix) # récupération du subreddit
hot = subreddit.top(limit = 20) # récupération des memes avec une limite aux 10 premiers memes
all_subs = [item async for item in hot] # liste des memes
submission = choice(all_subs) # choix aléatoire
image = ["png", "jpg", "jpeg", "bmp", "gif"] # gifv not working
if submission.url[-3:] in image:
embed = discord.Embed(title = f"r/{subredditchoix} pour {ctx.author.name}", color = discord.Colour.random(), description = f"[lien du meme]({submission.url})")
embed.set_footer(text = f"Meme de Reddit")
if fromSlash != None:
footer = "Meme de Reddit"
memeOuImage = "[lien du meme]"
else:
footer = "NSFW de Reddit"
memeOuImage = "[lien de l'image]"
embed = discord.Embed(title = f"r/{subredditchoix} pour {ctx.author.name}", color = discord.Colour.random(), description = f"{memeOuImage}(https://www.reddit.com{submission.permalink})")
embed.set_footer(text = footer)
embed.set_image(url = submission.url)
message = await ctx.send(embed = embed)
else:
await ctx.send(f"```r/{subredditchoix} pour {ctx.author.name}```\n{submission.url}")
message = await ctx.send("```Meme de Reddit```")
if fromSlash != None:
message = await ctx.send(f"`r/{subredditchoix} pour {ctx.author.name}`\n{submission.url}")
else:
message = await ctx.send(f"`{subredditchoix.capitalize()} pour {ctx.author.name}`\n{submission.url}")
if fromSlash != True and fromSlash != None:
await ctx.message.add_reaction(emoji = '')
await message.add_reaction('👍')
return await message.add_reaction('👎')
if fromSlash != None:
for emoji in ['🔺', '🔻']:
await message.add_reaction(emoji)
except Exception as error:
print(f"args: {args}, subreddit: {subredditchoix}, error: {error}")
print(f"Error in _memes command = args: {args}, subreddit: {subredditchoix}, error: {error}")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(f"Ce subreddit est interdit, mis en quarantaine ou n'existe pas. ({subredditchoix})")
def _random_image(self, link):
temps_requete = int(round(time.time() * 1000))
try:
request_data = requests.get(link)
except Exception as e:
raise Exception(f"Une erreur s'est produite lors de la tentative de demande de l'API {link} : {e}")
if not request_data.status_code == 200:
raise Exception(f"Code HTTP {request_data.status_code} au lieu de HTTP 200 à l'appel de {link} : {request_data.text}")
try:
json_data = json.loads(request_data.text)
except Exception as e:
raise Exception(f"Erreur lors de la transformation les données de {link} en json : {e}")
temps_requete = int(round(time.time() * 1000)) - temps_requete
return (json_data, temps_requete)
return await mySendHidden(ctx, fromSlash, f"Ce subreddit est interdit, mis en quarantaine ou n'existe pas. ({subredditchoix})")
@cog_ext.cog_slash(name="meme", description = "Envoie un meme de reddit.")
async def __memes(self, ctx, subreddit = None):
ctx.prefix = "/"
if subreddit == None:
return await self._memes(ctx, True)
else:
return await self._memes(ctx, subreddit, True)
@commands.command(name='cat', aliases = ['chat'])
async def _cat(self, ctx):
"""Te montre un magnifique chat\n ➡ Syntaxe: .cat/chat"""
async def _cat(self, ctx, fromSlash = None):
"""Te montre un magnifique chat.\n ➡ Syntaxe: {PREFIX}cat/chat"""
if fromSlash != True:
fromSlash = False
if ctx.author.nick:
name = f"{ctx.author.nick} ({ctx.author.name}#{ctx.author.discriminator})"
else:
name = f"{ctx.author.name}"
embed = discord.Embed(title = f"Poticha pour {name}", colour = discord.Colour.random())
cat = self._random_image("http://aws.random.cat/meow")
cat = randomImage("http://aws.random.cat/meow")
embed.set_image(url = cat[0]['file'])
embed.set_footer(text = f"random.cat a pris {cat[1]} ms.")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
message = await ctx.send(embed=embed)
return await message.add_reaction('❤️')
@cog_ext.cog_slash(name="cat", description = "Te montre un magnifique chat.")
async def __cat(self, ctx):
return await self._cat(ctx, True)
@commands.command(name='dog', aliases = ['chien'])
async def _dog(self, ctx):
"""Te montre un magnifique chien\n ➡ Syntaxe: .dog/chien"""
async def _dog(self, ctx, fromSlash = None):
"""Te montre un magnifique chien.\n ➡ Syntaxe: {PREFIX}dog/chien"""
if fromSlash != True:
fromSlash = False
if ctx.author.nick:
name = f"{ctx.author.nick} ({ctx.author.name}#{ctx.author.discriminator})"
else:
name = f"{ctx.author.name}"
embed = discord.Embed(title = f"Potichien pour {name}", colour = discord.Colour.random())
dog = self._random_image("https://dog.ceo/api/breeds/image/random")
dog = randomImage("https://dog.ceo/api/breeds/image/random")
embed.set_image(url = dog[0]['message'])
embed.set_footer(text = f"dog.ceo a pris {dog[1]} ms.")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
message = await ctx.send(embed=embed)
return await message.add_reaction('❤️')
@cog_ext.cog_slash(name="dog", description = "Te montre un magnifique chien.")
async def __dog(self, ctx):
return await self._dog(ctx, True)
@commands.command(name='sexe', aliases=['sexes', 'nude', 'nudes', 'nsfw'])
async def _sexe(self, ctx, *, choice_of_nsfw = None):
"""Envois une image coquine. (NSFW)\n ➡ Syntaxe: .sexe/sexes/nude/nudes [butts/boobs]"""
liste_hot = ['butts', 'boobs']
if choice_of_nsfw in liste_hot:
pass
else:
choice_of_nsfw = choice(liste_hot)
async def _sexe(self, ctx, fromSlash = None):
"""Envois une image coquine. (NSFW)\n ➡ Syntaxe: {PREFIX}sexe/sexes/nude/nudes"""
if fromSlash != True:
fromSlash = False
if ctx.channel.is_nsfw():
embed = discord.Embed(title = f"{choice_of_nsfw.capitalize()} pour {ctx.author.name}", colour = discord.Colour.random())
nsfw = self._random_image(f'http://api.o{choice_of_nsfw}.ru/noise/')
embed.set_image(url = f"http://media.o{choice_of_nsfw}.ru/{nsfw[0][0]['preview']}")
embed.set_footer(text = f"o{choice_of_nsfw}.ru a pris {nsfw[1]} ms.")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(embed = embed)
else:
ctx.prefix = "/"
return await self._memes(ctx, "nsfw", None)
else:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(f"Désolé mais je n'envois ce genre de message seulement dans les salons NSFW !")
await mySendHidden(ctx, fromSlash, f"Désolé mais je n'envois ce genre de message seulement dans les salons NSFW !")
@cog_ext.cog_slash(name="sexe", description = "Envois une image coquine. (NSFW)")
async def __sexe(self, ctx):
return await self._sexe(ctx, True)
@commands.command(name='news', aliases=['rss'])
async def _news(self, ctx, *, arg = ""):
"""Info random dans le domaine de l'informatique\n ➡ Syntaxe: .news/rss [site/liste]"""
async def _news(self, ctx, *arg):
"""Info random dans le domaine de l'informatique\n ➡ Syntaxe: {PREFIX}news/rss [site/liste]"""
arg, fromSlash, _ = isSlash(arg)
if arg == None:
arg = ""
rss_website = {
"anandtech": "https://www.anandtech.com/rss/",
@ -170,7 +173,7 @@ class Internet(commands.Cog):
embed = discord.Embed(title = "Liste des sources", color = discord.Colour.random(), description = ", ".join([key.capitalize() for key in rss_website.keys()]))
return await ctx.send(embed = embed)
newsfeed = feedparser.parse(rss_website[choix_site])
newsfeed = parse(rss_website[choix_site])
info = choice([newsfeed.entries[i] for i in range(0, 10 if len(newsfeed.entries) > 10 else len(newsfeed.entries))])
desc = "Pas de description trouvée." if "<p>" in info.description or "</a>" in info.description else info.description
@ -181,4 +184,11 @@ class Internet(commands.Cog):
pass
embed.set_footer(text = f"News de {choix_site.capitalize()}")
await ctx.send(embed = embed)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
@cog_ext.cog_slash(name="news", description = "Info random dans le domaine de l'informatique, met commme arg liste pour la liste des sources dispo.")
async def __news(self, ctx, source = None):
if source == None:
return await self._news(ctx, True)
else:
return await self._news(ctx, source, True)

View file

@ -18,10 +18,13 @@ from async_timeout import timeout
from discord.ext import commands
# Genius API
import lyricsgenius
import time
import os
genius = lyricsgenius.Genius(os.environ['TOKEN_GENIUS'])
from lyricsgenius import Genius
from utils.core import ligneFormatage, userOrNick, load
from utils.time import nowCustom
from re import sub
genius = Genius(load(["TOKEN_GENIUS"])["TOKEN_GENIUS"])
genius.response_format = "markdown"
# Silence useless bug reports messages
youtube_dl.utils.bug_reports_message = lambda: ''
@ -270,7 +273,7 @@ class VoiceState:
class Music(commands.Cog):
"""Commandes relatives à la musique - © vbe0201."""
"""Commandes relatives à la musique © vbe0201. (slash commands not working)"""
def __init__(self, client: commands.bot):
self.client = client
self.voice_states = {}
@ -299,9 +302,9 @@ class Music(commands.Cog):
async def cog_command_error(self, ctx: commands.Context, error: commands.CommandError):
await ctx.send(f"Une erreur est survenue : {str(error)}")
@commands.command(name='join', aliases=['j'], invoke_without_subcommand=True)
@commands.command(name='join', aliases=['j', 'connect'], invoke_without_subcommand=True)
async def _summon(self, ctx: commands.Context, *, channel: discord.VoiceChannel = None):
"""Se connecte au salon vocal.\n ➡ Syntaxe: .connect/join"""
"""Se connecte au salon vocal.\n ➡ Syntaxe: {PREFIX}connect/join"""
if not channel and not ctx.author.voice:
await ctx.send("Aucun channel à rejoindre. Connecte toi dans un vocal ou donne-moi son id.")
@ -314,13 +317,14 @@ class Music(commands.Cog):
return
ctx.voice_state.voice = await destination.connect()
await ctx.guild.change_voice_state(channel = destination, self_mute = False, self_deaf = True)
@commands.command(name='stop', aliases=['disconnect', 'dc'])
async def _leave(self, ctx: commands.Context):
"""Arrête la chanson en cours de lecture et quitte le salon vocal.\n ➡ Syntaxe: .disconnect/dc/stop"""
"""Arrête la chanson en cours de lecture et quitte le salon vocal.\n ➡ Syntaxe: {PREFIX}disconnect/dc/stop"""
if not ctx.voice_state.voice:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose ou `.join [id]` pour me connecter à un salon vocal.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose ou `{ctx.prefix}join [id]` pour me connecter à un salon vocal.", color = 0xC41B1B)
embed.set_author(name = "Je ne suis connecté à aucun vocal.")
return await ctx.send(embed = embed)
@ -330,7 +334,7 @@ class Music(commands.Cog):
@commands.command(name='volume', aliases=['vol'])
async def _volume(self, ctx: commands.Context, *, volume: int = False):
"""Modifie le volume du bot (entre 1 et 100).\n ➡ Syntaxe: .volume/vol [1;100]"""
"""Modifie le volume du bot (entre 1 et 100).\n ➡ Syntaxe: {PREFIX}volume/vol [1;100]"""
if not ctx.voice_state.is_playing:
return await ctx.send("Rien n'est joué pour le moment.")
@ -346,7 +350,7 @@ class Music(commands.Cog):
@commands.command(name='now', aliases=['current', 'playing', 'np'])
async def _now(self, ctx: commands.Context):
"""Affiche des informations sur la chanson en cours de lecture.\n ➡ Syntaxe: .now/current/playing/np"""
"""Affiche des informations sur la chanson en cours de lecture.\n ➡ Syntaxe: {PREFIX}now/current/playing/np"""
await ctx.send(embed=ctx.voice_state.current.create_embed())
@ -359,7 +363,7 @@ class Music(commands.Cog):
await ctx.message.add_reaction('')
await ctx.send(f"**`{ctx.author}`** met en pause la chanson en cours.")
else:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed.set_author(name = "Je ne joue rien en ce moment !")
return await ctx.send(embed = embed)
@ -373,20 +377,20 @@ class Music(commands.Cog):
await ctx.send(f"**`{ctx.author}`** relance la chanson.")
else:
if ctx.voice_state.is_playing:
embed = discord.Embed(description = "Tape `.pause` pour mettre en pause la chanson.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}pause` pour mettre en pause la chanson.", color = 0xC41B1B)
embed.set_author(name = "Je suis déjà en lecture !")
return await ctx.send(embed = embed)
else:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose ou `.join [id]` pour me connecter à un salon vocal.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose ou `{ctx.prefix}join [id]` pour me connecter à un salon vocal.", color = 0xC41B1B)
embed.set_author(name = "Je ne suis connecté à aucun vocal.")
return await ctx.send(embed = embed)
@commands.command(name='skip', aliases=['s'])
async def _skip(self, ctx: commands.Context):
"""Passe la chanson.\n ➡ Syntaxe: .skip/s"""
"""Passe la chanson.\n ➡ Syntaxe: {PREFIX}skip/s"""
if not ctx.voice_state.is_playing:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed.set_author(name = "Je ne joue rien en ce moment !")
return await ctx.send(embed = embed)
@ -396,10 +400,10 @@ class Music(commands.Cog):
@commands.command(name='queue', aliases=['q', 'playlist'])
async def _queue(self, ctx: commands.Context, *, page: int = 1):
"""Affiche la file d'attente des chansons à venir.\n ➡ Syntaxe: .queue/q/playlist [page]"""
"""Affiche la file d'attente des chansons à venir.\n ➡ Syntaxe: {PREFIX}queue/q/playlist [page]"""
if len(ctx.voice_state.songs) == 0:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed.set_author(name = "Il n'y a plus de chanson à venir dans la playlist.")
return await ctx.send(embed = embed)
@ -407,7 +411,7 @@ class Music(commands.Cog):
pages = math.ceil(len(ctx.voice_state.songs) / items_per_page)
if page > pages:
embed = discord.Embed(description = "Tape `.play <chanson>` pour rajouter encore de la musique.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour rajouter encore de la musique.", color = 0xC41B1B)
embed.set_author(name = "Il n'y a pas autant de pages")
return await ctx.send(embed = embed)
@ -427,7 +431,7 @@ class Music(commands.Cog):
"""Mélange la file d'attente."""
if len(ctx.voice_state.songs) == 0:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed.set_author(name = "La file est vide.")
return await ctx.send(embed = embed)
@ -447,10 +451,10 @@ class Music(commands.Cog):
@commands.command(name='loop', aliases=['repeat'])
async def _loop(self, ctx: commands.Context):
"""Répète la chanson actuellement en lecture.\n ➡ Syntaxe: .loop/repeat"""
"""Répète la chanson actuellement en lecture.\n ➡ Syntaxe: {PREFIX}loop/repeat"""
if not ctx.voice_state.is_playing:
embed = discord.Embed(description = "Tape `.play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed = discord.Embed(description = f"Tape `{ctx.prefix}play <chanson>` pour jouer quelque chose.", color = 0xC41B1B)
embed.set_author(name = "Je ne joue rien en ce moment !")
return await ctx.send(embed = embed)
@ -461,7 +465,7 @@ class Music(commands.Cog):
@commands.command(name='play', aliases=['p'])
async def _play(self, ctx: commands.Context, *, search: str):
"""Recherche une chanson sur les sites compatibles avec YoutubeDL si aucun URL n'est donné et l'ajoute à la file d'attente.\n ➡ Syntaxe: .play/p"""
"""Recherche une chanson sur les sites compatibles avec YoutubeDL si aucun URL n'est donné et l'ajoute à la file d'attente.\n ➡ Syntaxe: {PREFIX}play/p"""
if not ctx.voice_state.voice:
await ctx.invoke(self._summon)
@ -489,15 +493,15 @@ class Music(commands.Cog):
@commands.command(name='lyrics', aliases = ['l', 'lyric'])
async def _lyrics(self, ctx, *, song: str = None):
"""Affiche les paroles de la musique en cours, ou de la chanson spécifiée.\n ➡ Syntaxe: .lyrics/lyric/l (musique)"""
"""Affiche les paroles de la musique en cours, ou de la chanson spécifiée.\n ➡ Syntaxe: {PREFIX}lyrics/lyric/l (musique)"""
if song or ctx.voice_state.is_playing:
if not song:
song = f"{ctx.voice_state.current.title()}"
song = sub(r"(\ )?\(.*\)", "", ctx.voice_state.current.title())
if " romanized" in song:
message = await ctx.send(f":mag: **Cherche les paroles romanisées de ** `{song.replace(' romanized', '')}`")
else:
message = await ctx.send(f":mag: **Cherche les paroles de ** `{song}`")
temps_requete = int(round(time.time() * 1000))
temps_requete = int(round(nowCustom() * 1000))
song_genius = genius.search_song(song)
couleur_embed = discord.Colour.random()
try:
@ -505,6 +509,7 @@ class Music(commands.Cog):
except:
await ctx.message.add_reaction(emoji = '')
return await message.edit(content = f"Pas de résultats trouvés pour `{song}`.")
paroles = sub(r"3?EmbedShare URLCopyEmbedCopy", "", paroles) # Fix temporaire bug Genius
lignetotal = ""
premierembed = True
if len(paroles) > 7500:
@ -518,7 +523,7 @@ class Music(commands.Cog):
type_de_comptage = "\n"
for ligne in paroles.split(type_de_comptage):
if len(f"{lignetotal}{type_de_comptage}{ligne}") < 1900:
lignetotal = f"{lignetotal}{type_de_comptage}{self.ligne_formatage(ligne)}"
lignetotal = f"{lignetotal}{type_de_comptage}{ligneFormatage(ligne)}"
else:
if premierembed == True:
premierembed = False
@ -528,10 +533,10 @@ class Music(commands.Cog):
else:
embed = discord.Embed(description = lignetotal, color = couleur_embed)
await ctx.send(embed = embed)
lignetotal = f"{self.ligne_formatage(ligne)}"
lignetotal = f"{ligneFormatage(ligne)}"
temps_requete = int(round(time.time() * 1000)) - temps_requete
footer_embed = f"Pour {self.user_or_nick(ctx.author)} par Genius en {round(temps_requete / 1000, 2)} s."
temps_requete = int(round(nowCustom() * 1000)) - temps_requete
footer_embed = f"Pour {userOrNick(ctx.author)} par Genius en {round(temps_requete / 1000, 2)} s."
await ctx.message.add_reaction(emoji = '')
if premierembed == True:
premierembed = False
@ -544,21 +549,10 @@ class Music(commands.Cog):
return await ctx.send(embed = embed)
else:
await ctx.message.add_reaction(emoji = '')
await ctx.send("Aucune musique demandé... `.lyrics/l/lyrics <song>`.")
def ligne_formatage(self, ligne):
liste_balise = [
('[Hook', '[Accroche'), ('[Verse', '[Couplet'), ('[Chorus', '[Chœur'),
('[Bridge', '[Pont'),('[Pre-Chorus', '[Pré-chœur'), ('[Post-Chorus', '[Post-chœur')
]
for balises in liste_balise:
ligne = ligne.replace(balises[0], balises[1])
return ligne
def user_or_nick(self, user):
if user.nick:
return f"{user.nick} ({user.name}#{user.discriminator})"
else:
return f"{user.name}#{user.discriminator}"
await ctx.send(f"Aucune musique demandé... `{ctx.prefix}lyrics/l/lyrics <song>`.")
@commands.command(name='lyricsromanized', aliases = ['lr', 'lyricromanized'], hidden = True)
async def _lyricsromanized(self, ctx, *, song: str = None):
if not song and ctx.voice_state.is_playing:
song = sub(r"(\ )?\(.*\)", "", ctx.voice_state.current.title())
await ctx.invoke(self.client.get_command("lyrics"), song = f"{song} romanized" if song else song)

206
src/cogs/reminder.py Normal file
View file

@ -0,0 +1,206 @@
import discord
from discord.ext import commands, tasks
from discord_slash import cog_ext
from utils.reminder import Reminder, embedListe, listReaction
from utils.core import getURLsInString, getMentionInString, isSlash, mySendHidden, mentionToUser, cleanCodeStringWithMentionAndURLs
from utils.time import stringTempsVersSecondes, nowUTC, intToDatetime, timedeltaToString
def setup(client):
client.add_cog(ReminderDiscord(client))
class ReminderDiscord(commands.Cog):
"""Commandes relatives aux reminder."""
def __init__(self, client):
self.client = client
self._reminderLoop.start()
@tasks.loop(minutes = 1)
async def _reminderLoop(self):
"""Méthode qui se répète toute les minutes pour vérifier si des rappels n'ont pas expirés, si expirés, les envoient."""
expiration = Reminder().recuperationExpiration(int(nowUTC())) # on récupères les éléments expirés
for expired in expiration: # on regarde tout les éléments expirés
reminder = expired[2] # message
userID = expired[4] # personne qui a fait le rappel
channel = self.client.get_channel(expired[0]) # salon du message
finalEmbed = discord.Embed(description = cleanCodeStringWithMentionAndURLs(reminder), timestamp = intToDatetime(expired[3]), color = discord.Colour.random())
if expired[1] == 2: # s'il faut envoyer en DM le message
user = self.client.get_user(userID)
if user == None: # si l'utilisateur n'est pas trouvé
return Reminder().suppressionReminder(expired[5]) # suppression du rappel
channel = await user.create_dm() # envoie en DM
userID = None # plus de mention
sourceMessage = None # plus de message source
elif channel == None: # si le salon n'existe plus
user = self.client.get_user(userID)
if user == None: # si l'utilisateur n'est pas trouvé
return Reminder().suppressionReminder(expired[5]) # suppression du rappel
channel = await user.create_dm() # envoie en DM
userID = None # plus de mention
sourceMessage = None # plus de message source
finalEmbed.add_field(name = "Info", value = "Message envoyé en DM car le salon n'est plus disponible.")
else:
sourceMessage = expired[6]
if sourceMessage != None: # vérification message avec slash command et que si c'est pas en DM
try:
sourceMessage = await channel.fetch_message(sourceMessage) # récupération message
except:
sourceMessage = None # message a été supprimé
if sourceMessage != None:
await sourceMessage.add_reaction(emoji = '') # ajout réaction
finalEmbed.set_footer(text=f"Message d'il y a {timedeltaToString(int(nowUTC()) - expired[3])}")
links = ""
findedURLs = getURLsInString(reminder)
for i in range(0, len(findedURLs)): # ajout de field "lien" pour pouvoir cliquer sur les liens facilement
links += f"[Lien {i + 1}]({findedURLs[i]}) · "
if len(findedURLs) > 0:
finalEmbed.add_field(name = f"Lien{'s' if len(findedURLs) > 1 else ''}", value = links[:-3])
message = ""
if userID != None: # metion de l'utilisateur si le message n'est pas en DM
message = f"<@{userID}>"
if expired[1] == 1: # s'il faut mentionner les personnes dans le message
mentionList = getMentionInString(reminder)
for i in mentionList:
message += f" {i}"
try:
await channel.send(message, embed = finalEmbed) # envoie du rappel
except: # les DM sont fermés
pass
Reminder().suppressionReminder(expired[5]) # suppression du rappel
@_reminderLoop.before_loop
async def __avant_reminderLoop(self):
await self.client.wait_until_ready()
@commands.command(name='reminder', aliases=["remind", "remindme", "rappel"])
async def _reminder(self, ctx, time, *reminder):
"""Met en place un rappel.\n ➡ Syntaxe: {PREFIX}reminder/remind/remindme/rappel <temps>[@] [message]"""
_, fromSlash, reminder = isSlash(reminder)
if len(reminder) > 0:
reminder = " ".join(reminder)
else:
reminder = None
embed = discord.Embed(color = 0xC41B1B)
extrarg = 0
guildID = ctx.guild.id # can be set to 0 if its a DM message, so it can be see from anywhere
destination = "dans ce salon"
if not reminder:
reminder = "Notification"
if time == "help":
seconds = 0
else:
if time.endswith("@"):
time = time[:-1]
extrarg = 1
if time.lower().endswith("p"):
time = time[:-1]
extrarg = 2
guildID = 0
destination = "en MP"
seconds = stringTempsVersSecondes(time)
if type(seconds) != int:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await mySendHidden(ctx, fromSlash, seconds)
if seconds == 0:
embed.add_field(name="Informations", value=
"Format pour le temps : `y` ou `a` pour année, `w` pour semaine, `d` ou `j` pour jour, \
\n`h` pour heure, `m` pour minute, `s` pour seconde (légères variances acceptés aussi).\n \
\nMet un `@` accolée aux temps pour mentionner les gens mentionner dans ton message. \
\nMet un `P` accolée au temps pour que le bot te DM au lieu de t'envoyer un message dans ce salon."
)
elif seconds > (50 * (86400 * 365.242)): # 50 ans
embed.add_field(name="Attention", value="Tu as spécifié une durée trop longue, la durée maximum étant de 50 ans.")
else:
now = int(nowUTC())
messageID = None
if fromSlash != True:
messageID = ctx.message.id
reminderID = Reminder().ajoutReminder(messageID, ctx.channel.id, extrarg, reminder, now, now + seconds, ctx.author.id, guildID)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await mySendHidden(ctx, fromSlash, f"Reminder **`#{reminderID[0][0]}`** enrengistré ! Notification {destination} dans {timedeltaToString(seconds)}{'' if seconds >= 3600 else ' (avec 1m de retard maximum)'}.")
await mySendHidden(ctx, fromSlash, embed = embed)
@_reminder.error
async def _reminder_error(self, ctx, error):
if 'time is a required argument that is missing.' in str(error):
await ctx.send("Tu n'as pas spécifié de durée.")
@cog_ext.cog_slash(name="reminder", description = "Met en place un rappel.")
async def __reminder(self, ctx, time, reminder = None):
if reminder == None:
return await self._reminder(ctx, time, True)
else:
return await self._reminder(ctx, time, reminder, True)
@commands.command(name='reminderlist', aliases=["remindlist", "rl", "rappeliste"])
async def _reminderlist(self, ctx, *arg):
"""Affiche la liste des rappels d'un utilisateur.\n ➡ Syntaxe: {PREFIX}reminderlist/rl/remindlist/rappeliste [utilisateur]"""
_, fromSlash, arg = isSlash(arg)
utilisateur = ctx.author
page = 1
erreur = False
if len(arg) > 0:
for i in range(0, len(arg)):
try:
utilisateur = self.client.get_user(mentionToUser(getMentionInString(arg[i])[0]))
except:
try:
page = int(arg[i])
except:
erreur = True
if erreur == True:
return await mySendHidden(ctx, fromSlash, "Veuillez renseigné un utilisateur ou un numéro de page valide.")
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
embed, pageMAX, refresh = await embedListe(utilisateur, ctx.guild.id, page)
message = await ctx.send(embed = embed)
if refresh:
await message.add_reaction("🔄")
elif pageMAX > 1:
for emoji in ["⬅️", "➡️"]:
await message.add_reaction(emoji)
@cog_ext.cog_slash(name="reminderlist", description = "Affiche la liste des rappels d'un utilisateur.")
async def __reminderlist(self, ctx, userorpage = None):
if userorpage == None:
return await self._reminderlist(ctx, True)
else:
return await self._reminderlist(ctx, userorpage, True)
@commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
message, embed = await listReaction(self.client, payload)
if message:
await message.edit(embed = embed)
@commands.Cog.listener()
async def on_raw_reaction_remove(self, payload):
message, embed = await listReaction(self.client, payload)
if message:
await message.edit(embed = embed)
@commands.command(name='reminderdelete', aliases=["reminddelete", "rd"])
async def _reminderdelete(self, ctx, *id):
"""Suppprime un rappel.\n ➡ Syntaxe: {PREFIX}reminderdelete/rd <id>"""
id, fromSlash, _ = isSlash(id)
if id:
try:
id = int(id)
except:
return await mySendHidden(ctx, fromSlash, "L'ID renseigné n'est pas valide.")
else:
return await ctx.send("Veuillez renseigner un ID.")
verification = Reminder().appartenanceReminder(ctx.author.id, id, ctx.guild.id)
if verification:
Reminder().suppressionReminder(id)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await mySendHidden(ctx, fromSlash, f"Reminder **#{id}** supprimé !")
else:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await mySendHidden(ctx, fromSlash, "Rappel non trouvé, pas sur le bon serveur ou qui ne vous appartiens pas.")
@cog_ext.cog_slash(name="reminderdelete", description = "Suppprime un rappel.")
async def __reminderdelete(self, ctx, id):
return await self._reminderdelete(ctx, id, True)

80
src/cogs/school.py Normal file
View file

@ -0,0 +1,80 @@
import discord
from discord.ext import commands
from discord_slash import cog_ext
from utils.core import isSlash, mySendHidden
def setup(client):
client.add_cog(School(client))
class School(commands.Cog):
"""Commandes relatives aux cours."""
def __init__(self, client):
self.client = client
@commands.command(name='appel')
# @commands.has_any_role("Professeur", "professeur", "Prof", "prof")
async def _appel(self, ctx, *voice_channel: int):
"""Fais l'appel.\n ➡ Syntaxe: {PREFIX}appel [ID salon vocal]"""
voice_channel, fromSlash, _ = isSlash(voice_channel)
voice_channels = []
voice_channels.extend(ctx.guild.voice_channels)
if fromSlash != True:
await ctx.message.add_reaction(emoji = "")
limite_voice_channels = 7
if len(voice_channels) > limite_voice_channels and not voice_channel:
return await mySendHidden(ctx, fromSlash, f"""Désolé mais il y a plus de {limite_voice_channels} salons vocaux sur ce serveur, utilisez plutôt `{ctx.prefix}appel {{ID salon vocal}}`.
\nPour savoir comment récuperer l'id d'un salon vous pouvez faire `{ctx.prefix}getid`.""")
if voice_channel:
canal = self.client.get_channel(voice_channel)
if canal.type.__str__() == "voice":
voice_channels = [canal]
else:
return await mySendHidden(ctx, fromSlash, "Tu as spécifié un channel textuelle et non vocal.")
if len(voice_channels) > 0:
embed = discord.Embed(title = "Réagissez à ce message avec ✋ pour signaler votre présence.", description = f"(attention, je réagis aussi) — Demandeur : {ctx.author.mention}")
for channel in voice_channels:
prof = []
for role in ["Professeur", "professeur", "Prof", "prof"]:
role = discord.utils.get(ctx.guild.roles, name=role)
for user in channel.members:
if role in user.roles and user not in prof:
prof.append(user)
eleve = channel.members
for user in channel.members:
if user in prof:
eleve.remove(user)
value = f"**{len(channel.members)}** personne{'s' if len(channel.members)>1 else ''} connectée{'s' if len(channel.members)>1 else ''}.\nDont {len(eleve)} élève{'s' if len(eleve)>1 else ''} et {len(prof)} professeur{'s' if len(prof)>1 else ''}."
embed.add_field(name = f"🔊 {channel.name}", value = value, inline = False)
message = await ctx.send(embed = embed)
else:
message = await ctx.send("Aucun salon vocal dans ce serveur, réagissez à ce message avec ✋ pour signaler votre présence (attention, je réagis aussi).")
await message.add_reaction(emoji = "")
@_appel.error
async def _appel_error(self, ctx, _):
# if isinstance(error, commands.CheckFailure):
# await ctx.send("Tu n'as pas la permission de faire cette commande, demande à un professeur.")
# else:
await ctx.send(f"Une erreur est survenue, syntaxe: `{ctx.prefix}appel [ID salon vocal]`.")
@cog_ext.cog_slash(name="appel", description = "Fais l'appel.")
async def __appel(self, ctx, voice_channel_id = None):
ctx.prefix = "/"
if voice_channel_id == None:
return await self._appel(ctx, True)
else:
try:
return await self._appel(ctx, int(voice_channel_id), True)
except:
pass
@commands.command(name='getid', hidden = True)
async def _getid(self, ctx, fromSlash = None):
"""Tuto vidéo sur comment récupérer l'ID d'un utilisateur/salon"""
if fromSlash != True:
fromSlash = False
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await mySendHidden(ctx, fromSlash, "Explication sur comment récuperer l'ID d'un utilisateur/salon : https://cdn.discordapp.com/attachments/640312926892195842/780802253258358834/GetID.mp4")
@cog_ext.cog_slash(name="getid", description = "Tuto vidéo sur comment récupérer l'ID d'un utilisateur/salon")
async def __getid(self, ctx):
return await self._getid(ctx, True)

View file

@ -1,51 +1,81 @@
import discord, pytz, time
import discord
from discord.ext import commands
from random import randint, shuffle
from datetime import datetime
from pytz import timezone
import re
from re import findall
from discord_slash import cog_ext
from utils.core import map_list_among_us, cleanCodeStringWithMentionAndURLs, cleanUser, userOrNick
from utils.core import mySendHidden, mentionToUser, getChangelogs, getActualVersion, isSlash, load, devOrStableChannel
from utils.time import nowUTC, intToDatetime, timestampScreen, getAge, ageLayout, nowCustom
def setup(client):
client.add_cog(Utils(client))
class Utils(commands.Cog):
"""Commandes essentielles."""
def __init__(self, client):
self.client = client
self.customTimezone = load(["TIMEZONE"])["TIMEZONE"]
@commands.command(name='ping')
async def _ping(self, ctx, *, question = '0'):
"""Affiche mon ping.\n ➡ Syntaxe: .ping [help]"""
if question == 'help':
return await ctx.send(embed = discord.Embed(color = discord.Colour.random(), description = ":hourglass: correspond au temps entre deux battements de cœurs (en millisecondes)\n\n:stopwatch: correspond au temps que met le client a calculer le ping (en millisecondes)\n\n:heartbeat: correspond au temps que met le client a réagir au messages (en millisecondes)"))
async def _ping(self, ctx, *arg):
arg, fromSlash, _ = isSlash(arg)
if arg == 'help':
return await mySendHidden(ctx, fromSlash, embed = discord.Embed(color = discord.Colour.random(), description =
":hourglass: correspond au temps entre deux battements de cœurs\n\n \
:heartbeat: correspond au temps que met le client a réagir au messages (0 est normal lors de l'utilisation d'une commande slash)\n\n \
:stopwatch: correspond au temps que met le client a calculer le ping"
))
else:
now = int(round(time.time() * 1000))
now = int(round(nowCustom() * 1000))
if fromSlash != True:
ping = now - int(round(ctx.message.created_at.timestamp() * 1000))
else:
ping = now - int(round(ctx.slash_created_at * 1000))
embed = discord.Embed(description = 'Pinging...')
message = await ctx.send(embed = embed)
ping2 = int(round(time.time() * 1000)) - now
await message.edit(embed = discord.Embed(color = discord.Colour.random(), description = f':hourglass: {round(self.client.latency * 1000)}ms\n\n:stopwatch: {ping2}ms\n\n:heartbeat: {ping}ms'))
ping2 = int(round(nowCustom() * 1000)) - now
await message.edit(embed = discord.Embed(color = discord.Colour.random(), description = f':hourglass: {round(self.client.latency * 1000)} ms\n\n:heartbeat: {ping} ms\n\n:stopwatch: {ping2} ms'))
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
@cog_ext.cog_slash(name="ping", description = "Affiche mon ping, mettre 'help' en argument pour connaître à quoi correspond les données.")
async def __ping(self, ctx, arg = None):
ctx.slash_created_at = nowCustom()
if arg == None:
return await self._ping(ctx, True)
else:
return await self._ping(ctx, arg, True)
@commands.command(name='avatar')
async def _avatar(self, ctx, *, user = '0'):
"""Affiche ton avatar ou celui que tu mentionnes.\n ➡ Syntaxe: .avatar [user]"""
if user == '0':
async def _avatar(self, ctx, *user):
"""Affiche ton avatar ou celui que tu mentionnes.\n ➡ Syntaxe: {PREFIX}avatar [user]"""
user, fromSlash, _ = isSlash(user)
if user == None:
user = ctx.author
else:
user = self.client.get_user(int(user[2:-1].replace("!","")))
user = self.client.get_user(mentionToUser(user))
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
embed = discord.Embed(description = f"[lien vers la photo de profil]({user.avatar_url}) de {user.mention}", color = discord.Colour.random())
embed.set_author(name = f"Photo de profil de {user.name}")
embed.set_image(url = user.avatar_url)
await ctx.send(embed = embed)
@cog_ext.cog_slash(name="avatar", description = "Affiche ton avatar ou celui que tu mentionnes.")
async def __avatar(self, ctx, user = None):
if user == None:
return await self._avatar(ctx, True)
else:
return await self._avatar(ctx, user, True)
@commands.command(name='calc')
async def _calc(self, ctx, *, msg):
"""Calculatrice.\n ➡ Syntaxe: .calc <calcul>"""
equation = msg.replace('^', '**').replace('x', '*').replace('×', '*').replace('÷', '/').replace('', '>=').replace('', '<=')
async def _calc(self, ctx, *calcul):
"""Calculatrice.\n ➡ Syntaxe: {PREFIX}calc <calcul>"""
calcul, fromSlash, _ = isSlash(calcul)
if calcul == None:
raise ModuleNotFoundError
equation = calcul.replace('^', '**').replace('x', '*').replace('×', '*').replace('÷', '/').replace('', '>=').replace('', '<=')
try:
try:
if '=' in equation:
@ -64,9 +94,9 @@ class Utils(commands.Cog):
else:
answer = str(eval(equation))
except ZeroDivisionError:
return await ctx.send("Tu ne peux pas divisé par 0.")
return await mySendHidden(ctx, fromSlash, "Tu ne peux pas diviser par 0.")
except TypeError:
return await ctx.send("Requête de calcul invalide.")
return await mySendHidden(ctx, fromSlash, "Requête de calcul invalide.")
if '.' in answer:
aftercomma = answer.split(".")[1]
if len(aftercomma) > 2:
@ -74,86 +104,115 @@ class Utils(commands.Cog):
equation = f"'{equation}' arrondi à 2"
equation = equation.replace('*', '×').replace('/', '÷').replace('>=', '').replace('<=', '')
embed = discord.Embed(color = discord.Colour.random(), title = 'Calculatrice')
embed.set_footer(text = ctx.author)
embed.set_footer(text = userOrNick(ctx.author), icon_url = ctx.author.avatar_url)
embed.add_field(name = 'Calcul :', value = equation, inline = False)
embed.add_field(name = 'Réponse :', value = answer.replace('False', 'Faux').replace('True', 'Vrai'), inline = False)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(content = None, embed = embed)
await ctx.send(embed = embed)
@_calc.error
async def _calc_error(self, ctx, error):
async def _calc_error(self, ctx, _):
await ctx.send("Tu n'as pas spécifié de calcul.")
@cog_ext.cog_slash(name="calc", description = "Calculatrice.")
async def __calc(self, ctx, calcul):
return await self._calc(ctx, calcul, True)
@commands.command(name='syntax')
async def _syntax(self, ctx):
async def _syntax(self, ctx, fromSlash = None):
"""Informations pour bien éditer son texte."""
syntaxe = "-----------------------------------------------------\n"
syntaxe += discord.utils.escape_markdown("```Js\n")
if fromSlash != True:
fromSlash = False
separateur = "-----------------------------------------------------\n"
syntaxe = separateur
syntaxe += discord.utils.escape_markdown("```js\n")
syntaxe += discord.utils.escape_markdown("//code en js (possible de remplacer 'js' par d'autres languages . adaptez le !)\n")
syntaxe += discord.utils.escape_markdown('console.log("hi");\n')
syntaxe += discord.utils.escape_markdown("```\n")
syntaxe += "```Js\n"
syntaxe += "```js\n"
syntaxe += "//code en js (possible de remplacer 'js' par d'autres languages . adaptez le !)\n"
syntaxe += 'console.log("hi");\n'
syntaxe += "```\n"
syntaxe += "Si ton code est trop long, mets le sur <https://pastebin.com/>\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("`code sur une seule ligne`\n")
syntaxe += "`code sur une seule ligne`\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("*texte en italique*\n")
syntaxe += "*texte en italique*\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("**text en gras**\n")
syntaxe += "**text en gras**\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("<<https://www.youtube.com/watch?v=GhuYKL5NUYg>>\n")
syntaxe += "Un lien entre crochet, ça empêche Discord de rajouté son intégration automatique (mais le lien fonctionnera toujours).\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("__texte souligné__\n")
syntaxe += "__texte souligné__\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("~~texte barré~~\n")
syntaxe += "~~texte barré~~\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("~~__***text en italique-gras-souligné-barré***__~~\n")
syntaxe += "~~__***text en italique-gras-souligné-barré***__~~\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("\:joy: <- l'emoji ne va pas fonctionné grâce au \ \n")
syntaxe += "\:joy: <- l'emoji ne va pas fonctionné grâce au \ \n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown("> cette ligne est cité\npas celle là\n")
syntaxe += "> cette ligne est cité\npas celle là\n"
syntaxe += "-----------------------------------------------------\n"
syntaxe += separateur
syntaxe += discord.utils.escape_markdown(">>> cette ligne est cité\ncelle là aussi (et elles le seront toutes!)\n")
syntaxe += ">>> cette ligne est cité\ncelle là aussi (et elles le seront toutes!)\n"
try:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(syntaxe)
except:
pass
await mySendHidden(ctx, fromSlash, syntaxe)
@cog_ext.cog_slash(name="syntax", description = "Informations pour bien éditer son texte.")
async def __syntax(self, ctx):
return await self._syntax(ctx, True)
@commands.command(name='memo', aliases = ['note'])
async def _memo(self, ctx, *, text):
"""T'envoie un petit memo par message privé.\n ➡ Syntaxe: .memo/note <message>"""
if len(text) <= 5:
await ctx.message.add_reaction(emoji = '')
return await ctx.send("Ta note doit au moins faire 5 caractères.")
elif len(text) >= 2048:
await ctx.message.add_reaction(emoji = '')
return await ctx.send("Ta note doit faire moins de 2048 caractères.")
async def _memo(self, ctx, *text):
"""T'envoie un petit memo par message privé.\n ➡ Syntaxe: {PREFIX}memo/note <message>"""
_, fromSlash, text = isSlash(text)
if len(text) > 0:
text = " ".join(text)
else:
raise ModuleNotFoundError
if len(text) <= 5:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await mySendHidden(ctx, fromSlash, "Ta note doit au moins faire 5 caractères.")
elif len(text) >= 2048:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(ctx, fromSlash, "Ta note doit faire moins de 2048 caractères.")
else:
if fromSlash != True:
await ctx.message.delete()
embed = discord.Embed(description = text, color = discord.Colour.random())
embed.set_author(name = f"Mémo noté depuis {ctx.guild.name}", icon_url = ctx.author.avatar_url)
embed.set_footer(text = f'📝 le {datetime.now(pytz.timezone("Europe/Paris")).strftime("%d/%m/%Y à %H:%M:%S")}')
embed.set_footer(text = f'📝 le {timestampScreen(intToDatetime(nowUTC()))}')
await ctx.author.send(embed = embed)
return await ctx.send("Tu viens de recevoir ton mémo !", delete_after = 5)
return await mySendHidden(ctx, fromSlash, "Tu viens de recevoir ton mémo !", delete_after = 5)
@_memo.error
async def _note_error(self, ctx, error):
async def _memo_error(self, ctx, error):
if str(error) == "text is a required argument that is missing.":
await ctx.send("Vous devez renseigner un message : `.note/memo <message>`.")
await ctx.send(f"Vous devez renseigner un message : `{ctx.prefix}memo/note <message>`.")
@cog_ext.cog_slash(name="note", description = "T'envoie un petit memo par message privé.")
async def __memo(self, ctx, memo):
return await self._memo(ctx, memo, True)
@commands.command(name='infos', aliases = ['info'])
async def _infos(self, ctx):
"""Donne des infos sur le bot.\n ➡ Syntaxe: .infos/info"""
async def _infos(self, ctx, fromSlash = None):
"""Donne des infos sur le bot.\n ➡ Syntaxe: {PREFIX}infos/info"""
if fromSlash != True:
fromSlash = False
appinfo = await self.client.application_info()
embed = discord.Embed(color = discord.Colour.random())
@ -171,71 +230,101 @@ class Utils(commands.Cog):
text = len(text_channels)
voice = len(voice_channels)
nombreServeur = len(self.client.guilds)
embed.add_field(name = "Dev", value = f"[{appinfo.owner}](https://github.com/Mylloon)")
embed.add_field(name = "Serveurs", value = len(self.client.guilds))
embed.add_field(name = "Membres", value = f"{total_unique} au total\n{total_online} en ligne")
embed.add_field(name = "Channels", value = f"{text} textuelles\n{voice} vocales")
version = getActualVersion()
dev = self.client.get_user(158260864623968257)
devOrMain = devOrStableChannel()
embed.add_field(name = "Dev", value = f"[{dev}](https://discord.gg/Z5ePxH4)")
embed.add_field(name = "Hébergeur", value = appinfo.owner.mention)
embed.add_field(name = f"Serveur{'s' if nombreServeur > 1 else ''}", value = f"`{nombreServeur}`")
embed.add_field(name = f"Membre{'s' if total_unique > 1 else ''}", value = f"`{total_unique}` au total\n`{total_online}` en ligne")
embed.add_field(name = f"Salon{'s' if (text + voice) > 1 else ''}", value = f"`{text}` textuel{'s' if text > 1 else ''}\n`{voice}` voca{'ux' if voice > 1 else 'l'}")
embed.add_field(name = "Prefix", value = f"`{ctx.prefix}`")
embed.add_field(name = "Code source", value = f"[Lien Gitlab](https://gitlab.com/ConfrerieDuKassoulait/KassouBot/-/tree/{devOrMain})")
embed.add_field(name = "Timezone", value = f"`{self.customTimezone}`")
changes = getChangelogs(version)
version = f"`{version}`"
if changes[0] == 200:
version = f"[{version}]({changes[1]})"
embed.add_field(name = "Version", value = f"{version} ({devOrMain.replace('main', 'stable')})")
embed.set_footer(text = f"Basé sur discord.py {discord.__version__}")
try:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
except:
pass
await ctx.send(embed = embed)
@cog_ext.cog_slash(name="infos", description = "Donne des infos sur le bot.")
async def __infos(self, ctx):
ctx.prefix = "/"
return await self._infos(ctx, True)
@commands.command(name='amongus')
async def _amongus(self, ctx, *map):
"""Affiche la carte voulue d'Among Us.\n ➡ Syntaxe: {PREFIX}amongus <mira/polus/skeld/airship>"""
_, fromSlash, map = isSlash(map)
if len(map) > 0:
map = " ".join(map)
else:
map = "0"
if map.lower() in map_list_among_us("mira"):
image = "https://i.imgur.com/6ijrH1h.jpg"
embed = discord.Embed(title = f"Map Mira HQ d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(embed = embed)
def _map_list_among_us(self, map):
maps = {}
maps["skeld"] = ["skeld", "the skeld", "theskeld"]
maps["mira"] = ["mira", "mira hq", "mirahq"]
maps["polus"] = ["polus"]
maps["airship"] = ["airship", "air ship"]
if map == "all":
return maps["skeld"] + maps["mira"] + maps["polus"] + maps["airship"]
return maps[map]
elif map.lower() in map_list_among_us("polus"):
image = "https://i.imgur.com/mhFmcw3.jpg"
embed = discord.Embed(title = f"Map Polus d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(embed = embed)
elif map.lower() in map_list_among_us("skeld"):
image = "https://i.imgur.com/OSXI4Zv.jpg"
embed = discord.Embed(title = f"Map The Skeld d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(embed = embed)
elif map.lower() in map_list_among_us("airship"):
image = "https://i.imgur.com/cm8Wogw.png"
embed = discord.Embed(title = f"Map Airship d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
await ctx.send(embed = embed)
else:
await mySendHidden(ctx, fromSlash, f"`{ctx.prefix}amongus <mira/polus/skeld/airship>`")
@commands.command(name='among', hidden = True)
async def _among(self, ctx, *, args = ""):
"""Raccourci à la commande amongus"""
if not args == "":
args = args.split()
del args[0]
args = " ".join(args)
if args.lower() in self._map_list_among_us("all"):
if args.lower() in map_list_among_us("all"):
await ctx.invoke(self.client.get_command("amongus"), map=args)
else:
await ctx.invoke(self.client.get_command("amongus"))
else:
await ctx.message.add_reaction(emoji = '')
@commands.command(name='amongus')
async def _amongus(self, ctx, *, map = "0"):
"""Affiche la carte voulue d'Among Us.\n ➡ Syntaxe: .amongus <carte>"""
if map.lower() in self._map_list_among_us("mira"):
image = "https://i.imgur.com/6ijrH1h.jpg"
embed = discord.Embed(title = f"Map Mira HQ d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
await ctx.send(embed = embed)
await ctx.message.add_reaction(emoji = '')
elif map.lower() in self._map_list_among_us("polus"):
image = "https://i.imgur.com/mhFmcw3.jpg"
embed = discord.Embed(title = f"Map Polus d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
await ctx.send(embed = embed)
await ctx.message.add_reaction(emoji = '')
elif map.lower() in self._map_list_among_us("skeld"):
image = "https://i.imgur.com/OSXI4Zv.jpg"
embed = discord.Embed(title = f"Map The Skeld d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
await ctx.send(embed = embed)
await ctx.message.add_reaction(emoji = '')
elif map.lower() in self._map_list_among_us("airship"):
image = "https://i.imgur.com/CYbPlQ6.png"
embed = discord.Embed(title = f"Map Airship d'Among Us", color = discord.Colour.random(), description = f"[lien de l'image]({image})")
embed.set_image(url = image)
await ctx.send(embed = embed)
await ctx.message.add_reaction(emoji = '')
else:
await ctx.send("`.amongus <mira/polus/skeld/airship>`")
@cog_ext.cog_slash(name="amongus", description = "Affiche la carte voulue d'Among Us. Carte dispo : <mira/polus/skeld/airship>")
async def __amongus(self, ctx, map):
ctx.prefix = "/"
return await self._amongus(ctx, map, True)
@commands.command(name='whois')
async def _whois(self, ctx, *user: discord.Member):
"""Affiche les infos sur l'utilisateur.\n ➡ Syntaxe: .whois [user]"""
"""Affiche les infos sur l'utilisateur.\n ➡ Syntaxe: {PREFIX}whois [user]"""
_, fromSlash, user = isSlash(user)
if len(user) <= 1:
if user == ():
user = [ctx.author]
@ -246,64 +335,39 @@ class Utils(commands.Cog):
embed.add_field(name = "ID", value = user[0].id)
value = str(user[0].created_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').split()
embed.add_field(name = "Compte créé le", value = f"{value[0][8:]}/{value[0][5:-3]}/{value[0][:4]} à {value[1]}")
embed.add_field(name = "Compte créé le", value = timestampScreen(user[0].created_at))
embed.add_field(name = "Âge du compte", value = self._age_layout(self._get_age(user[0].created_at)))
embed.add_field(name = "Âge du compte", value = ageLayout(getAge(user[0].created_at)))
embed.add_field(name = "Mention", value = user[0].mention)
value = str(user[0].joined_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').split()
embed.add_field(name = "Serveur rejoint le", value = f"{value[0][8:]}/{value[0][5:-3]}/{value[0][:4]} à {value[1]}")
embed.add_field(name = "Serveur rejoint le", value = timestampScreen(user[0].joined_at))
embed.add_field(name = "Est sur le serveur depuis", value = self._age_layout(self._get_age(user[0].joined_at)))
embed.add_field(name = "Est sur le serveur depuis", value = ageLayout(getAge(user[0].joined_at)))
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send(embed = embed)
await ctx.send("Tu mentionnes trop d'utilisateurs : `.whois [@Membre]`")
def _get_age(self, date):
joursRestants = datetime.now() - date
years = joursRestants.total_seconds() / (365.242 * 24 * 3600)
months = (years - int(years)) * 12
days = (months - int(months)) * (365.242 / 12)
hours = (days - int(days)) * 24
minutes = (hours - int(hours)) * 60
seconds = (minutes - int(minutes)) * 60
return (int(years), int(months), int(days), int(hours), int(minutes), int(seconds))
def _age_layout(self, tuple):
time = {}
time[0], time[1], time[2], time[3], time[4], time[5] = "an", "mois", "jour", "heure", "minute", "seconde"
for i in range(len(tuple)):
if tuple[i] > 1 and i != 1:
time[i] = time[i] + "s"
message = ""
if tuple[5] > 0: # pour les secondes
affichage = [5] # on affiche que : seconde
if tuple[4] > 0:
affichage = [4, 5] # on affiche : minute + seconde
if tuple[3] > 0:
affichage = [3, 4, 5] # on affiche : heure + minute + seconde
if tuple[2] > 0:
affichage = [2, 3, 4] # on affiche : jour + heure + minute
if tuple[1] > 0:
affichage = [1, 2, 3] # on affiche : mois + jour + heure
if tuple[0] > 0:
affichage = [0, 1, 3] # on affiche : an + mois + heure
for i in affichage:
message = message + f", {tuple[i]} {time[i]}"
return message[2:]
return await ctx.send(f"Tu mentionnes trop d'utilisateurs : `{ctx.prefix}whois [@membre]`")
@cog_ext.cog_slash(name="whois", description = "Affiche les infos sur l'utilisateur.")
async def __whois(self, ctx, user: discord.Member = None):
ctx.prefix = "/" # pas sûr que ce soit utile
if user == None:
return await self._whois(ctx, True)
else:
return await self._whois(ctx, user, True)
@commands.command(name='sondage')
async def _sondage(self, ctx, *args):
"""Fais un sondage.\n ➡ Syntaxe: .sondage "<Question>" "<Proposition1>" "<Proposition...>" "<Proposition20>" """
"""Fais un sondage.\n ➡ Syntaxe: {PREFIX}sondage "<Question>" "<Proposition1>" "<Proposition...>" "<Proposition20>" """
_, fromSlash, args = isSlash(args)
if type(args[0]) == list:
args = args[0]
args = list(args)
if len(args) > 2:
question = args[0].replace("<@!", "").replace(">", "").replace("<@", "")
for i in re.findall(r'\d+', question):
ii = self.user_or_nick(ctx.author.guild.get_member(int(i)))
try:
question = question.replace(i, ii)
except:
pass
question = args[0]
for i in findall(r'\d+', question):
question = cleanUser(ctx, question, i)
propositions = args[1:]
if len(propositions) <= 20:
message = ""
@ -327,17 +391,103 @@ class Utils(commands.Cog):
shuffle(emojis_chosen)
for i in range(len(args[1:])):
message += f"{emojis_chosen[i]} -> {propositions[i]}\n"
embed = discord.Embed(title = question, description = message,color = discord.Colour.random()).set_footer(text = self.user_or_nick(ctx.author), icon_url = ctx.author.avatar_url)
embed = discord.Embed(title = question, description = message, color = discord.Colour.random()).set_footer(text = f"Sondage de {userOrNick(ctx.author)}", icon_url = ctx.author.avatar_url)
sondage = await ctx.send(embed = embed)
for i in range(len(args[1:])):
await sondage.add_reaction(emoji = emojis_chosen[i])
if fromSlash != True:
return await ctx.message.add_reaction(emoji = '')
else:
return await ctx.send(f"Désolé, mais tu as mis trop de possibilités (maximum : 20)")
else:
return await ctx.send(f'Désolé, mais il manque des arguments : `.sondage "<Question>" "<Proposition1>" "<Proposition...>" "<Proposition20>"`')
def user_or_nick(self, user):
if user.nick:
return f"{user.nick} ({user.name}#{user.discriminator})"
return await ctx.send(f'Désolé, mais il manque des arguments : `{ctx.prefix}sondage "<Question>" "<Proposition1>" "<Proposition...>" "<Proposition20>"`')
@cog_ext.cog_slash(name="sondage", description = "Fais un sondage.")
async def __sondage(self, ctx, question, prop1, prop2, prop3 = None, prop4 = None,
prop5 = None, prop6 = None, prop7 = None, prop8 = None, prop9 = None, prop10 = None,
prop11 = None, prop12 = None, prop13 = None, prop14 = None, prop15 = None, prop16 = None,
prop17 = None, prop18 = None, prop19 = None, prop20 = None):
ctx.prefix = "/"
args = [question, prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8,
prop9, prop10, prop11, prop12, prop13, prop14, prop15, prop16,
prop17, prop18, prop19, prop20]
for i in range(3, 20): # suppression des None
if args[i] == None:
args = args[:i]
break
return await self._sondage(ctx, args, True)
@commands.command(name='avis', aliases=['vote'])
async def _avis(self, ctx, *args):
"""Demande un avis.\n ➡ Syntaxe: {PREFIX}avis/vote "[Titre]" "<Demande>" """
_, fromSlash, args = isSlash(args)
if type(args[0]) == list:
args = args[0]
args = list(args)
if len(args) > 2 or len(args) == 0:
return await ctx.send("Désolé, la syntaxe est mauvaise.")
else:
return f"{user.name}#{user.discriminator}"
if len(args) == 1: # si aucun titre défini
titre = "Nouveau vote"
else: # si titre défini
titre = args[0]
for findedId in findall(r'\d+', titre): # récupération mention dans titre
titre = cleanUser(ctx, titre, findedId)
args = args[1:]
embed = discord.Embed(title = titre, description = cleanCodeStringWithMentionAndURLs(args[0]), color = discord.Colour.random()).set_footer(text = f"Sondage de {userOrNick(ctx.author)}", icon_url = ctx.author.avatar_url)
message = await ctx.send(embed = embed)
reactions = ['', '🤷', '']
for i in reactions:
await message.add_reaction(emoji = i)
if fromSlash != True:
return await ctx.message.delete()
@cog_ext.cog_slash(name="avis", description = "Demande un avis, si 2 arguments, alors l'argument 1 est le titre, sinon c'est la demande.")
async def __avis(self, ctx, titreoudemande, demande = None):
args = [titreoudemande, demande]
if args[1] == None:
args = args[:1]
return await self._avis(ctx, args, True)
@commands.command(name='changelogs', aliases=["changelog", "changement", "changements"])
async def _changelogs(self, ctx, *version):
"""Affiche les changements de la dernière version ou d'une version précise.\n ➡ Syntaxe: {PREFIX}changelogs/changelog/changement/changements [version]"""
version, fromSlash, _ = isSlash(version)
if not version:
version = 'actual'
changes = getChangelogs(version.replace(',', '.'))
if changes[0] != 200:
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
if changes[0] == 404:
message = "Veuillez renseigner un numéro de version valide et existant. Peut-être que vous utilisez une version de développement."
elif changes[0] == 429:
message = "Trop de requêtes sur l'API de Gitlab, réessayez plus tard."
else:
message = f"Erreur API inconnue ({changes[0]})."
return await mySendHidden(ctx, fromSlash, message)
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
changements = changes[3].replace("## ", "").replace("# ", "")
if len(changements) > 2048:
changements = f"{changements[:1900]}..."
embed = discord.Embed(description = f"[lien vers la page Gitlab]({changes[1]})\n\n{changements}", color = discord.Colour.random())
embed.set_author(name = f"Changements de la v{changes[2]}")
await ctx.send(embed = embed)
@cog_ext.cog_slash(name="changelogs", description = "Affiche les changements de la dernière version ou d'une version précise.")
async def __changelogs(self, ctx, version = None):
if version == None:
return await self._changelogs(ctx, True)
else:
return await self._changelogs(ctx, version, True)
@commands.command(name='minecraft', aliases=["codecouleurminecraft", "minecraftcodecouleur"])
async def _minecraft(self, ctx, fromSlash = None):
"""Affiche le code couleur utilisé dans Minecraft.\n ➡ Syntaxe: {PREFIX}minecraft/codecouleurminecraft/minecraftcodecouleur"""
if fromSlash != True:
fromSlash = False
if fromSlash != True:
await ctx.message.add_reaction(emoji = '')
return await ctx.send("https://imgr.search.brave.com/5-k6Lgh0OyRI8IVwhiBrNRmlY94utGxSX0k9tvtpqiA/fit/590/150/no/1/aHR0cDovL2kuaW1n/dXIuY29tL3Azd2lz/OVAucG5n")
@cog_ext.cog_slash(name="minecraft", description = "Affiche le code couleur utilisé dans Minecraft.")
async def __minecraft(self, ctx):
return await self._minecraft(ctx, True)

0
src/db/.gitkeep Normal file
View file

View file

@ -1,214 +1,68 @@
print("Connexion à Discord...")
print("Chargement des extensions & librairie...", end = " ")
import discord, re, pytz, os
import discord
from os import listdir, rename, getcwd
from discord_slash import SlashCommand
from discord.ext import commands
from random import choice
from datetime import datetime
from pytz import timezone
from utils.reminder import Reminder
from utils.core import load
keys = load(["PREFIX", "TOKEN_DISCORD", "DEACTIVATE"])
customPrefix = keys["PREFIX"]
client = commands.Bot(command_prefix = ".", case_insensitive = True, intents = discord.Intents.all())
client = commands.Bot(command_prefix = customPrefix, case_insensitive = True, intents = discord.Intents.all())
slash = SlashCommand(client, sync_commands = True)
print("Chargement des extensions & librairie...")
client.load_extension("cogs.help")
client.load_extension("cogs.utils")
client.load_extension("cogs.internet")
client.load_extension("cogs.music")
client.load_extension("cogs.games")
client.load_extension("cogs.fun")
client.load_extension("cogs.autopublish")
if keys["DEACTIVATE"] != "None":
path = getcwd()
for file in keys["DEACTIVATE"]:
try:
rename(f"{path}/cogs/{file}.py", f"{path}/cogs/-{file}.py") # désactivation
except:
print(f"No file {file} found, check your \"DEACTIVATE\" variable.")
exit(1)
for file in listdir("cogs"):
if file.endswith(".py") and file.startswith("-") == False:
client.load_extension(f"cogs.{file[:-3]}")
print("Terminé !")
@client.event
async def on_connect():
print(f"Connecté.")
print(f"Connecté !")
@client.event
async def on_disconnect():
print(f"Déconnecté.")
@client.event
async def on_resumed():
print(f"Reconnecté !")
@client.event
async def on_ready():
await client.change_presence(status = discord.Status.online, activity = discord.Activity(name = ".help", type = discord.ActivityType.playing))
await client.change_presence(status = discord.Status.online, activity = discord.Activity(name = f"{customPrefix}help", type = discord.ActivityType.playing))
Reminder().creationTable()
print("Bot prêt.")
channel = client.get_channel(742564480050790512)
await channel.send("Le bot a bien démarré.")
@client.event
async def on_member_join(member):
if member.guild.id == 441208120644075520: # Confrérie du Kassoulait
if member.bot == True:
role = discord.utils.get(member.guild.roles, name = "Bot")
else:
role = discord.utils.get(member.guild.roles, name = "Copain")
await member.add_roles(role)
try:
await member.send(f"Coucou **{member.name}** sur {member.guild.name} ! 🥰\n\nTu as le rôle **{role}** 💖!")
except:
pass
channel = client.get_channel(741639570172674120) # salons des arrivées
switch = [
f"Bienvenue, {member.mention}. On espère que tu as apporté de la pizza.",
f"C'est un plaisir de te voir, {member.mention}.",
f"{member.mention} vient juste d'arriver !",
f"{member.mention} vient juste d'atterrir.",
f"{member.mention} vient de se glisser dans le serveur.",
f"{member.mention} a bondi dans le serveur.",
f"Contents de te voir, {member.mention}.",
f"{member.mention} est arrivé(e).",
f"Tout le monde, accueillez comme il se doit {member.mention} !",
f"Youhou, tu as réussi, {member.mention} !",
f"{member.mention} a rejoint le groupe."
]
message = await channel.send("...") # évite d'envoyer une notification
await message.edit(content = choice(switch))
@client.event
async def on_member_remove(member):
if member.guild.id == 441208120644075520: # Confrérie du Kassoulait
channel = client.get_channel(741639570172674120) # salons des arrivées
await channel.send(f"{member.mention} vient de quitter le serveur.")
@client.event
async def on_raw_reaction_add(payload):
if payload.message_id == 644922358745792512: # Règles de la Confrérie du Kassoulait
if payload.emoji.name == '':
role = discord.utils.get(payload.member.guild.roles, name="règles-acceptés")
await payload.member.add_roles(role)
@client.event
async def on_raw_reaction_remove(payload):
if payload.message_id == 644922358745792512: # Règles de la Confrérie du Kassoulait
if payload.emoji.name == '':
guild = discord.utils.find(lambda g : g.id == payload.guild_id, client.guilds)
member = discord.utils.find(lambda m: m.id == payload.user_id, guild.members)
role = discord.utils.get(guild.roles, name="règles-acceptés")
await member.remove_roles(role)
@client.event
async def on_command_error(ctx, error):
if not ctx.invoked_with.startswith('.'):
if not ctx.invoked_with.startswith(customPrefix):
print(error)
if ctx.message:
await ctx.message.add_reaction(emoji = '')
@client.event
async def on_message(message):
await client.process_commands(message)
if message.author == client.user:
return
"""informations concernant le bot lorsqu'il est mentionner"""
if client.user.mention == message.content.replace("!",""):
ctx = await client.get_context(message)
prefix = await client.get_prefix(message)
await ctx.send(f">>> Coucou !\nMon préfix est `{prefix}` et ma commande d'aide est `{prefix}help`")
urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', message.content)
for i in range(len(urls)):
if urls[i].startswith("https://discordapp.com/channels/") or urls[i].startswith("https://discord.com/channels/") or urls[i].startswith("https://ptb.discordapp.com/"):
link = urls[i]
linkURL = link
if link.startswith("https://discord.com/channels/"):
link = f'000{link}'
if link.startswith("https://ptb.discordapp.com/"):
link = link[4:]
if "@me" in urls[i]:
return await message.channel.send("Je ne cite pas les messages privés.", delete_after = 5)
try:
if int(link[32:-38]) == message.guild.id:
msgID = await client.get_channel(int(link[51:-19])).fetch_message(int(link[70:]))
couleur = 0x2f3136
msgFiles = msgID.attachments
imageExtensions = ["jpg", "jpeg", "png", "webp", "gif"]
desc = msgID.content
if len(msgFiles) > 1:
listOfFiles = ""
for i in range(0, len(msgFiles)):
listOfFiles = f"{listOfFiles}, {msgFiles[i].filename}"
listOfFiles = listOfFiles[2:]
if len(msgID.content) > 0:
desc = f"{msgID.content}\n\nIl y a plusieurs fichiers dans ce message : {listOfFiles}"
else:
desc = f"Il y a plusieurs fichiers dans ce message : {listOfFiles}"
else:
if len(msgFiles) == 1:
if msgFiles[0].filename[-4:].split('.')[1] in imageExtensions:
if not len(msgID.content) > 0:
desc = f"Une image jointe : {msgFiles[0].filename}"
else:
linkFile = msgFiles[0].url
if not len(msgID.content) > 0:
desc = f"Un fichier joint : {msgFiles[0].filename}"
embed = discord.Embed(description = desc, colour = couleur)
auteur = "Auteur"
if message.author == msgID.author:
auteur = "Auteur & Citateur"
embed.add_field(name = auteur, value = msgID.author.mention, inline=True)
try:
if len(msgFiles) == 1:
if msgFiles[0].filename[-4:].split('.')[1] in imageExtensions:
embed.set_image(url=msgFiles[0].url)
else:
embed.add_field(name = "Fichier", value = f"[Lien]({linkFile})", inline=True)
except:
pass
embed.add_field(name = "Message", value = f"{msgID.channel.mention} - [Lien Message]({linkURL})", inline=True)
embed.set_author(name = "Citation", icon_url = msgID.author.avatar_url)
icon_url = message.author.avatar_url
date_1 = str(msgID.created_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').split()
edit = ""
if msgID.edited_at:
date_edit = str(msgID.edited_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').split()
edit = f"(Dernier edit : {date_edit[0][8:]}/{date_edit[0][5:-3]}/{date_edit[0][:4]} à {date_edit[1]})"
message_1 = f"Date : {date_1[0][8:]}/{date_1[0][5:-3]}/{date_1[0][:4]} à {date_1[1]} {edit}"
date_2 = str(message.created_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').split()
date_2 = f"{date_2[0][8:]}/{date_2[0][5:-3]}/{date_2[0][:4]} à {date_2[1]}"
cite = ""
if auteur == "Auteur":
cite = f"\nCité par {user_or_nick(message.author)} le {date_2}"
embed.set_footer(icon_url = icon_url, text = f"{message_1}{cite}")
if message.content == linkURL.replace(' ',''):
await message.channel.send(embed = embed)
await message.delete()
else:
await message.reply(embed = embed)
except Exception as e:
e = str(e)
if not "invalid literal for int() with base 10:" in e or not "404 Not Found (error code: 10008)" in e: # faute de frappe / message supprimé
print(e)
@client.event
async def on_message_delete(message):
if message.author.guild.id == 441208120644075520: # Confrérie du Kassoulait
prefix = await client.get_prefix(message)
if not (
message.content.startswith(f"{prefix}note") or
message.content.startswith(f"{prefix}memo") or
len(re.findall(".com/channels/", message.content)) != 0 or
client.user.id is message.author.id
):
user_suppressed = None
async for entry in message.guild.audit_logs(limit=1):
if (datetime.now() - entry.created_at).seconds < 5 and str(entry.action) == 'AuditLogAction.message_delete':
user_suppressed = entry.user
channel = client.get_channel(742588187456831659)
embed = discord.Embed(description = f"{message.content}")
embed.set_author(name = user_or_nick(message.author), icon_url = message.author.avatar_url)
if not user_suppressed:
embed.set_footer(text = f"Channel: #{message.channel.name} | Date : {str(message.created_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').replace(' ', ' à ')}\nSupprimé le {datetime.now(pytz.timezone('Europe/Paris')).strftime('%d/%m/%Y à %H:%M:%S')}")
else:
embed.set_footer(icon_url = user_suppressed.avatar_url, text = f"Channel: #{message.channel.name} | Date : {str(message.created_at.astimezone(timezone('Europe/Paris')))[:-13].replace('-', '/').replace(' ', ' à ')}\nSupprimé par {user_or_nick(user_suppressed)} le {datetime.now(pytz.timezone('Europe/Paris')).strftime('%d/%m/%Y à %H:%M:%S')}")
await channel.send(embed = embed)
# ne fonctionne pas quand un message a été supprimé avant que le bot ai démarré
# info sur la personne qui a supprimé ne fonctionne pas si il a supprimé un message auparavant (les logs se rajoute a un log deja existant)
def user_or_nick(user):
if user.nick:
return f"{user.nick} ({user.name}#{user.discriminator})"
else:
return f"{user.name}#{user.discriminator}"
client.run(os.environ['TOKEN_DISCORD'])
print("Connexion à Discord...", end = " ")
client.run(keys["TOKEN_DISCORD"])

185
src/utils/core.py Normal file
View file

@ -0,0 +1,185 @@
import json
import requests
from re import findall
from time import time
from os import environ, path
from dotenv import load_dotenv
def map_list_among_us(map):
"""Sélecteur de map pour la commande amongus"""
maps = {}
maps["skeld"] = ["skeld", "the skeld", "theskeld"]
maps["mira"] = ["mira", "mira hq", "mirahq"]
maps["polus"] = ["polus"]
maps["airship"] = ["airship", "air ship"]
if map == "all":
return maps["skeld"] + maps["mira"] + maps["polus"] + maps["airship"]
return maps[map]
def userOrNick(user):
"""Affiche le pseudo et/ou le surnom"""
if user == None:
return "Utilisateur inconnu" # Mauvais copié/collé -> changement d'ID
if user.nick:
return f"{user.nick} ({user.name}#{user.discriminator})"
else:
return f"{user.name}#{user.discriminator}"
def cleanUser(ctx, stringMessage, stringID):
"""récupère l'utilisateur avec son id"""
stringMessage = stringMessage.replace("<@!", "").replace(">", "").replace("<@", "") # améliorer ça avec du regex
if len(str(stringMessage)) not in (17, 18): # si ce n'est pas un ID valide
return stringMessage
associatedID = userOrNick(ctx.author.guild.get_member(int(stringID)))
try:
stringMessage = stringMessage.replace(stringID, associatedID)
except:
pass
return stringMessage
def cleanCodeStringWithMentionAndURLs(string):
"""formate un string avec des ` tout en gardant les mention et les liens"""
string = f"`{removeStartEndSpacesString(string)}`"
findedMention = getMentionInString(string)
for i in range(0, len(findedMention)):
string = string.replace(findedMention[i], f"`{findedMention[i]}`") # conserve la mention dans le message
if string.startswith("``<@"): # conserve le format quand mention au début de la string
string = string[2:]
if string.endswith(">``"): # conserve le format quand mention à la fin de la string
string = string[:-2]
string = string.replace("``", "") # conserve le format quand deux mentions sont collés
return string
def getMentionInString(string):
"""récupère les mentions dans un string"""
findedMention = []
for findingMention in findall(r'<@[!]?\d*>', string): # récupération mention dans le string
findedMention.append(findingMention)
findedMention = list(dict.fromkeys(findedMention)) # suppression doublon de mention dans la liste
return findedMention
def getURLsInString(string):
"""récupère les liens dans un string"""
findedURLs = []
for findingMention in findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', string): # récupération URLs dans le string
findedURLs.append(findingMention)
return findedURLs
def removeStartEndSpacesString(string):
"""Retire les espaces en trop au début et à la fin d'un string"""
while string.startswith(" "):
string = string[1:]
while string.endswith(" "):
string = string[:-1]
return string
def randomImage(link):
"""Récupération d'une image aléatoirement"""
temps_requete = int(round(time() * 1000))
try:
request_data = requests.get(link)
except Exception as e:
raise Exception(f"Une erreur s'est produite lors de la tentative de demande de l'API {link} : {e}")
if not request_data.status_code == 200:
raise Exception(f"Code HTTP {request_data.status_code} au lieu de HTTP 200 à l'appel de {link} : {request_data.text}")
try:
json_data = json.loads(request_data.text)
except Exception as e:
raise Exception(f"Erreur lors de la transformation les données de {link} en json : {e}")
temps_requete = int(round(time() * 1000)) - temps_requete
return (json_data, temps_requete)
def retirerDoublons(liste):
"""Supprime les doublons d'une liste"""
Newliste = []
for element in liste:
if element not in Newliste:
Newliste.append(element)
return Newliste
def ligneFormatage(ligne):
"""Traduit en français les balises dans les lyrics d'une chanson"""
liste_balise = [
('[Hook', '[Accroche'), ('[Verse', '[Couplet'), ('[Chorus', '[Chœur'),
('[Bridge', '[Pont'),('[Pre-Chorus', '[Pré-chœur'), ('[Post-Chorus', '[Post-chœur')
]
for balises in liste_balise:
ligne = ligne.replace(balises[0], balises[1])
return ligne
def mentionToUser(mention: str):
"""Récupère une mention et renvois son ID"""
return int(mention[2:-1].replace("!",""))
def getChangelogs(version = 'actual'):
"""Récupère les changements d'une version (par défaut, la dernière en date) et renvois dans l'ordre : url, n° version, changements"""
if version == 'actual':
version = getActualVersion()
changements = requests.get(f"https://gitlab.com/api/v4/projects/28424435/releases/v{version}")
if changements.status_code != 200: # si pas valide
return [changements.status_code]
else:
code = 200
changements = changements.json()
return [code, changements["_links"]["self"], changements["tag_name"][1:], changements["description"]]
def getActualVersion():
with open(path.join(path.dirname(path.dirname(path.dirname(__file__))), "README.md"), "r") as file:
return findall(r'https:\/\/img.shields.io\/badge\/version-(\d+\.\d+)-green\?style=for-the-badge\)', file.readlines()[2])[0]
def devOrStableChannel():
with open(path.join(path.dirname(path.dirname(path.dirname(__file__))), "README.md"), "r") as file:
return findall(r'https:\/\/img.shields.io\/gitlab\/pipeline\/ConfrerieDuKassoulait\/KassouBot\/([a-z]+)\?style=for-the-badge\)]', file.readlines()[3])[0]
def isSlash(arg):
"""Regarde si la commande viens d'un slash ou pas, retourne l'argument sans le 'True' si c'est le cas"""
fromSlash = False
fullarg = arg
if len(arg) > 0:
if arg[-1] == True or arg[-1] == None:
fromSlash = arg[-1]
fullarg = arg[:-1]
if len(fullarg) == 0:
arg = None
else:
arg = arg[0]
return (arg, fromSlash, fullarg)
async def mySendHidden(
ctx, fromSlash, message = None, tts = False, embed = None, file = None, files = None,
delete_after = None, allowed_mentions = None):
if fromSlash == True: # can't delete hidden message
await ctx.send( # sending hidden message
content = message, tts = tts, embed = embed, file = file, files = files,
delete_after = None, allowed_mentions = allowed_mentions, hidden = fromSlash)
else:
await ctx.send( # sending normal message
content = message, tts = tts, embed = embed, file = file, files = files,
delete_after = delete_after, allowed_mentions = allowed_mentions)
def load(variables):
"""Load env variables"""
keys = {}
load_dotenv() # load .env file
for var in variables:
try:
res = environ[var]
if var == "DEACTIVATE":
res = list(set(res.split(',')) - {""}) # create a list for the cogs we need to deactivate and remove blank channels and doubles
keys[var] = res
except KeyError:
if var == "DEACTIVATE":
keys[var] = "None"
else:
print(f"Please set the environment variable {var} (.env file supported)")
exit(1)
return keys

50
src/utils/db.py Normal file
View file

@ -0,0 +1,50 @@
import sqlite3
class Database:
def __init__(self, urlDatabase: str):
self.connexion = self.createConnection(urlDatabase)
def createConnection(self, path):
"""Connexion à une base de donnée SQLite"""
if not self.isFileExists(path):
open(path, "x")
connnexion = None
try:
connnexion = sqlite3.connect(path)
except sqlite3.Error as e:
print(e)
return connnexion
def isFileExists(self, path):
"""Vérifie qu'un fichier existe"""
try:
open(path, "r")
except FileNotFoundError:
return False
else:
return True
def requete(self, requete, valeurs = None):
"""Envois une requête vers la base de données"""
try:
curseur = self.connexion.cursor()
if valeurs:
if type(valeurs) not in [list, tuple]:
valeurs = [valeurs]
curseur.execute(requete, valeurs)
else:
curseur.execute(requete)
self.connexion.commit()
return (curseur, curseur.lastrowid)
except sqlite3.Error as e:
print(e)
def affichageResultat(self, curseur):
"""Affiche le résultat d'une requête"""
tableau = []
if curseur == None:
return tableau
lignes = curseur[0].fetchall()
for ligne in lignes:
tableau.append(ligne)
return tableau

161
src/utils/reminder.py Normal file
View file

@ -0,0 +1,161 @@
from utils.db import Database
from discord import Embed, Colour
from utils.time import nowUTC, intToDatetime, timedeltaToString, timestampScreen
from re import findall
class Reminder(Database):
def __init__(self):
super().__init__(r"db/bot.sqlite3")
def creationTable(self):
"""Créer la table qui stocker les reminders."""
requete = """
CREATE TABLE IF NOT EXISTS reminder (
id INTEGER PRIMARY KEY,
message_id INTEGER,
channel_id INTEGER,
extrarg_id INTEGER,
reminder_str TEXT,
creation_int INTEGER,
expiration_int INTEGER,
user_id INTEGER,
guild_id INTEGER
);
"""
self.requete(requete)
def ajoutReminder(self, messageID: int, channelID: int, extrarg: int, reminder: str, creation: int, expiration: int, userID: int, guildID: int):
"""Ajoute un reminder."""
requete = """
INSERT INTO reminder (
message_id, channel_id, extrarg_id, reminder_str, creation_int, expiration_int, user_id, guild_id
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?
);
"""
self.requete(requete, [messageID, channelID, extrarg, reminder, creation, expiration, userID, guildID])
return self.affichageResultat(self.requete("SELECT last_insert_rowid();"))
def suppressionReminder(self, id: int):
"""Supprime un reminder."""
requete = """
DELETE FROM reminder
WHERE id = ?
"""
self.requete(requete, id)
def listeReminder(self, userID: int, guildID: int):
"""Retourne la liste des reminders d'un utilisateur."""
requete = """
SELECT reminder_str, creation_int, expiration_int, id FROM reminder
WHERE user_id = ? AND (guild_id = ? OR guild_id = 0)
"""
return self.affichageResultat(self.requete(requete, [userID, guildID]))
def recuperationExpiration(self, time: int):
"""Récupère les reminders qui sont arrivés à expiration et ses infos."""
requete = """
SELECT channel_id, extrarg_id, reminder_str, creation_int, user_id, id, message_id FROM reminder
WHERE expiration_int < ?
"""
return self.affichageResultat(self.requete(requete, time))
def appartenanceReminder(self, userID: int, id: int, guildID: int):
"""Vérifie qu'un rappel appartiens à un utilisateur et que la guilde soit la bonne. Renvois False si le rappel n'existe pas."""
requete = """
SELECT EXISTS (
SELECT 1 FROM reminder
WHERE id = ? AND user_id = ? AND (guild_id = ? OR guild_id = 0)
)
"""
return True if self.affichageResultat(self.requete(requete, [id, userID, guildID]))[0][0] == 1 else False
async def embedListe(utilisateur, guildID, page, color = None, refresh_message = None):
"""Fais l'embed d'une page pour l'affichage de la liste des reminders d'un utilisateur."""
reminders = Reminder().listeReminder(utilisateur.id, guildID)
pageMAX = -(-len(reminders) // 5)
if refresh_message:
if len(reminders) > 0:
if pageMAX > 1 and refresh_message.reactions[0] != "⬅️":
for emoji in ["⬅️", "➡️"]:
await refresh_message.add_reaction(emoji)
else:
return (False, False, False)
if pageMAX > 1:
refresh = False
else:
refresh = True
if color == None:
color = Colour.random()
embed = Embed(description = f"**Rappel{'s' if len(reminders) > 1 else ''} de {utilisateur.mention}** • Page {page}/{pageMAX}", color = color)
embed.set_thumbnail(url = utilisateur.avatar_url_as(size = 64))
limit = 5 * page
if len(reminders) > 0 and page <= pageMAX:
curseur = limit - 4
for reminder in reminders[limit - 5:]:
if curseur <= limit:
texte = reminder[0]
if len(texte) > 1024:
texte = f"{texte[:1021]}..."
expiration = reminder[2] - int(nowUTC())
if expiration > 0:
expiration = f"Expire dans {timedeltaToString(expiration)}"
else:
expiration = f"A déjà expiré."
embed.add_field(value = texte, name = f"#{reminder[3]} • Fais le {timestampScreen(intToDatetime(reminder[1]))}\n{expiration}", inline = False)
curseur += 1
else:
embed.add_field(name = "\u200b", value = f"L'utilisateur n'a aucun rappel en attente ou page n°{page} vide !")
embed.set_footer(text = "Les rappels qui ont déjà expirés vont apparaître dans quelques instants.\nIls peuvent avoir jusqu'à 1 minute de retard maximum.")
return (embed, pageMAX, refresh)
async def listReaction(client, payload):
"""Gère le changement de page du reminderlist avec les réactions."""
if payload.emoji.name in ["⬅️", "🔄", "➡️"]:
if payload.event_type == "REACTION_ADD":
if payload.member.bot == True: # vérifie que c'est pas un bot qui a réagit
return False, False
channel = client.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
if message.author.id != client.user.id or len(message.embeds) == 0: # vérification message du bot + au moins 1 embed
return False, False
embed = message.embeds[0].to_dict()
if "description" in embed: # si c'est un embed avec une description
if len(findall(r"\*\*Rappels? de <@\!?\d+>\*\* • Page \d+\/\d+", embed["description"])) == 1: # si c'est le bon embed
infoDescription = findall(r"\*\*Rappels? de <@\!?(\d+)>\*\* • Page (\d+)\/(\d+)", embed["description"])[0]
utilisateur = client.get_user(int(infoDescription[0]))
page = int(infoDescription[1])
refresh_message = None
if payload.emoji.name == "⬅️":
if page > 1:
page -= 1
else:
if int(infoDescription[2]) > 1:
page = int(infoDescription[2])
else:
return False, False
if payload.emoji.name == "➡️":
if page + 1 <= int(infoDescription[2]):
page += 1
else:
if page > 1:
page = 1
else:
return False, False
if payload.emoji.name == "🔄":
refresh_message = message
embed, _, refresh = await embedListe(utilisateur, payload.guild_id, page, embed["color"], refresh_message)
if embed == False:
return False, False
if refresh == True:
await message.add_reaction("🔄")
else:
for reaction in message.reactions:
if str(reaction) == "🔄":
users = await reaction.users().flatten()
for user in users:
await message.remove_reaction("🔄", user)
return message, embed
return False, False

117
src/utils/time.py Normal file
View file

@ -0,0 +1,117 @@
from pytz import timezone
from datetime import datetime, timedelta
from re import findall, sub
from utils.core import load
myTimezone = load(["TIMEZONE"])["TIMEZONE"]
def stringTempsVersSecondes(time):
"""Convertis une durée rentrée par un utilisateur en string vers des secondes en int"""
conversionTemps = {
"31536000 ": ["y", "a"],
"604800": ["w"],
"86400": ["j", "d"],
"3600": ["h"],
"60": ["m"],
"1": ["s", ""]
}
valeursMultiplicateur = ""
for i in conversionTemps.values():
for j in i:
valeursMultiplicateur += f"{j}|"
match = findall(f'([0-9]+)({valeursMultiplicateur[:-1]})?', time)
if not match:
return "Veuillez entrer un temps valide."
remindertime = 0
for i in match:
for tempsEnSeconde, nomCommun in conversionTemps.items():
if i[1] in nomCommun:
remindertime += int(tempsEnSeconde) * int(i[0])
return remindertime
def nowCustom():
"""Heure de maintenant avec fuseau horaire local en float"""
return datetime.now(timezone(myTimezone)).timestamp()
def nowUTC():
"""Heure de maintenant en UTC en float"""
return datetime.utcnow().timestamp()
def UTCDatetimeToCustomDatetime(datetime):
"""Conversion d'une timestamp UTC en timestamp local en datetime"""
return timezone(myTimezone).fromutc(datetime)
def intToDatetime(intOrFloat):
"""Convertis un int ou float en Datetime"""
return datetime.fromtimestamp(intOrFloat)
def timestampScreen(timestamp):
"""Affichage d'une timestamp"""
date_edit = str(UTCDatetimeToCustomDatetime(timestamp)).replace('-', '/').split(' ')
date = date_edit[0]
heure = date_edit[1].split('+')[0]
return f"{date[8:]}/{date[5:-3]}/{date[:4]} à {heure.split('.')[0]}"
def timedeltaToString(time):
"""Différence entre une heure en seconde et maintenant"""
age = sub(r' days?, ', ':', str(timedelta(seconds = time))).split(':')
affichage = [1, 1, 1, 1]
if len(age) == 3:
affichage = [0, 1, 1, 1]
age.insert(0, None)
for i in range(1, len(affichage)):
if int(age[i]) == 0:
affichage[i] = 0
if affichage[0] == 1:
day = int(age[0]) # récupération du nombre de jour
year = day // 365 # ajout du nombre d'année
day -= year * 365 # suppression des années dans le nombre de jour
week = day // 7 # ajout du nombres de semaines
day -= week * 7 # suppression des semaines dans le nombre du jour
yearINT = year # besoin pour le message age[0] final
year = f"{year} an{'s' if year > 1 else ''}" if year > 0 else ""
weekINT = week # besoin pour le message age[0] final
week = f"{week} semaine{'s' if week > 1 else ''}" if week > 0 else ""
day = f"{day} jour{'s' if day > 1 else ''}" if day > 0 else ""
age[0] = f"{year}{', ' if yearINT > 0 else ''}{week}{', ' if weekINT > 0 else ''}{day}"
else:
age[0] = ""
age[1] = f"{age[1]}h" if affichage[1] == 1 else ''
age[2] = f"{age[2]}m" if affichage[2] == 1 else ''
age[3] = f"{age[3]}s" if affichage[3] == 1 else ''
while "" in age:
age.remove("")
return ', '.join(age).strip(' ')
def getAge(date):
"""Décompose la différence entre une date et maintenant avec les bons timezone"""
joursRestants = UTCDatetimeToCustomDatetime(intToDatetime(nowUTC())) - UTCDatetimeToCustomDatetime(date)
years = joursRestants.total_seconds() / (365.242 * 24 * 60 * 60)
months = (years - int(years)) * 12
days = (months - int(months)) * (365.242 / 12)
hours = (days - int(days)) * 24
minutes = (hours - int(hours)) * 60
seconds = (minutes - int(minutes)) * 60
return (int(years), int(months), int(days), int(hours), int(minutes), int(seconds))
def ageLayout(tuple):
"""Avec la méthode `getAge`, permet de mettre en forme un âge"""
time = {}
time[0], time[1], time[2], time[3], time[4], time[5] = "an", "mois", "jour", "heure", "minute", "seconde"
for i in range(len(tuple)):
if tuple[i] > 1 and i != 1:
time[i] = time[i] + "s"
message = ""
if tuple[5] > 0:
affichage = [5]
if tuple[4] > 0:
affichage = [4, 5]
for i in range(3, -1, -1):
if tuple[i] > 0:
affichage = [i, i + 1, i + 2]
for i in affichage:
message = message + f", {tuple[i]} {time[i]}"
return message[2:]