From ebf1d95ea0821cbc6a367d2c5845c81e43c798f7 Mon Sep 17 00:00:00 2001 From: Aeris One Date: Mon, 13 Mar 2023 12:38:23 +0100 Subject: [PATCH 001/148] :construction_worker: ci: add dependabot config --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..8b83fe78 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + commit-message: + prefix: ":arrow_up: deps:" + include: "scope" From 078c4ff5c0eb8e9b326a0c4d739d7ae7cfb89db8 Mon Sep 17 00:00:00 2001 From: Aeris One Date: Tue, 14 Mar 2023 11:01:05 +0100 Subject: [PATCH 002/148] =?UTF-8?q?=F0=9F=91=B7=20ci:=20add=20docs=20depen?= =?UTF-8?q?dencies=20to=20dependabot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8b83fe78..8decec50 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,14 +1,16 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - version: 2 updates: - - package-ecosystem: "pip" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: "pip" + directory: "/" schedule: interval: "weekly" commit-message: prefix: ":arrow_up: deps:" include: "scope" + - package-ecosystem: "pip" + directory: "/docs" + schedule: + interval: "weekly" + commit-message: + prefix: ":arrow_up: docs:" + include: "scope" From a33b892c690913c3a6ac179df6a5ce885fab3b8a Mon Sep 17 00:00:00 2001 From: Aeris One Date: Sun, 12 Feb 2023 15:32:23 +0100 Subject: [PATCH 003/148] :see_no_evil: gitignore: add PyLint configuration to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fdf82969..d9c971dd 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ docs/_templates docs/_build docs/build require.json +.pylintrc docs/plugins From 1a563fb7890102db1d6e8e9a1f3353f02793c860 Mon Sep 17 00:00:00 2001 From: Aeris One Date: Sun, 12 Feb 2023 15:33:19 +0100 Subject: [PATCH 004/148] :art: style: fix style issues raised by PyLint in quizz plugin --- plugins/quizz/QuiPyQuizz.py | 104 --------- plugins/quizz/quipyquizz.py | 230 ++++++++++++++++++ plugins/quizz/quizz.py | 449 +++++++++++++++++++++++++++--------- start.py | 3 +- 4 files changed, 574 insertions(+), 212 deletions(-) delete mode 100644 plugins/quizz/QuiPyQuizz.py create mode 100644 plugins/quizz/quipyquizz.py diff --git a/plugins/quizz/QuiPyQuizz.py b/plugins/quizz/QuiPyQuizz.py deleted file mode 100644 index 9840481c..00000000 --- a/plugins/quizz/QuiPyQuizz.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Ce programme est régi par la licence CeCILL soumise au droit français et -respectant les principes de diffusion des logiciels libres. Vous pouvez -utiliser, modifier et/ou redistribuer ce programme sous les conditions -de la licence CeCILL diffusée sur le site "http://www.cecill.info". -""" - -import requests -import json -import os - - -class QuiPyQuizz: - def __init__(self): - with open("plugins/quizz/data/quizz.json", "r", encoding="utf-8") as f: - self.data = json.load(f) - - @staticmethod - def request_questions(quizz): - params = {"quiz": str(quizz)} # Payload - r = requests.get( - url="https://quipoquiz.com/module/sed/quiz/fr/start_quiz.snc", params=params - ) - paulaod = json.loads(r.text) - return paulaod["questions"] - - @staticmethod - def request_answer(uid_variation, question_id, answer: str): - params = { - "quiz": uid_variation, # Payload - "answer": answer.lower(), - "question": question_id, - } - r = requests.get( - url="https://quipoquiz.com/module/sed/quiz/fr/answer_question.snc", - params=params, - ) - pauload = json.loads(r.text) - return pauload["answer"] - - @staticmethod - def request_stats(quizz): - params = {"quiz": str(quizz)} # Payload - r = requests.get( - url="https://quipoquiz.com/module/sed/quiz/fr/end_quiz.snc", params=params - ) - pauload = json.loads(r.text) - return pauload["result"]["statistics"] - - def get_name(self, quizz_id): - if quizz_id in self.data: - return self.data[quizz_id]["name"] - else: - return None - - def get_url(self, quizz_id): - if quizz_id in self.data: - return f"https://quipoquiz.com/quiz/{self.data[quizz_id]['url']}" - else: - return None - - def get_question(self, quizz_id, question_id): - if quizz_id in self.data and question_id in self.data[quizz_id]["questions"]: - return self.data[quizz_id]["questions"][question_id] - """ - Exemple: - { - "question": "

Les chytridiomycètes sont des champignons aquatiques ou semi-aquatiques.

\n", - "credit": "Wikipedia", - "image": "/sn_uploads/quizzes/13_wiki_Synchytrium_on_Erodium_cicutarium.jpg" - } - """ - else: - return None - - def get_questions(self, quizz_id): - if quizz_id in self.data: - return self.data[quizz_id]["questions"] - """ - Exemple: - { - "14180": { - "question": "

Les chytridiomycètes sont des champignons aquatiques ou semi-aquatiques.

\n", - "credit": "Wikipedia", - "image": "/sn_uploads/quizzes/13_wiki_Synchytrium_on_Erodium_cicutarium.jpg" - }, - ... - } - """ - else: - return None - - def get_answer(self, quizz_id, question_id): - if quizz_id in self.data and question_id in self.data[quizz_id]["answers"]: - return self.data[quizz_id]["answers"][question_id] - """ - Exemple: - { - "real_answer": true, - "explanation": "La réponse est VRAI.

Ce sont les seuls champignons à avoir encore des spores uniflagellées.

\n" - } - """ - else: - return None diff --git a/plugins/quizz/quipyquizz.py b/plugins/quizz/quipyquizz.py new file mode 100644 index 00000000..233d0be0 --- /dev/null +++ b/plugins/quizz/quipyquizz.py @@ -0,0 +1,230 @@ +""" +Ce programme est régi par la licence CeCILL soumise au droit français et +respectant les principes de diffusion des logiciels libres. Vous pouvez +utiliser, modifier et/ou redistribuer ce programme sous les conditions +de la licence CeCILL diffusée sur le site "http://www.cecill.info". +""" + +import json +import requests + + +class QuiPyQuizz: + """ + QuiPyQuizz + Utilise l'API de quipoquiz.com afin de créer un quizz interactif sur Discord + """ + def __init__(self): + with open("plugins/quizz/data/quizz.json", "r", encoding="utf-8") as file: + self.data = json.load(file) + + @staticmethod + def request_questions(quizz): + """ + Récupère les questions d'un quizz depuis l'API de quipoquiz.com + + :param quizz: ID du quizz + :type quizz: int + + :return: Dictionnaire contenant les questions et/ou les erreurs + :rtype: dict + + >>> QuiPyQuizz.request_questions(1) + { + "errcode": 0, + "errmesg": "", + "questions": [ + { + "image": "/sn_uploads/quizzes/a10e63da6494d4ee5c588cbf251ceb401465146801107.jpeg", + "question": "La phrase « j’ignore si j’ai un ecchymose ou un hématome » est juste.", + "credit": "Myk-haematoma. By MykReeve [CC BY-SA 3.0 or GFDL], via Wikimedia Commons", + "uid_variation": "25" + }, + ... + ] + } + """ + params = {"quiz": str(quizz)} # Payload + request = requests.get( + url="https://quipoquiz.com/module/sed/quiz/fr/start_quiz.snc", + params=params, + timeout=10 + ) + payload = json.loads(request.text) + return payload["questions"] + + @staticmethod + def request_answer(uid_variation, question_id, answer: str): + """ + Demande à l'API de quipoquiz.com si la réponse est correcte + + :param uid_variation: ID de la variation de la question + :type uid_variation: int + :param question_id: ID de la question + :type question_id: int + :param answer: Réponse à la question + :type answer: str + + :return: Dictionnaire contenant la réponse et/ou les erreurs + :rtype: dict + + >>> QuiPyQuizz.request_answer(25, 1, "vrai") + { + "answer": { + "correct": false, + "explanation": "La réponse est FAUX. " + }, + "errcode": 0, + "errmesg": "" + } + """ + params = { + "quiz": uid_variation, # Payload + "answer": answer.lower(), + "question": question_id, + } + request = requests.get( + url="https://quipoquiz.com/module/sed/quiz/fr/answer_question.snc", + params=params, + timeout=10 + ) + payload = json.loads(request.text) + return payload["answer"] + + @staticmethod + def request_stats(quizz): + """ + Récupère les statistiques d'un quizz depuis l'API de quipoquiz.com + + :param quizz: ID du quizz + :type quizz: int + + :return: Dictionnaire contenant les statistiques et/ou les erreurs + :rtype: dict + + >>> QuiPyQuizz.request_stats(1) + { + "result": { + "statistics": { + "new_avg": 73.6717463, + "nb_attempt": "208286" + }, + "correct": "0", + "total": "10" + }, + "errcode": 0, + "errmesg": "" + } + """ + params = {"quiz": str(quizz)} # Payload + request = requests.get( + url="https://quipoquiz.com/module/sed/quiz/fr/end_quiz.snc", + params=params, + timeout=10 + ) + payload = json.loads(request.text) + return payload["result"]["statistics"] + + def get_name(self, quizz_id): + """ + Récupère le nom d'un quizz à partir de son ID + + :param quizz_id: ID du quizz + :type quizz_id: int + + :return: Nom du quizz + :rtype: str + + >>> QuiPyQuizz.get_name(1) + "Quizz de test" + """ + if quizz_id in self.data: + return self.data[quizz_id]["name"] + return None + + def get_url(self, quizz_id): + """ + Récupère l'URL d'un quizz à partir de son ID + + :param quizz_id: ID du quizz + :type quizz_id: int + + :return: URL du quizz + :rtype: str + + >>> QuiPyQuizz.get_url(1) + "https://quipoquiz.com/quiz/quizz-de-test" + """ + if quizz_id in self.data: + return f"https://quipoquiz.com/quiz/{self.data[quizz_id]['url']}" + return None + + def get_question(self, quizz_id, question_id): + """ + Récupère une question d'un quizz à partir de leurs ID + + :param quizz_id: ID du quizz + :type quizz_id: int + :param question_id: ID de la question + :type question_id: int + + :return: Dictionnaire contenant les informations de la question + :rtype: dict + + >>> QuiPyQuizz.get_question(1, 1) + { + "question": "

Les chytridiomycètes sont-ils des champignons aquatiques ou semi-aquatiques ?

", + "credit": "Wikipedia", + "image": "/sn_uploads/quizzes/13_wiki_Synchytrium_on_Erodium_cicutarium.jpg" + } + + """ + if quizz_id in self.data and question_id in self.data[quizz_id]["questions"]: + return self.data[quizz_id]["questions"][question_id] + return None + + def get_questions(self, quizz_id): + """ + Récupère toutes les questions d'un quizz à partir de son ID + + :param quizz_id: ID du quizz + :type quizz_id: int + + :return: Dictionnaire contenant les informations de toutes les questions + :rtype: dict + + >>> QuiPyQuizz.get_questions(1) + { + "14180": { + "question": "

Les chytridiomycètes sont des champignons aquatiques ou semi-aquatiques.

", + "credit": "Wikipedia", + "image": "/sn_uploads/quizzes/13_wiki_Synchytrium_on_Erodium_cicutarium.jpg" + }, + ... + } + """ + if quizz_id in self.data: + return self.data[quizz_id]["questions"] + return None + + def get_answer(self, quizz_id, question_id): + """ + Récupère la réponse d'une question d'un quizz à partir de leurs ID + + :param quizz_id: ID du quizz + :type quizz_id: int + :param question_id: ID de la question + :type question_id: int + + :return: Dictionnaire contenant les informations de la réponse + :rtype: dict + + >>> QuiPyQuizz.get_answer(1, 1) + { + "real_answer": true, + "explanation": "La réponse est VRAI.

C'est le seul champignon à avoir des spores uniflagellées.

" + } + """ + if quizz_id in self.data and question_id in self.data[quizz_id]["answers"]: + return self.data[quizz_id]["answers"][question_id] + return None diff --git a/plugins/quizz/quizz.py b/plugins/quizz/quizz.py index c7d8960c..dd43fb65 100644 --- a/plugins/quizz/quizz.py +++ b/plugins/quizz/quizz.py @@ -11,12 +11,22 @@ import discord from discord.ext import commands, tasks + +# pylint: disable=import-error from utils import Gunibot, MyContext -from .QuiPyQuizz import QuiPyQuizz +from quipyquizz import QuiPyQuizz def clean_question(question: str): + """ + Retire les balises

et

de la question + + :param question: La question à nettoyer + :type question: str + :return: La question nettoyée + :rtype: str + """ junk_to_remove = ["

", "

"] for junk in junk_to_remove: question = question.replace(junk, "") @@ -24,12 +34,46 @@ def clean_question(question: str): def clean_answer(answer: str): + """ + Retire les balises

et

de la réponse et les remplace par des + sauts de ligne pris en charge par Discord + + :param answer: La réponse à nettoyer + :type answer: str + :return: La réponse nettoyée + :rtype: str + """ answer = answer.replace("

", "\n") answer = answer.replace("

", "") return answer +# pylint: disable=too-few-public-methods class REACTIONS: + """ + Classe contenant les réactions utilisées par le quizz + + :cvar ANSWER_TRUE: Réaction pour répondre vrai + :vartype ANSWER_TRUE: str + :cvar ANSWER_FALSE: Réaction pour répondre faux + :vartype ANSWER_FALSE: str + :cvar ANSWER_SEPARATOR: Réaction pour séparer les réponses + :vartype ANSWER_SEPARATOR: str + :cvar ANSWER_LEAVE: Réaction pour quitter le quizz + :vartype ANSWER_LEAVE: str + :cvar START_QUIZ: Réaction pour démarrer le quizz + :vartype START_QUIZ: str + :cvar STOP_QUIZ: Réaction pour arrêter le quizz + :vartype STOP_QUIZ: str + :cvar JOIN_QUIZ: Réaction pour rejoindre le quizz + :vartype JOIN_QUIZ: str + :cvar FORWARD_QUESTION: Réaction pour passer une question + :vartype FORWARD_QUESTION: str + :cvar PREVIOUS_QUESTION: Réaction pour revenir à la question précédente + :vartype PREVIOUS_QUESTION: str + :cvar NEXT_QUESTION: Réaction pour passer à la question suivante + :vartype NEXT_QUESTION: str + """ ANSWER_TRUE = "✅" ANSWER_FALSE = "❎" ANSWER_SEPARATOR = "⬛" @@ -56,13 +100,22 @@ class REACTIONS: def sort_dict(leaders: dict) -> list[tuple]: - """Sort a dict by each value, returning a list of (key, value) couples - Note that it will sort in lexicographical order - For mathematical way, change it to float""" + """ + Trie un dictionnaire par valeur, renvoie une liste de couples (clé, valeur) + Notez que cela triera dans l'ordre lexicographique + + :param leaders: Le dictionnaire à trier + :type leaders: dict + :return: La liste triée + :rtype: list[tuple] + """ return sorted(leaders.items(), key=lambda kv: (kv[1], kv[0])) class Quizz(commands.Cog): + """ + Plugin "Quizz" pour le bot Gipsy + """ def __init__(self, bot: Gunibot): self.bot = bot self.file = "quizz" @@ -73,37 +126,86 @@ def __init__(self, bot: Gunibot): self.quick_quizz_channels = [] self.quick_quizz_messages = [] # Same self.check_if_active.start() # Démarrage du check des quizz inactifs - self.QPQ = QuiPyQuizz() + self.quipyquizz = QuiPyQuizz() def update_timestamp(self, party_id): + """ + Met à jour le timestamp de la partie + + :param party_id: L'ID de la partie + :type party_id: str + + :return: None + """ self.parties[party_id]["timestamp"] = time.time() - # Vu que c'est gros et qu'il faut le foutre partout j'en ai fait une - # fonction + def ez_set_author(self, embed: discord.Embed, party_id): - # Récupère l'id du quizz + """ + Ajoute les informations dans le champ "author" de l'embed + + :param embed: L'embed à modifier + :type embed: discord.Embed + :param party_id: L'ID de la partie + :type party_id: str + + :return: L'embed modifié + :rtype: discord.Embed + """ quizz_id = self.parties[party_id]["quizz"]["id"] embed.set_author( - name=self.QPQ.get_name(quizz_id), - url=self.QPQ.get_url(quizz_id), - icon_url="https://scontent.fcdg1-1.fna.fbcdn.net/v/t1.6435-1/p148x148/48416529_2354370714793494_5893141918379933696_n.png?_nc_cat=110&ccb=1-3&_nc_sid=1eb0c7&_nc_ohc=AI2a2_Vn0c4AX9pPIK8&_nc_ht=scontent.fcdg1-1.fna&tp=30&oh=f8c88dae60c23d52fe81b8264031bf9f&oe=60D6AFB7", + name=self.quipyquizz.get_name(quizz_id), + url=self.quipyquizz.get_url(quizz_id), + icon_url="https://scontent.fcdg1-1.fna.fbcdn.net/v/t1.6435-1/p148x148" + "/48416529_2354370714793494_5893141918379933696_n.png?_nc_cat=110&ccb=1-3&_nc_sid=1eb0c7&_nc_ohc" + "=AI2a2_Vn0c4AX9pPIK8&_nc_ht=scontent.fcdg1-1.fna&tp=30&oh=f8c88dae60c23d52fe81b8264031bf9f&oe" + "=60D6AFB7", ) return embed def ez_players_list(self, party_id, waiting=False): + """ + Génère la liste des joueurs + + :param party_id: L'ID de la partie + :type party_id: str + :param waiting: Si le joueur est en attente de réponse + :type waiting: bool + + :return: La liste des joueurs + :rtype: list[str] + """ final_list = [] for player in self.parties[party_id]["players"]: + if waiting: + hourglass = " " + else: + hourglass = "" + final_list.append( - f"- <@!{player}>: {self.parties[party_id]['players'][player]['score']}/10{' ' if waiting else ''}" + f"- <@!{player}>: {self.parties[party_id]['players'][player]['score']}/10{hourglass}" ) return final_list # Used to generate question embed lmao def ez_question_embed(self, party_id, leaderboard=False, waiting=False): + """ + Génère l'embed de la question + + :param party_id: L'ID de la partie + :type party_id: str + :param leaderboard: Si le leaderboard doit être affiché + :type leaderboard: bool + :param waiting: Si le joueur est en attente de réponse + :type waiting: bool + + :return: L'embed de la question + :rtype: discord.Embed + """ curent_question_id = self.parties[party_id]["ids"][ self.parties[party_id]["quizz"]["current"] - ] # Choppe l'id de la question en cours - raw_question = self.QPQ.get_question( + ] # Récupère l'id de la question en cours + raw_question = self.quipyquizz.get_question( self.parties[party_id]["quizz"]["id"], curent_question_id ) # Récupère le paquet de la question @@ -114,7 +216,7 @@ def ez_question_embed(self, party_id, leaderboard=False, waiting=False): ) try: - # Rajoute une thumbnail si il y en a une + # Rajoute une thumbnail s'il y en a une embed.set_thumbnail(url=f"https://quipoquiz.com{raw_question['image']}") except KeyError: pass @@ -130,13 +232,22 @@ def ez_question_embed(self, party_id, leaderboard=False, waiting=False): return embed def ez_answer_embed(self, party_id): + """ + Génère l'embed de la réponse + + :param party_id: L'ID de la partie + :type party_id: str + + :return: L'embed de la réponse + :rtype: discord.Embed + """ curent_question_id = self.parties[party_id]["ids"][ self.parties[party_id]["quizz"]["current"] ] # Choppe l'id de la question en cours - raw_answer = self.QPQ.get_answer( + raw_answer = self.quipyquizz.get_answer( self.parties[party_id]["quizz"]["id"], curent_question_id ) # Choppe le paquet de la réponse - raw_question = self.QPQ.get_question( + raw_question = self.quipyquizz.get_question( self.parties[party_id]["quizz"]["id"], curent_question_id ) # Choppe le paquet de la question @@ -173,6 +284,15 @@ def ez_answer_embed(self, party_id): return embed def ez_summary_embed(self, party_id): + """ + Génère l'embed de fin de partie + + :param party_id: L'ID de la partie + :type party_id: str + + :return: L'embed de fin de partie + :rtype: discord.Embed + """ embed = discord.Embed(title="Quizz terminé !", color=discord.Colour.gold()) embed = self.ez_set_author(embed, party_id) embed.set_footer(text=party_id) @@ -193,7 +313,15 @@ def ez_summary_embed(self, party_id): return embed def has_everyone_answered(self, party_id): - """Test si tout le monde a répondu""" + """ + Vérifie si tout le monde a répondu + + :param party_id: L'ID de la partie + :type party_id: str + + :return: True si tout le monde a répondu, False sinon + :rtype: bool + """ verif = True for player_id in self.parties[party_id]["players"]: if self.parties[party_id]["players"][player_id]["answer"] is None: @@ -202,6 +330,16 @@ def has_everyone_answered(self, party_id): return verif async def send_question(self, player_id, party_id): + """ + Envoie la question a un joueur + + :param player_id: L'ID du joueur + :type player_id: int + :param party_id: L'ID de la partie + :type party_id: str + + :return: None + """ # Fetch player player: discord.User = await self.bot.fetch_user(player_id) embed = self.ez_question_embed(party_id) # Generate question embed @@ -217,12 +355,27 @@ async def send_question(self, player_id, party_id): self.quick_quizz_channels.append(msg.channel.id) async def send_party_question(self, party_id): - """Envoie les question a tout les joueurs""" + """ + Envoie la question a tout les joueurs d'une partie + + :param party_id: L'ID de la partie + :type party_id: str + + :return: None + """ for player_id in self.parties[party_id]["players"]: await self.send_question(player_id, party_id) self.update_timestamp(party_id) async def send_answer(self, party_id): + """ + Envoie la réponse a tout les joueurs d'une partie + + :param party_id: L'ID de la partie + :type party_id: str + + :return: None + """ for player_id in self.parties[party_id]["players"]: # Fetch player player: discord.User = await self.bot.fetch_user(player_id) @@ -231,7 +384,7 @@ async def send_answer(self, party_id): ) embed = self.ez_question_embed(party_id) # Generate question embed - raw_answer = self.QPQ.get_answer( + raw_answer = self.quipyquizz.get_answer( self.parties[party_id]["quizz"]["id"], self.parties[party_id]["ids"][ self.parties[party_id]["quizz"]["current"] @@ -252,6 +405,18 @@ async def send_answer(self, party_id): self.update_timestamp(party_id) async def update_main_embed(self, embed: discord.Embed, party_id, player_id): + """ + Met a jour l'embed principal de la partie + + :param embed: L'embed a mettre a jour + :type embed: discord.Embed + :param party_id: L'ID de la partie + :type party_id: str + :param player_id: L'ID du joueur + :type player_id: int + + :return: None + """ old_field = embed.fields[0] raw_players = old_field.value.split("\n") new_field_value = [] @@ -274,6 +439,18 @@ async def update_main_embed(self, embed: discord.Embed, party_id, player_id): return await msg.edit(embed=embed) async def player_leave_update(self, message: discord.Message, party_id, user): + """ + Met a jour l'embed principal de la partie quand un joueur quitte + + :param message: Le message a mettre a jour + :type message: discord.Message + :param party_id: L'ID de la partie + :type party_id: str + :param user: Le joueur qui quitte + :type user: discord.User + + :return: None + """ embed = message.embeds[0] # Récupère l'embed raw_players = embed.fields[0].value.split("\n") # Split tout les joueurs @@ -288,7 +465,7 @@ async def player_leave_update(self, message: discord.Message, party_id, user): embed.clear_fields() # Cleanup embed.add_field( - name="{} joueur{}".format(temp, "s" if temp > 1 else ""), value=players + name=f"{temp} joueur{'s' if temp > 1 else ''}", value=players ) # Refait la liste des joueurs self.parties[party_id]["players"].pop(user.id) # remove le joueur de la party embed.set_footer(text=party_id) @@ -297,6 +474,16 @@ async def player_leave_update(self, message: discord.Message, party_id, user): return await message.edit(embed=embed) # Nouvel embed async def update_player_choice(self, party_id, player_id): + """ + Met a jour l'embed principal de la partie quand un joueur a répondu + + :param party_id: L'ID de la partie + :type party_id: str + :param player_id: L'ID du joueur + :type player_id: int + + :return: None + """ channel: discord.TextChannel = await self.bot.fetch_channel( self.parties[party_id]["channel_id"] ) @@ -309,11 +496,9 @@ async def update_player_choice(self, party_id, player_id): new_value = "" for line in field_value: if str(player_id) in line: - new_value += "\n{}".format( - line.replace("", "✅") - ) + new_value += f"\n{line.replace('', '✅')}" else: - new_value += "\n{}".format(line) + new_value += f"\n{line}" embed.clear_fields() embed.add_field(name=field_name, value=new_value) await msg.edit(embed=embed) @@ -321,6 +506,11 @@ async def update_player_choice(self, party_id, player_id): @tasks.loop(minutes=5) async def check_if_active(self): + """ + Vérifie que les parties sont toujours actives + + :return: None + """ timestamp = round(time.time()) partys_to_pop = [] for party_id in self.parties: @@ -330,8 +520,10 @@ async def check_if_active(self): self.parties[party_id]["channel_id"] ) if channel is not None: + quiz_name = self.quipyquizz.get_name(self.parties[party_id]['quizz']['id']) await channel.send( - f"<@{self.parties[party_id]['author_id']}> ton quizz sur {self.QPQ.get_name(self.parties[party_id]['quizz']['id'])} s'est arrêté car inactif !" + f"<@{self.parties[party_id]['author_id']}> ton quizz sur {quiz_name} s'est arrêté car " + f"inactif !" ) partys_to_pop.append(party_id) else: @@ -340,23 +532,32 @@ async def check_if_active(self): for party_id in partys_to_pop: self.parties.pop(party_id) + # pylint: disable=too-many-locals, too-many-return-statements, too-many-branches, too-many-statements @commands.Cog.listener() - async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): - if pauload.emoji.name not in REACTIONS.all_reactions: - return # Si c'est pas les émojis du quizz alors on passe - elif pauload.user_id == self.bot.user.id: - return # Si c'est le bot sa dégage - elif pauload.channel_id not in self.quick_quizz_channels: - return # Permet d'éviter de faire une chiée de requêtes - elif pauload.message_id not in self.quick_quizz_messages: - return # Same - - channel: discord.DMChannel = await self.bot.fetch_channel(pauload.channel_id) - message: discord.Message = await channel.fetch_message(pauload.message_id) + async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): + """ + Gère les réactions sur les messages + + :param payload: Le payload de la réaction + :type payload: discord.RawReactionActionEvent + + :return: None + """ + if payload.emoji.name not in REACTIONS.all_reactions: + return # Si ce n'est pas les émojis du quizz alors on passe + if payload.user_id == self.bot.user.id: + return # Si c'est le bot qui a réagi alors on passe + if payload.channel_id not in self.quick_quizz_channels: + return # Si le channel n'est pas dans la liste des channels du quizz alors on passe + if payload.message_id not in self.quick_quizz_messages: + return # Idem + + channel: discord.DMChannel = await self.bot.fetch_channel(payload.channel_id) + message: discord.Message = await channel.fetch_message(payload.message_id) if len(message.embeds) == 0: - return # Vu que tout passe par embeds, si y'en a pas on passe + return # Vu que tout passe par embeds, s'il n'y en a pas on passe - if pauload.emoji.name == REACTIONS.PREVIOUS_QUESTION: + if payload.emoji.name == REACTIONS.PREVIOUS_QUESTION: if "/" in message.embeds[0].footer.text: raw_footer = message.embeds[0].footer.text.split("/") embed = message.embeds[0] @@ -366,40 +567,40 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): else: param = int(raw_footer[0]) - 1 - ids = [quizz_id for quizz_id in self.QPQ.data] - for n in range(15): + ids = list(self.quipyquizz.data) + for index in range(15): embed.add_field( - name=self.QPQ.data[ids[n + ((param - 1) * 15)]]["name"], - value=f"ID du quizz: `{ids[n + ((param-1) * 15)]}`", + name=self.quipyquizz.data[ids[index + ((param - 1) * 15)]]["name"], + value=f"ID du quizz: `{ids[index + ((param-1) * 15)]}`", ) - embed.set_footer(text=f"{param}/{len(self.QPQ.data) // 15}") + embed.set_footer(text=f"{param}/{len(self.quipyquizz.data) // 15}") await message.remove_reaction( - REACTIONS.PREVIOUS_QUESTION, pauload.member + REACTIONS.PREVIOUS_QUESTION, payload.member ) return await message.edit(embed=embed) - elif pauload.emoji.name == REACTIONS.NEXT_QUESTION: + elif payload.emoji.name == REACTIONS.NEXT_QUESTION: if "/" in message.embeds[0].footer.text: raw_footer = message.embeds[0].footer.text.split("/") embed = message.embeds[0] embed.clear_fields() - if raw_footer[0] == str(len(self.QPQ.data) // 15): - param = len(self.QPQ.data) // 15 + if raw_footer[0] == str(len(self.quipyquizz.data) // 15): + param = len(self.quipyquizz.data) // 15 else: param = int(raw_footer[0]) + 1 - ids = [quizz_id for quizz_id in self.QPQ.data] - for n in range(15): + ids = list(self.quipyquizz.data) + for index in range(15): embed.add_field( - name=self.QPQ.data[ids[n + ((param - 1) * 15)]]["name"], - value=f"ID du quizz: `{ids[n + ((param-1) * 15)]}`", + name=self.quipyquizz.data[ids[index + ((param - 1) * 15)]]["name"], + value=f"ID du quizz: `{ids[index + ((param-1) * 15)]}`", ) - embed.set_footer(text=f"{param}/{len(self.QPQ.data) // 15}") - await message.remove_reaction(REACTIONS.NEXT_QUESTION, pauload.member) + embed.set_footer(text=f"{param}/{len(self.quipyquizz.data) // 15}") + await message.remove_reaction(REACTIONS.NEXT_QUESTION, payload.member) return await message.edit(embed=embed) try: - # On vérifie que y'est bien l'id de la party dans le footer + # On vérifie qu'il y ait bien l'id de la party dans le footer party_id = int(message.embeds[0].footer.text) except ValueError: return # Sinon on passe @@ -409,11 +610,11 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): if party_id not in self.parties: return - if pauload.guild_id is not None: # Si la réaction est sur un serveur - # Si celui qui a réagis est le créateur du quizz - if pauload.user_id == self.parties[party_id]["author_id"]: + if payload.guild_id is not None: # Si la réaction est sur un serveur + # Si celui qui a réagi est le créateur du quizz + if payload.user_id == self.parties[party_id]["author_id"]: if ( - pauload.emoji.name == REACTIONS.START_QUIZ + payload.emoji.name == REACTIONS.START_QUIZ ): # 🆗 => Commencer le quizz # Génération de l'embed de question @@ -422,16 +623,13 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): message.embeds[0].fields[0].value.split("\n") ) # On récupère les joueurs - for n, player in enumerate(prev_players_markdown): + for index, player in enumerate(prev_players_markdown): # On rajoute le petit emote de sablier - prev_players_markdown[n] = ( + prev_players_markdown[index] = ( player + " " ) embed.add_field( - name="{} joueur{}".format( - len(prev_players_markdown), # Nombre de joueurs - "s" if len(prev_players_markdown) > 1 else "", - ), + name=f"{len(prev_players_markdown)} joueur{'s' if len(prev_players_markdown) > 1 else ''}", value="\n".join(prev_players_markdown), ) # Remise de la liste des joueurs await message.edit(embed=embed) # Edit de l'ancien embed @@ -439,19 +637,19 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): await message.clear_reaction(REACTIONS.START_QUIZ) await message.add_reaction(REACTIONS.FORWARD_QUESTION) self.parties[party_id]["started"] = True - # On envoit les questions en mp + # On envoie les questions en mp return await self.send_party_question(party_id) - elif ( - pauload.emoji.name == REACTIONS.STOP_QUIZ + if ( + payload.emoji.name == REACTIONS.STOP_QUIZ ): # ❌ => annulation du quizz embed = discord.Embed(title="Quizz annulé") self.parties.pop(party_id) # Supression dans le dict await message.clear_reactions() # Retire toute les réactions return await message.edit(embed=embed) # Feedback user - elif ( - pauload.emoji.name == REACTIONS.FORWARD_QUESTION + if ( + payload.emoji.name == REACTIONS.FORWARD_QUESTION ): # on skip cette question if not self.has_everyone_answered(party_id): return await channel.send( @@ -468,52 +666,50 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): for player in self.parties[party_id]["players"]: self.parties[party_id]["players"][player]["answer"] = None await message.remove_reaction( - REACTIONS.FORWARD_QUESTION, pauload.member + REACTIONS.FORWARD_QUESTION, payload.member ) else: await message.clear_reactions() await message.edit(embed=self.ez_summary_embed(party_id)) return self.parties.pop(party_id) else: - if pauload.emoji.name == REACTIONS.JOIN_QUIZ: # Un joueur join + if payload.emoji.name == REACTIONS.JOIN_QUIZ: # Un joueur join embed = message.embeds[0] # Rajoute le joueur dans l'embed players = ( - embed.fields[0].value + f"\n- <@!{pauload.user_id}> 0/10" + embed.fields[0].value + f"\n- <@!{payload.user_id}> 0/10" f'{" " if self.parties[party_id]["started"] else ""}' ) embed.clear_fields() # Cleanup temp = players.split("\n") embed.add_field( - name="{} joueur{}".format( - len(temp), "s" if len(temp) > 1 else "" - ), + name=f"{len(temp)} joueur{'s' if len(temp) > 1 else ''}", value=players, ) embed.set_footer(text=party_id) embed = self.ez_set_author(embed, party_id) - self.parties[party_id]["players"][int(pauload.user_id)] = { + self.parties[party_id]["players"][int(payload.user_id)] = { "score": 0, "answer": None, "msg_id": 0, } if self.parties[party_id]["started"]: - await self.send_question(pauload.user_id, party_id) + await self.send_question(payload.user_id, party_id) return await message.edit(embed=embed) # Nouvel embed else: # Si c'est en mp - if pauload.emoji.name == REACTIONS.ANSWER_FALSE: + if payload.emoji.name == REACTIONS.ANSWER_FALSE: # Choisi la réponse négative - self.parties[party_id]["players"][pauload.user_id]["answer"] = False - await self.update_player_choice(party_id, pauload.user_id) - elif pauload.emoji.name == REACTIONS.ANSWER_TRUE: + self.parties[party_id]["players"][payload.user_id]["answer"] = False + await self.update_player_choice(party_id, payload.user_id) + elif payload.emoji.name == REACTIONS.ANSWER_TRUE: # Choisi la réponse positive - self.parties[party_id]["players"][pauload.user_id]["answer"] = True - await self.update_player_choice(party_id, pauload.user_id) + self.parties[party_id]["players"][payload.user_id]["answer"] = True + await self.update_player_choice(party_id, payload.user_id) - if pauload.emoji.name in [REACTIONS.ANSWER_TRUE, REACTIONS.ANSWER_FALSE]: + if payload.emoji.name in [REACTIONS.ANSWER_TRUE, REACTIONS.ANSWER_FALSE]: if self.has_everyone_answered(party_id): main_channel: discord.TextChannel = await self.bot.fetch_channel( self.parties[party_id]["channel_id"] @@ -527,7 +723,7 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): return if ( - pauload.emoji.name == REACTIONS.ANSWER_LEAVE + payload.emoji.name == REACTIONS.ANSWER_LEAVE ): # Le joueur veut quitter le quizz main_channel: discord.TextChannel = await self.bot.fetch_channel( self.parties[party_id]["channel_id"] @@ -536,25 +732,31 @@ async def on_raw_reaction_add(self, pauload: discord.RawReactionActionEvent): self.parties[party_id]["msg_id"] ) # Faut optimiser ct'e merde - user = await self.bot.fetch_user(pauload.user_id) + user = await self.bot.fetch_user(payload.user_id) # Retire le joueur await self.player_leave_update(main_message, party_id, user) # Feedback user return await channel.send("Vous avez quitté le quizz") @commands.Cog.listener() - async def on_raw_reaction_remove(self, pauload: discord.RawReactionActionEvent): - if pauload.emoji.name != REACTIONS.JOIN_QUIZ: + async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent): + """ + Permet de gérer les réactions retirées + + :param payload: Le payload de la réaction + :type payload: discord.RawReactionActionEvent + """ + if payload.emoji.name != REACTIONS.JOIN_QUIZ: return # Si c'est pas les émojis du quizz alors on passe - elif pauload.user_id == self.bot.user.id: + if payload.user_id == self.bot.user.id: return # Si c'est le bot, alors on passe - elif pauload.channel_id not in self.quick_quizz_channels: + if payload.channel_id not in self.quick_quizz_channels: return # Permet d'éviter de faire une chiée de requêtes - elif pauload.message_id not in self.quick_quizz_messages: + if payload.message_id not in self.quick_quizz_messages: return # Same - channel: discord.DMChannel = await self.bot.fetch_channel(pauload.channel_id) - message: discord.Message = await channel.fetch_message(pauload.message_id) + channel: discord.DMChannel = await self.bot.fetch_channel(payload.channel_id) + message: discord.Message = await channel.fetch_message(payload.message_id) if len(message.embeds) == 0: return # Vu que tout passe par embeds, si y'en a pas on passe @@ -570,23 +772,31 @@ async def on_raw_reaction_remove(self, pauload: discord.RawReactionActionEvent): return if ( - pauload.guild_id is not None - and pauload.emoji.name == REACTIONS.JOIN_QUIZ + payload.guild_id is not None + and payload.emoji.name == REACTIONS.JOIN_QUIZ and not self.parties[party_id]["started"] ): # Si un joueur se barre # Récupère l'user - user: discord.User = await self.bot.fetch_user(pauload.user_id) + user: discord.User = await self.bot.fetch_user(payload.user_id) # Generate new player list return await self.player_leave_update(message, party_id, user) @commands.group(name="quizz") async def quizz_core(self, ctx: MyContext): + """ + Fonction principale du quizz, est appelée quand on fait la commande `quizz` + + :param ctx: Le contexte de la commande + :type ctx: MyContext + + :return: None + """ await ctx.message.delete() if ctx.invoked_subcommand is None: embed = discord.Embed(title="Quizz help", color=discord.Colour.orange()) - embed.add_field(name=f"`quizz`", value="Shows this message", inline=False) + embed.add_field(name="`quizz`", value="Shows this message", inline=False) embed.add_field( - name=f"`quizz start `", value="Démarre un quizz", inline=False + name="`quizz start `", value="Démarre un quizz", inline=False ) embed.add_field( name="`quizz themes`", @@ -597,12 +807,22 @@ async def quizz_core(self, ctx: MyContext): @quizz_core.command(name="start") async def _quizz_start(self, ctx: MyContext, quizz_id: str): + """ + Démarre un quizz + + :param ctx: Le contexte de la commande + :type ctx: MyContext + :param quizz_id: L'ID du quizz + :type quizz_id: str + + :return: None + """ party_id = "0" while party_id in self.parties: party_id = str(round(random.random() * 10000)) question_ids = [] - raw_question = self.QPQ.get_questions(quizz_id) + raw_question = self.quipyquizz.get_questions(quizz_id) if raw_question is None: return await ctx.send("L'ID du quizz est invalide.") for question_id in raw_question: @@ -622,7 +842,7 @@ async def _quizz_start(self, ctx: MyContext, quizz_id: str): embed = discord.Embed( title=f"Partie de {ctx.author.display_name}", - description=f"Sur le thème de :\n\t- **{self.QPQ.get_name(quizz_id)}**", + description=f"Sur le thème de :\n\t- **{self.quipyquizz.get_name(quizz_id)}**", ) embed.add_field(name="1 joueur", value=f"- {ctx.author.mention}: 0/10") embed.set_footer(text=party_id) @@ -637,13 +857,21 @@ async def _quizz_start(self, ctx: MyContext, quizz_id: str): @quizz_core.command(name="themes") async def _quizz_themes(self, ctx: MyContext): + """ + Donne la liste des thèmes + + :param ctx: Le contexte de la commande + :type ctx: MyContext + + :return: None + """ embed = discord.Embed(title="THEMES", color=discord.Colour.random()) - ids = [quizz_id for quizz_id in self.QPQ.data] - for n in range(15): + ids = list(self.quipyquizz.data) + for index in range(15): embed.add_field( - name=self.QPQ.data[ids[n]]["name"], value=f"ID du quizz: `{ids[n]}`" + name=self.quipyquizz.data[ids[index]]["name"], value=f"ID du quizz: `{ids[index]}`" ) - embed.set_footer(text=f"1/{len(self.QPQ.data)//15}") + embed.set_footer(text=f"1/{len(self.quipyquizz.data) // 15}") msg: discord.Message = await ctx.send(embed=embed) emojis = ["⬅️", "➡️"] for emoji in emojis: @@ -654,9 +882,16 @@ async def _quizz_themes(self, ctx: MyContext): config = {} async def setup(bot:Gunibot=None, plugin_config:dict=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + :param plugin_config: La configuration du plugin + :type plugin_config: dict + """ if bot is not None: await bot.add_cog(Quizz(bot), icon="❓") if plugin_config is not None: global config config.update(plugin_config) - diff --git a/start.py b/start.py index 6dcbd9c6..30051e35 100644 --- a/start.py +++ b/start.py @@ -8,7 +8,6 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import setup # do not remove this import, it also check the dependencies import os import asyncio @@ -18,6 +17,8 @@ from LRFutils import color from LRFutils import logs +import setup # do not remove this import, it also check the dependencies + if not os.path.isdir("plugins"): os.mkdir("plugins") From 36b87178cf3c0aae972325c3e810f821dbc9fb55 Mon Sep 17 00:00:00 2001 From: Aeris One Date: Sun, 12 Feb 2023 18:15:24 +0100 Subject: [PATCH 005/148] :art: style: fix style issues raised by PyLint in utils.py --- utils.py | 187 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 65 deletions(-) diff --git a/utils.py b/utils.py index a27ccf73..179657a6 100644 --- a/utils.py +++ b/utils.py @@ -5,8 +5,6 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import discord -from discord.ext import commands import logging import sqlite3 import json @@ -14,6 +12,11 @@ from typing import Any, Callable, Coroutine, Dict, Union, List, TYPE_CHECKING import os +import discord +from discord.ext import commands + +from core import config + if TYPE_CHECKING: from bot.utils.sconfig import Sconfig @@ -28,9 +31,8 @@ def bot_permissions(self) -> discord.Permissions: if self.guild: # message in a guild return self.channel.permissions_for(self.guild.me) - else: - # message in DM - return self.channel.permissions_for(self.bot) + # message in DM + return self.channel.permissions_for(self.bot) @property def user_permissions(self) -> discord.Permissions: @@ -44,7 +46,9 @@ def can_send_embed(self) -> bool: class Gunibot(commands.bot.AutoShardedBot): - """Bot class, with everything needed to run it""" + """ + Classe principale du bot + """ def __init__(self, case_insensitive=None, status=None, beta=False): # defining allowed default mentions @@ -68,8 +72,15 @@ def __init__(self, case_insensitive=None, status=None, beta=False): self.cog_icons = {} # icons for cogs self._update_database_structure() + # pylint: disable=arguments-differ async def get_context(self, message: discord.Message, *, cls=MyContext): - """Get a custom context class when creating one from a message""" + """ + Récupérer le contexte d'une commande + + :param message: Le message + :param cls: La classe du contexte + :return: Le contexte + """ # when you override this method, you pass your new Context # subclass to the super() method, which tells the bot to # use the new MyContext class @@ -77,34 +88,54 @@ async def get_context(self, message: discord.Message, *, cls=MyContext): @property def server_configs(self): - """Guilds configuration manager""" + """ + Récupérer la configuration du serveur + + :return: La configuration du serveur + """ return self.get_cog("ConfigCog").confManager @property def sconfig(self) -> "Sconfig": - """Return sconfig configuration manager""" + """ + Récupérer le gestionnaire de configuration du serveur + + :return: Le gestionnaire de configuration du serveur + """ return self.get_cog("Sconfig") def _update_database_structure(self): - """Create tables and indexes from 'data/model.sql' file""" - c = self.database.cursor() - with open("data/model.sql", "r", encoding="utf-8") as f: - c.executescript(f.read()) + """ + Mettre à jour la structure de la base de données + + :return: None + """ + cursor = self.database.cursor() + with open("data/model.sql", "r", encoding="utf-8") as file: + cursor.executescript(file.read()) + + # pylint: disable=redefined-outer-name for plugin in os.listdir("./plugins/"): if plugin[0] != "_": if os.path.isfile("./plugins/" + plugin + "/data/model.sql"): with open( "./plugins/" + plugin + "/data/model.sql", "r", encoding="utf-8" - ) as f: - c.executescript(f.read()) - c.close() + ) as file: + cursor.executescript(file.read()) + cursor.close() async def user_avatar_as( self, user: Union[discord.User, discord.Member], size=512, ): - """Get the avatar of an user, format gif or png (as webp isn't supported by some browsers)""" + """ + Récupérer l'avatar d'un utilisateur au format PNG ou GIF + + :param user: L'utilisateur + :param size: La taille de l'avatar + :return: L'avatar + """ avatar = user.display_avatar.with_size(size) # the avatar always exist, returns the URL to the default one if avatar.is_animated(): return avatar.with_format("gif") @@ -112,11 +143,20 @@ async def user_avatar_as( return avatar.with_format("png") class SafeDict(dict): + """ + ??? + """ def __missing__(self, key): return "{" + key + "}" + # pylint: disable=arguments-differ async def get_prefix(self, msg): - """Get a prefix from a message... what did you expect?""" + """ + Récupérer le préfixe du bot pour un message donné + + :param msg: Le message + :return: Le préfixe + """ prefix = None if msg.guild is not None: prefix = self.server_configs[msg.guild.id]["prefix"] @@ -133,17 +173,29 @@ def db_query( returnrowcount: bool = False, astuple: bool = False, ) -> Union[int, List[dict], dict]: - """Do any query to the bot database - If SELECT, it will return a list of results, or only the first result (if fetchone) - For any other query, it will return the affected row ID if returnrowscount, or the amount of affected rows (if returnrowscount)""" + """ + Faire une requête à la base de données du bot + + Si SELECT, retourne une liste de résultats, ou seulement le premier résultat (si fetchone) + Pour toute autre requête, retourne l'ID de la ligne affectée si returnrowscount, + ou le nombre de lignes affectées (si returnrowscount) + + :param query: La requête à faire + :param args: Les arguments de la requête + :param fetchone: Si la requête est un SELECT, retourne seulement le premier résultat + :param returnrowcount: Si la requête est un INSERT, UPDATE ou DELETE, retourne le nombre de lignes affectées + :param astuple: Si la requête est un SELECT, retourne les résultats sous forme de tuple + :return: Le résultat de la requête + """ + cursor = self.database.cursor() try: cursor.execute(query, args) if query.startswith("SELECT"): _type = tuple if astuple else dict if fetchone: - v = cursor.fetchone() - result = _type() if v is None else _type(v) + row = cursor.fetchone() + result = _type() if row is None else _type(row) else: result = list(map(_type, cursor.fetchall())) else: @@ -152,39 +204,38 @@ def db_query( result = cursor.rowcount else: result = cursor.lastrowid - except Exception as e: + except Exception as exception: cursor.close() - raise e + raise exception cursor.close() return result @property def _(self) -> Callable[[Any, str], Coroutine[Any, Any, str]]: - """Translate something""" + """ + Traduire un texte + + :return: La fonction de traduction + """ cog = self.get_cog("Languages") if cog is None: self.log.error("Unable to load Languages cog") return lambda *args, **kwargs: args[1] return cog.tr + # pylint: disable=arguments-differ async def add_cog(self, cog: commands.Cog, icon=None): - """Adds a "cog" to the bot. - A cog is a class that has its own event listeners and commands. + """ + Ajouter un cog au bot - Parameters - ----------- - cog: :class:`Cog` - The cog to register to the bot. + :param cog: Le cog à ajouter + :param icon: L'icône du cog - Raises - ------- - TypeError - The cog does not inherit from :class:`Cog`. + :return: None - CommandError - An error happened during loading. + :raises TypeError: Le cog n'hérite pas de commands.Cog + :raises CommandError: Une erreur est survenue lors du chargement """ - self.cog_icons.update({cog.qualified_name.lower(): icon}) await super().add_cog(cog) @@ -193,25 +244,27 @@ async def add_cog(self, cog: commands.Cog, icon=None): if hasattr(module, "on_anycog_load"): try: module.on_anycog_load(cog) + # pylint: disable=broad-exception-caught except BaseException: - self.log.warning(f"[add_cog]", exc_info=True) + self.log.warning("[add_cog]", exc_info=True) def get_cog_icon(self, cog_name): - """Get a cog icon""" + """ + Récupérer l'icône d'un cog + + :param cog_name: Le nom du cog + :return: L'icône du cog + """ return self.cog_icons.get(cog_name.lower()) async def remove_cog(self, cog: str): - """Removes a cog from the bot. - - All registered commands and event listeners that the - cog has registered will be removed as well. + """ + Supprimer un cog du bot - If no cog is found then this method has no effect. + Toutes les commandes et listeners enregistrés par le cog seront supprimés - Parameters - ----------- - name: :class:`str` - The name of the cog to remove. + :param cog: Le cog à supprimer + :return: None """ await super().remove_cog(cog) for module in self.cogs.values(): @@ -219,24 +272,30 @@ async def remove_cog(self, cog: str): if hasattr(module, "on_anycog_unload"): try: module.on_anycog_unload(cog) + # pylint: disable=broad-exception-caught except BaseException: - self.log.warning(f"[remove_cog]", exc_info=True) + self.log.warning("[remove_cog]", exc_info=True) class CheckException(commands.CommandError): - """Exception raised when a custom check failed, to send errors when needed""" - - def __init__(self, id, *args): - super().__init__(message=f"Custom check '{id}' failed", *args) - self.id = id + """ + Exception personnalisée pour les checks + """ + def __init__(self, check_id, *args): + super().__init__(message=f"Custom check '{check_id}' failed", *args) + self.id = check_id def setup_logger(): - """Create the logger module, used for logs""" + """ + Initialiser le logger + + :return: None + """ # on chope le premier logger log = logging.getLogger("runner") - # on défini un formatteur - format = logging.Formatter( + # on définit un formatteur + formatter = logging.Formatter( "%(asctime)s %(levelname)s: %(message)s", datefmt="[%d/%m/%Y %H:%M]" ) # ex du format : [08/11/2018 14:46] WARNING RSSCog fetch_rss_flux l.288 : @@ -247,15 +306,15 @@ def setup_logger(): file_handler = logging.FileHandler("logs/debug.log") # tous les logs de niveau DEBUG et supérieur sont evoyés dans le fichier file_handler.setLevel(logging.DEBUG) - file_handler.setFormatter(format) + file_handler.setFormatter(formatter) # log vers la console stream_handler = logging.StreamHandler(sys.stdout) # tous les logs de niveau INFO et supérieur sont evoyés dans le fichier stream_handler.setLevel(logging.INFO) - stream_handler.setFormatter(format) + stream_handler.setFormatter(formatter) - # supposons que tu veuille collecter les erreurs sur ton site d'analyse d'erreurs comme sentry + # supposons que nous voulions collecter les erreurs sur ton site d'analyse d'erreurs comme sentry # sentry_handler = x # sentry_handler.setLevel(logging.ERROR) # on veut voir que les erreurs et au delà, pas en dessous # sentry_handler.setFormatter(format) @@ -275,8 +334,6 @@ def setup_logger(): CONFIG_OPTIONS: Dict[str, Dict[str, Any]] = {} -from core import config - CONFIG_OPTIONS.update( { "prefix": { @@ -300,5 +357,5 @@ def setup_logger(): for plugin in os.listdir("./plugins/"): if plugin[0] != "_": if os.path.isfile("./plugins/" + plugin + "/config/options.json"): - with open("./plugins/" + plugin + "/config/options.json") as config: + with open("./plugins/" + plugin + "/config/options.json", "r", encoding="utf8") as config: CONFIG_OPTIONS.update(json.load(config)) From 5155c0d90bdd492e1cf5a7a5926699c2db8849e7 Mon Sep 17 00:00:00 2001 From: Aeris One Date: Sun, 12 Feb 2023 18:53:59 +0100 Subject: [PATCH 006/148] :ambulance: Hopefully fix import error in quizz plugin --- plugins/quizz/quizz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/quizz/quizz.py b/plugins/quizz/quizz.py index dd43fb65..6996f993 100644 --- a/plugins/quizz/quizz.py +++ b/plugins/quizz/quizz.py @@ -15,7 +15,7 @@ # pylint: disable=import-error from utils import Gunibot, MyContext -from quipyquizz import QuiPyQuizz +from .quipyquizz import QuiPyQuizz def clean_question(question: str): From c47cbef97501990899fcfedbfc441600bbb29db8 Mon Sep 17 00:00:00 2001 From: ascpial Date: Sun, 12 Feb 2023 20:45:41 +0100 Subject: [PATCH 007/148] =?UTF-8?q?=F0=9F=95=B5=20style:=20fix=20style=20i?= =?UTF-8?q?ssues=20in=20inviteTracker=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/inviteTracker/inviteTracker.py | 43 ++++++++++++++------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/plugins/inviteTracker/inviteTracker.py b/plugins/inviteTracker/inviteTracker.py index 7115bb83..9faef487 100644 --- a/plugins/inviteTracker/inviteTracker.py +++ b/plugins/inviteTracker/inviteTracker.py @@ -5,9 +5,9 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, List, Optional, Tuple, Union import discord -from discord.ext import tasks, commands +from discord.ext import commands from utils import Gunibot, MyContext from bot import checks @@ -144,15 +144,15 @@ def set_description(self, description: str) -> None: query = "UPDATE invites SET description=? WHERE id=?;" self.parent.db_query(query, (description, self.id)) - def __eq__(self, object: Union[int, str, "Invite", discord.Invite]) -> bool: - if isinstance(object, int): - return self.id == object - elif isinstance(object, str): - return self.code == object - elif isinstance(object, Invite): - return self.id == object.id - elif isinstance(object, discord.Invite): - return self.id == object.id + def __eq__(self, item: Union[int, str, "Invite", discord.Invite]) -> bool: + if isinstance(item, int): + return self.id == item + elif isinstance(item, str): + return self.code == item + elif isinstance(item, Invite): + return self.id == item.id + elif isinstance(item, discord.Invite): + return self.id == item.id class Invite(commands.Cog): @@ -319,7 +319,7 @@ def get_invite_by_code(self, code: str) -> Optional[DatabaseInvite]: else: return None - def get_invite_by_id(self, id: int) -> Optional[DatabaseInvite]: + def get_invite_by_id(self, invite_id: int) -> Optional[DatabaseInvite]: """Return a dict representing the discord invitation stored in database Attributes @@ -333,7 +333,7 @@ def get_invite_by_id(self, id: int) -> Optional[DatabaseInvite]: The representation of the database object """ query = "SELECT * FROM invites WHERE id = ?" - data = self.bot.db_query(query, (id,), fetchone=True, astuple=True) + data = self.bot.db_query(query, (invite_id,), fetchone=True, astuple=True) if data is not tuple(): return DatabaseInvite(data, self.bot) else: @@ -356,7 +356,7 @@ def get_invite_by_server( """ if isinstance(guild, discord.Guild): guild = guild.id - query = f"SELECT * FROM invites WHERE guild = ?;" + query = "SELECT * FROM invites WHERE guild = ?;" datas = self.bot.db_query(query, (guild,), astuple=True) return [DatabaseInvite(data, self.bot) for data in datas] @@ -392,11 +392,14 @@ async def get_invitation_string( uses=invite.uses, ) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + :param plugin_config: La configuration du plugin + :type plugin_config: dict + """ if bot is not None: await bot.add_cog(Invite(bot), icon="👋") - if plugin_config is not None: - global config - config.update(plugin_config) - From bb567bdeee8fb194185fbafa021b8a5e306b1659 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 17:43:16 +0000 Subject: [PATCH 008/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(antikikoo):=20linter?= =?UTF-8?q?=20warnings=20in=20antikikoo=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/antikikoo/antikikoo.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/plugins/antikikoo/antikikoo.py b/plugins/antikikoo/antikikoo.py index 1b452b2c..0093c3f4 100644 --- a/plugins/antikikoo/antikikoo.py +++ b/plugins/antikikoo/antikikoo.py @@ -5,14 +5,11 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -from utils import Gunibot, MyContext -from discord.ext import commands -from discord.channel import TextChannel import discord -from bot import checks -import sys +from discord.channel import TextChannel +from discord.ext import commands -sys.path.append("./bot") +from utils import Gunibot, MyContext WELCOME_MESSAGE = """(FR) Bienvenue sur {server} {user} ! Vous n'avez accès qu'au salon Lobby pour le moment. Pour débloquer l'accès au reste du Discord, lisez les instructions présentes dans le salon {channel} :wink: @@ -93,7 +90,7 @@ async def on_message(self, message: discord.Message): await message.channel.send(embed=emb) try: await message.delete() - except BaseException: + except (discord.Forbidden, discord.NotFound): self.bot.log.exception("Cannot delete the verification message") verif_role = message.guild.get_role(config["verification_role"]) if verif_role is None: @@ -103,7 +100,7 @@ async def on_message(self, message: discord.Message): await message.author.add_roles(verif_role) else: await message.author.remove_roles(verif_role) - except BaseException: + except (discord.Forbidden, discord.NotFound): self.bot.log.exception( f"Cannot give or take away verification role from member {message.author}" ) @@ -134,8 +131,6 @@ async def ak_channel(self, ctx: MyContext, channel: discord.TextChannel): async def ak_msg(self, ctx: MyContext, *, message: str = None): """Modifies the informative message sent in the verification channel Put nothing to reset it, or "None" for no message""" - if message.lower() == "none": - value = "None" # no message self.bot.server_configs[ctx.guild.id]["verification_info_message"] = message await ctx.send(await self.bot._(ctx.guild.id, "antikikoo.msg-edited")) @@ -222,10 +217,6 @@ async def verification_add_role( ) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(Antikikoo(bot), icon="⛔") - if plugin_config is not None: - global config - config.update(plugin_config) From 8acd07242f46b34e45847c04a32ba7ba1d12e772 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 17:49:29 +0000 Subject: [PATCH 009/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(admin):=20fix=20pyli?= =?UTF-8?q?nt=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/admin/admin.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/plugins/admin/admin.py b/plugins/admin/admin.py index 5677c5f1..ed398fe3 100644 --- a/plugins/admin/admin.py +++ b/plugins/admin/admin.py @@ -5,11 +5,6 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -from utils import Gunibot -from git import Repo, exc -from discord.ext import commands -import discord -from bot import checks import io import os import sys @@ -17,6 +12,13 @@ import traceback from contextlib import redirect_stdout +import discord +from discord.ext import commands +from git import Repo, GitCommandError + +from utils import Gunibot +from bot import checks + sys.path.append("./bot") @@ -38,6 +40,7 @@ def __init__(self, bot: Gunibot): @commands.check(checks.is_bot_admin) async def main_msg(self, ctx: commands.Context): """Commandes réservées aux administrateurs de GuniBot""" + self.main_msg: commands.Group if ctx.subcommand_passed is None: text = "Liste des commandes disponibles :" for cmd in sorted(self.main_msg.commands, key=lambda x: x.name): @@ -60,7 +63,7 @@ async def gitpull(self, ctx: commands.Context, branch: str = None): if branch: try: repo.git.checkout(branch) - except exc.GitCommandError as e: + except GitCommandError as e: self.bot.log.exception(e) if ( "Your local changes to the following files would be overwritten by checkout" @@ -150,7 +153,7 @@ async def reload_cog(self, ctx: commands.Context, *, cog: str): await ctx.send("Cog {} can't be found".format(cog)) except commands.errors.ExtensionNotLoaded: await ctx.send("Cog {} was never loaded".format(cog)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught await errors_cog.on_error(e, ctx) await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}") else: @@ -171,7 +174,7 @@ async def add_cog(self, ctx: commands.Context, name: str): self.bot.load_extension("plugins." + name) await ctx.send("Module '{}' ajouté !".format(name)) self.bot.log.info("Module {} ajouté".format(name)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught await ctx.send(str(e)) @main_msg.command(name="del_cog", aliases=["remove_cog"], hidden=True) @@ -181,7 +184,7 @@ async def rm_cog(self, ctx: commands.Context, name: str): self.bot.unload_extension("plugins." + name) await ctx.send("Module '{}' désactivé !".format(name)) self.bot.log.info("Module {} désactivé".format(name)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught await ctx.send(str(e)) @main_msg.command(name="cogs", hidden=True) @@ -236,19 +239,19 @@ async def _eval(self, ctx: commands.Context, *, body: str): stdout = io.StringIO() try: to_compile = f'async def func():\n{textwrap.indent(body, " ")}' - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught await self.bot.get_cog("Errors").on_error(e, ctx) return try: - exec(to_compile, env) - except Exception as e: + exec(to_compile, env) # pylint: disable=exec-used + except Exception as e: # pylint: disable=broad-exception-caught return await ctx.send(f"```py\n{e.__class__.__name__}: {e}\n```") func = env["func"] try: with redirect_stdout(stdout): ret = await func() - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught value = stdout.getvalue() await ctx.send(f"```py\n{value}{traceback.format_exc()}\n```") else: @@ -261,10 +264,6 @@ async def _eval(self, ctx: commands.Context, *, body: str): self._last_result = ret await ctx.send(f"```py\n{value}{ret}\n```") -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: - await bot.add_cog(Admin(bot), icon="🚨") - if plugin_config is not None: - global config - config.update(plugin_config) \ No newline at end of file + await bot.add_cog(Admin(bot), icon="🚨") \ No newline at end of file From 26f77a337e2e6686fc6f9a134714576b5e76038a Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 17:52:04 +0000 Subject: [PATCH 010/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(ban):=20remove=20pyl?= =?UTF-8?q?int=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/ban/ban.py | 49 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/plugins/ban/ban.py b/plugins/ban/ban.py index 01cecb3c..564acb38 100644 --- a/plugins/ban/ban.py +++ b/plugins/ban/ban.py @@ -5,16 +5,38 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ +from typing import Callable import importlib import random import discord.abc import discord from discord.ext import commands -from utils import Gunibot, MyContext +from utils import Gunibot, MyContext from core import config +async def ban_perm_check(ctx: commands.Context) -> bool: + """Checks if the user has the permission to ban""" + + self: Ban = ctx.bot.get_cog("Ban") + + if ctx.guild.id not in self.friendly_ban_guilds: + return await commands.has_guild_permissions(ban_members=True).predicate(ctx) + else: + for role in ctx.author.roles: + if role.id in self.friendly_ban_whitelisted_roles: + return True + + return await commands.has_guild_permissions(ban_members=True).predicate(ctx) + +async def fake_ban_guild_check(ctx: commands.Context) -> bool: + """Checks if the guild is configured for the friendly ban command""" + + self: Ban = ctx.bot.get_cog("Ban") + + return ctx.guild.id in self.friendly_ban_guilds + class Ban(commands.Cog): friendly_ban_guilds: list[int] @@ -54,27 +76,6 @@ async def on_member_join(self, member: discord.Member): + ", ".join([role.name for role in forbidden]))[:2000] ) - async def ban_perm_check(ctx: commands.Context) -> bool: - """Checks if the user has the permission to ban""" - - self: Ban = ctx.bot.get_cog("Ban") - - if ctx.guild.id not in self.friendly_ban_guilds: - return await commands.has_guild_permissions(ban_members=True).predicate(ctx) - else: - for role in ctx.author.roles: - if role.id in self.friendly_ban_whitelisted_roles: - return True - - return await commands.has_guild_permissions(ban_members=True).predicate(ctx) - - async def fake_ban_guild_check(ctx: commands.Context) -> bool: - """Checks if the guild is configured for the friendly ban command""" - - self: Ban = ctx.bot.get_cog("Ban") - - return ctx.guild.id in self.friendly_ban_guilds - # ------------------# # Commande /ban # # ------------------# @@ -229,8 +230,8 @@ def load_friendly_ban(self): "module_name": "just_a_message" } ] - self.systematic_events: list[function] = [] - self.random_events: list[function] = [] + self.systematic_events: list[Callable] = [] + self.random_events: list[Callable] = [] for event in self.friendly_ban_events: chances = event.get("chances", None) From 90257cb9929e897c93d5c1be0a95a169a60b8378 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 19:33:35 +0000 Subject: [PATCH 011/148] =?UTF-8?q?=F0=9F=9A=A8=20feat:=20add=20configurat?= =?UTF-8?q?ion=20for=20local=20modules=20fix(channelarchive):=20linter=20w?= =?UTF-8?q?arnings=20fix(quizz):=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - .pylintrc | 4 ++++ plugins/channelArchive/channelArchive.py | 17 ++++++++--------- plugins/quizz/quizz.py | 12 +++--------- 4 files changed, 15 insertions(+), 19 deletions(-) create mode 100644 .pylintrc diff --git a/.gitignore b/.gitignore index d9c971dd..fdf82969 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,4 @@ docs/_templates docs/_build docs/build require.json -.pylintrc docs/plugins diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..59962f39 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,4 @@ +[MASTER] + +# resolve import issues for local modules +init-hook='import sys; sys.path.append(".")' \ No newline at end of file diff --git a/plugins/channelArchive/channelArchive.py b/plugins/channelArchive/channelArchive.py index a019282b..2ca0632f 100644 --- a/plugins/channelArchive/channelArchive.py +++ b/plugins/channelArchive/channelArchive.py @@ -6,17 +6,20 @@ """ import typing + import discord from discord.ext import tasks, commands + from utils import Gunibot, MyContext -import bot.args as args +from bot import args class ChannelArchive(commands.Cog): + def __init__(self, bot: Gunibot): self.bot = bot self.config_options = ["archive_category", "archive_duration"] - self.update_loop.start() + self.update_loop.start() # pylint: disable=no-member bot.get_command("config").add_command(self.config_archive_category) bot.get_command("config").add_command(self.config_archive_duration) @@ -48,8 +51,8 @@ async def config_archive_duration( ) await ctx.send(x) - def cog_unload(self): - self.update_loop.cancel() + async def cog_unload(self): + self.update_loop.cancel() # pylint: disable=no-member async def add_to_archive(self, guild: discord.Guild, channel: discord.TextChannel): # Get archive category @@ -283,10 +286,6 @@ async def archive(self, ctx: MyContext, channel: discord.TextChannel = None): await ctx.send(embed=embed) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(ChannelArchive(bot), icon="🗃️") - if plugin_config is not None: - global config - config.update(plugin_config) diff --git a/plugins/quizz/quizz.py b/plugins/quizz/quizz.py index 6996f993..d7300dfb 100644 --- a/plugins/quizz/quizz.py +++ b/plugins/quizz/quizz.py @@ -12,12 +12,11 @@ import discord from discord.ext import commands, tasks -# pylint: disable=import-error from utils import Gunibot, MyContext +# pylint: disable=relative-beyond-top-level from .quipyquizz import QuiPyQuizz - def clean_question(question: str): """ Retire les balises

et

de la question @@ -125,6 +124,7 @@ def __init__(self, bot: Gunibot): # on_aw_reaction_add/remove self.quick_quizz_channels = [] self.quick_quizz_messages = [] # Same + # pylint: disable=no-member self.check_if_active.start() # Démarrage du check des quizz inactifs self.quipyquizz = QuiPyQuizz() @@ -880,18 +880,12 @@ async def _quizz_themes(self, ctx: MyContext): self.quick_quizz_channels.append(ctx.channel.id) return -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): """ Fonction d'initialisation du plugin :param bot: Le bot :type bot: Gunibot - :param plugin_config: La configuration du plugin - :type plugin_config: dict """ if bot is not None: await bot.add_cog(Quizz(bot), icon="❓") - if plugin_config is not None: - global config - config.update(plugin_config) From cb20ca4f2ef5e45cb1827e0036608253690bb8d2 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 19:38:32 +0000 Subject: [PATCH 012/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(contact):=20linter?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/contact/contact.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/plugins/contact/contact.py b/plugins/contact/contact.py index 6ed529c6..328a27df 100644 --- a/plugins/contact/contact.py +++ b/plugins/contact/contact.py @@ -5,19 +5,17 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import sqlite3 -from utils import Gunibot, MyContext -from discord.utils import snowflake_time -from discord.ext import commands -import discord -from bot import checks import typing +import sqlite3 from datetime import datetime, timedelta +import discord +from discord.utils import snowflake_time +from discord.ext import commands import aiohttp -import sys -# sys.path.append("./bot") +from bot import checks +from utils import Gunibot, MyContext class Contact(commands.Cog): @@ -217,10 +215,6 @@ async def ct_clear(self, ctx: commands.Context, days: int = 15): await ctx.send(answer) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(Contact(bot), icon="💬") - if plugin_config is not None: - global config - config.update(plugin_config) From 3d20456f565660fd0373a69722d8432d36b096de Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 19:44:57 +0000 Subject: [PATCH 013/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(generale):=20linter?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/general/general.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/plugins/general/general.py b/plugins/general/general.py index c4e4ef77..b0d165e4 100644 --- a/plugins/general/general.py +++ b/plugins/general/general.py @@ -34,7 +34,7 @@ async def count_lines_code(self): """Count the number of lines for the whole project""" count = 0 try: - for root, dirs, files in os.walk("."): + for root, dirs, files in os.walk("."): # pylint: disable=unused-variable if "/lib/python" in root: continue for file in files: @@ -43,8 +43,8 @@ async def count_lines_code(self): for line in f.read().split("\n"): if len(line.strip()) > 2 and line[0] != "#": count += 1 - except Exception as e: - await self.bot.get_cog("Errors").on_error(e, None) + except Exception as exception: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exception, None) self.codelines = count @commands.command(name="hs") @@ -156,21 +156,17 @@ async def stats(self, ctx: MyContext): ) d = d.replace(cpu_txt, cpu_ended) await msg.edit(content=d) - except Exception as e: - await ctx.bot.get_cog("Errors").on_command_error(ctx, e) + except Exception as exception: # pylint: disable=broad-exception-caught + await ctx.bot.get_cog("Errors").on_command_error(ctx, exception) @commands.command(name="halp", enabled=False) async def halp(self, ctx): - embed = discord.Embed(name="Help", colour=discord.Colour.green()) - embed.set_author(name=f"Gunibot commands") + embed = discord.Embed(title="Help", colour=discord.Colour.green()) + embed.set_author(name="Gunibot commands") embed.add_field(name="admin", value="Affiche les commandes admin disponibles") embed.add_field(name="admin", value="Affiche les commandes admin disponibles") await ctx.send(embed=embed) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: - await bot.add_cog(General(bot), icon="🌍") - if plugin_config is not None: - global config - config.update(plugin_config) + await bot.add_cog(General(bot), icon="🌍") \ No newline at end of file From 85aa4ba662517db25bcc2610a31dc1c8b392adfc Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 19:50:29 +0000 Subject: [PATCH 014/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(giveaways):=20linter?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/giveaways/giveaways.py | 45 ++++++++++++---------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/plugins/giveaways/giveaways.py b/plugins/giveaways/giveaways.py index 68c46cb6..e6f96b39 100644 --- a/plugins/giveaways/giveaways.py +++ b/plugins/giveaways/giveaways.py @@ -5,32 +5,25 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ +from typing import List, Optional, Union import datetime import random import time from marshal import dumps, loads -from typing import List, Optional, Union - -import sys - -sys.path.append("./bot") -import args -import sys -sys.path.append("./bot") -from bot import checks import discord -import emoji from discord.ext import commands, tasks +import emoji + from utils import Gunibot, MyContext -import re +from bot import checks, args class Giveaways(commands.Cog): def __init__(self, bot: Gunibot): self.bot = bot self.config_options = ["giveaways_emojis"] - self.internal_task.start() + self.internal_task.start() # pylint: disable=no-member bot.get_command("config").add_command(self.giveaways_emojis) @@ -255,7 +248,7 @@ async def start(self, ctx: MyContext, *, settings: str): channel = await commands.TextChannelConverter().convert( ctx, setting.replace("channel: ", "") ) - except: + except commands.BadArgument: await ctx.send( await self.bot._( ctx.guild.id, "giveaways.creation.invalid-channel" @@ -338,9 +331,9 @@ async def start(self, ctx: MyContext, *, settings: str): return if msg.channel.permissions_for(ctx.guild.me).add_reactions: try: - for emoji in allowed_emojis: + for allowed_emoji in allowed_emojis: try: - await msg.add_reaction(emoji) + await msg.add_reaction(allowed_emoji) except discord.NotFound: pass except discord.Forbidden: @@ -622,21 +615,17 @@ async def info(self, ctx: MyContext, *, giveaway: str): ) ) - def cog_unload(self): - self.internal_task.cancel() + async def cog_unload(self): + self.internal_task.cancel() # pylint: disable=no-member @tasks.loop(seconds=2.0) async def internal_task(self): for giveaway in self.db_get_expired_giveaways(): if giveaway["running"]: - try: - serv = self.bot.get_guild(giveaway["guild"]) - winners = await self.pick_winners(serv, giveaway) - await self.send_results(giveaway, winners) - self.db_stop_giveaway(giveaway["rowid"]) - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) - self.db_stop_giveaway(giveaway["rowid"]) + serv = self.bot.get_guild(giveaway["guild"]) + winners = await self.pick_winners(serv, giveaway) + await self.send_results(giveaway, winners) + self.db_stop_giveaway(giveaway["rowid"]) async def get_users( self, @@ -732,10 +721,6 @@ async def send_results(self, giveaway: dict, winners: List[discord.Member]): self.db_delete_giveaway(giveaway["rowid"]) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(Giveaways(bot), icon="🎁") - if plugin_config is not None: - global config - config.update(plugin_config) From d54d2dfcc0b5443cd0eb25d6ba0485052d77f63a Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 19:54:38 +0000 Subject: [PATCH 015/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(groups):=20litner=20?= =?UTF-8?q?warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/groups/groups.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/plugins/groups/groups.py b/plugins/groups/groups.py index 45427c72..35162b62 100644 --- a/plugins/groups/groups.py +++ b/plugins/groups/groups.py @@ -5,16 +5,14 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ +from typing import List from utils import Gunibot, MyContext -from discord.ext import commands + import discord -from bot import checks +from discord.ext import commands import asyncio -from typing import List - -import sys -sys.path.append("./bot") +from bot import checks class Group: @@ -27,6 +25,7 @@ def __init__( channelID: int, privacy: bool, ): + self.bot = bot self.roleID = roleID self.ownerID = ownerID self.channelID = channelID @@ -126,8 +125,8 @@ async def convert(self, ctx: MyContext, arg: str) -> Group: try: # try to convert it to a role role = await commands.RoleConverter().convert(ctx, arg) - except commands.BadArgument: - raise commands.BadArgument(f'Group "{arg}" not found.') + except commands.BadArgument as exception: + raise commands.BadArgument(f'Group "{arg}" not found.') from exception # make sure the cog is actually loaded, let's not break everything if cog := ctx.bot.get_cog("Groups"): if res := cog.db_get_group(ctx.guild.id, role.id): @@ -473,7 +472,7 @@ async def group_list(self, ctx: MyContext): ) return txt = "**" + await self.bot._(ctx.guild.id, "groups.list") + "**\n" - for group in groups: + for group in groups: # pylint: disable=not-an-iterable txt += group.to_str() + "\n\n" if ctx.can_send_embed: embed = discord.Embed(description=txt) @@ -736,10 +735,6 @@ async def group_channel_unregister(self, ctx: MyContext, group: GroupConverter): ) ) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(Groups(bot), icon="🎭") - if plugin_config is not None: - global config - config.update(plugin_config) From 1ca32894f83f05e0142b95de6ffcf010d57bc1ae Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:07:46 +0000 Subject: [PATCH 016/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(help):=20linter=20wa?= =?UTF-8?q?rnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/help/help.py | 88 +++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/plugins/help/help.py b/plugins/help/help.py index 062e6fc7..f52c1dd6 100644 --- a/plugins/help/help.py +++ b/plugins/help/help.py @@ -15,6 +15,7 @@ import discord from discord.ext import commands + from utils import Gunibot, MyContext @@ -43,13 +44,14 @@ async def decorator( return await self.send_error_message( await self.command_not_found(command.qualified_name) ) - + # the user can use the command return await callback(self, command) return decorator class Help(commands.HelpCommand): + """Everything you need to know about this bot.""" ANNOTATION_TRANSLATION = { discord.User: "annotation.user", discord.Member: "annotation.user", @@ -90,12 +92,6 @@ async def get_help_embed(self, *args, **kwargs) -> discord.Embed: # load the config options color = self.context.bot.server_configs[self.context.guild.id].get("help_embed_color", 0) - author = self.context.bot.server_configs[self.context.guild.id].get("help_author").format( - user=self.context.bot.user - ) - icon_url = self.context.bot.server_configs[self.context.guild.id].get("help_author_icon_url").format( - user=self.context.bot.user - ) embed = discord.Embed(*args, **kwargs, color=color) @@ -107,12 +103,12 @@ async def get_help_embed(self, *args, **kwargs) -> discord.Embed: return embed - async def get_bot_command_formating(self, commands_: List[commands.Command], size:int=None) -> str: + async def get_bot_command_formating(self, cmds: List[commands.Command], size:int=None) -> str: """Returns a string representing `commands_` Attributes ---------- - commands_: List[:class:`discord.ext.commands.Command`] + cmds: List[:class:`discord.ext.commands.Command`] The commands fro which to return the string representation Returns @@ -124,22 +120,26 @@ async def get_bot_command_formating(self, commands_: List[commands.Command], siz # Get max size of command name name_size = 0 - for commands in commands_: - name_size = max(name_size, len(commands.name)) - - for command in commands_: - output += await self.get_command_list_string(command, name_size=name_size, total_size=size) + for command in cmds: + name_size = max(name_size, len(command.name)) + + for command in cmds: + output += await self.get_command_list_string( + command, + name_size=name_size, + total_size=size + ) output += "\n" output += "```" return output - async def get_type_string(self, type: Any) -> Optional[str]: + async def get_type_string(self, target_type: Any) -> Optional[str]: """Returns the string representation of a type (like discord.Message, etc...) Attributes ---------- - type: Any + target_type: Any The type for which to return the string representation Returns @@ -148,9 +148,9 @@ async def get_type_string(self, type: Any) -> Optional[str]: The string representation If not translation is found, it return None """ - if type in self.ANNOTATION_TRANSLATION: + if target_type in self.ANNOTATION_TRANSLATION: return await self.context.bot._( - self.context, self.ANNOTATION_TRANSLATION[type] + self.context, self.ANNOTATION_TRANSLATION[target_type] ) async def get_annotation_type_string( @@ -196,7 +196,7 @@ async def get_parameter_string(self, parameter: inspect.Parameter) -> str: types.append( await self.context.bot._(self.context, "help.greedy", type=type_) ) - elif isinstance(annotation, typing._UnionGenericAlias): + elif isinstance(annotation, typing._UnionGenericAlias): # pylint: disable=protected-access for arg in annotation.__args__: type_ = await self.get_type_string(arg) if type_ is not None: @@ -229,14 +229,18 @@ async def get_parameters_string( result = "" for name, parameter in command.clean_params.items(): - type = await self.get_parameter_string(parameter) - if type is not None: - type = await bot._(self.context, "help.type", type=type) + parameter_string = await self.get_parameter_string(parameter) + if parameter_string is not None: + parameter_string = await bot._( + self.context, + "help.type", + type=parameter_string, + ) else: - type = "" + parameter_string = "" if ( - parameter.default and parameter.default != inspect._empty + parameter.default and parameter.default != inspect._empty # pylint: disable=protected-access ): # parse default if parameter.default in self.DEFAULT_TRANSLATION: default = await bot._( @@ -250,7 +254,7 @@ async def get_parameters_string( else: default = "" - result += f"**`{name}`**{type}{default}" + result += f"**`{name}`**{parameter_string}{default}" result += sep # add end separator return result if len(result) > 0 else None @@ -384,7 +388,7 @@ async def add_subcommands(self, group: Union[commands.Group, commands.Cog]): inline=False, ) - async def send_bot_help(self, mapping) -> None: + async def send_bot_help(self) -> None: # pylint: disable=arguments-differ """Send the help message for the bot in the context channel""" ctx = self.context bot: Gunibot = ctx.bot @@ -438,7 +442,7 @@ def get_category(command, *, no_category=no_category): await ctx.send(embeds=embeds[i*10: min((i+1)*10, len(embeds))]) @permission_check - async def send_command_help(self, command: commands.Command) -> None: + async def send_command_help(self, command: commands.Command) -> None: # pylint: disable=arguments-differ """Send the help message for command in the context channel Attributes @@ -475,7 +479,7 @@ async def send_command_help(self, command: commands.Command) -> None: await ctx.send(embed=self.embed) @permission_check - async def send_group_help(self, group: commands.Group) -> None: + async def send_group_help(self, group: commands.Group) -> None: # pylint: disable=arguments-differ """Send the help message for a group in the context channel Attributes @@ -514,7 +518,7 @@ async def send_group_help(self, group: commands.Group) -> None: await ctx.send(embed=self.embed) @permission_check - async def send_cog_help(self, cog: commands.Cog) -> None: + async def send_cog_help(self, cog: commands.Cog) -> None: # pylint: disable=arguments-differ """Send the help message for the cog in the context channel Attributes @@ -540,7 +544,7 @@ async def send_cog_help(self, cog: commands.Cog) -> None: await ctx.send(embed=self.embed) - async def command_not_found(self, command: str) -> str: + async def command_not_found(self, command: str) -> str: # pylint: disable=invalid-overridden-method, arguments-differ """Return the string for command not found error Attributes @@ -550,7 +554,7 @@ async def command_not_found(self, command: str) -> str: """ return await self.context.bot._(self.context, "help.not-found", command=command) - async def send_error_message(self, error: str) -> None: + async def send_error_message(self, error: str) -> None: # pylint: disable=arguments-differ """Raise the help error message in context channel Attributes @@ -559,7 +563,6 @@ async def send_error_message(self, error: str) -> None: The error to raise """ ctx = self.context - bot: Gunibot = ctx.bot self.embed = await self.get_help_embed(title=error) @@ -567,6 +570,8 @@ async def send_error_message(self, error: str) -> None: class HelpCog(commands.Cog): + """The Gipsy beautiful help command and configuration.""" + def __init__(self, bot: Gunibot): self.bot = bot self.file = "help" @@ -604,21 +609,30 @@ async def help_author(self, ctx: MyContext, *, text: str): @commands.command(name="help_author_icon_url") @commands.guild_only() async def help_author_icon_url(self, ctx: MyContext, url: str): + """Edit the help embed author icon url.""" await ctx.send( await self.bot.sconfig.edit_config( ctx.guild.id, "help_author_icon_url", url ) ) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: bot.help_command = Help() await bot.add_cog(HelpCog(bot), icon="🤝") - if plugin_config is not None: - global config - config.update(plugin_config) async def teardown(bot: Gunibot): + """ + Fonction de retrait du plugin + + :param bot: Le bot + :type bot: Gunibot + """ bot.help_command = commands.DefaultHelpCommand() await bot.remove_cog("HelpCog") From 6b2ebd60b82f03f6f9cdef39af4e73e025e53ec7 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:13:11 +0000 Subject: [PATCH 017/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(hypesquad):=20fix=20?= =?UTF-8?q?linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/hypesquad/hypesquad.py | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/plugins/hypesquad/hypesquad.py b/plugins/hypesquad/hypesquad.py index 81605e5f..a4fdad70 100644 --- a/plugins/hypesquad/hypesquad.py +++ b/plugins/hypesquad/hypesquad.py @@ -9,12 +9,15 @@ from typing import Dict import discord -from checks import is_roles_manager from discord.ext import commands, tasks + +from bot.checks import is_roles_manager from utils import Gunibot, MyContext class Hypesquad(commands.Cog): + """Give role to users based on their Hypesquad guild.""" + def __init__(self, bot: Gunibot): self.bot = bot self.config_options = [ @@ -23,15 +26,15 @@ def __init__(self, bot: Gunibot): "hs_balance_role", "hs_none_role", ] - self.roles_loop.start() + self.roles_loop.start() # pylint: disable=no-member bot.get_command("config").add_command(self.hs_main) - @commands.group(name="hypesquad", aliases=["hs"], enabled=False) + @commands.group(name="hypesquad") async def hs_main(self, ctx: MyContext): - """Manage options about Discord ypesquads""" + """Hypesquads-related commands""" if ctx.subcommand_passed is None: - await ctx.send_help("config hypesquad") + await ctx.send_help("hypesquad") @hs_main.command(name="role") async def hs_role(self, ctx: MyContext, house: str, *, role: discord.Role = None): @@ -67,21 +70,21 @@ async def hs_role(self, ctx: MyContext, house: str, *, role: discord.Role = None @tasks.loop(hours=12) async def roles_loop(self): """Check every 12h the members roles""" - t1 = time.time() + start = time.time() self.bot.log.debug("[hypesquad] Started roles check") count = 0 # count of edited members - for g in self.bot.guilds: + for guild in self.bot.guilds: try: - roles = await self.get_roles(g) + roles = await self.get_roles(guild) if any(roles.values()): # if at least a role is set - for member in g.members: + for member in guild.members: count += await self.edit_roles(member, roles) except discord.Forbidden: # missing a perm self.bot.log.warn( - f"[hypesquad] Unable to give roles in guild {g.id} ({g.name})" + f"[hypesquad] Unable to give roles in guild {guild.id} ({guild.name})" ) - delta = round(time.time() - t1, 2) + delta = round(time.time() - start, 2) self.bot.log.info( f"[hypesquad] Finished roles check in {delta}s with {count} editions" ) @@ -184,12 +187,6 @@ async def get_roles(self, guild: discord.Guild) -> Dict[str, discord.Role]: result[k] = guild.get_role(config[k]) return result - @commands.group(name="hypesquad") - async def hs_main(self, ctx: MyContext): - """Hypesquads-related commands""" - if ctx.subcommand_passed is None: - await ctx.send_help("hypesquad") - @hs_main.command(name="reload") @commands.check(is_roles_manager) async def hs_reload(self, ctx: MyContext, *, user: discord.Member): @@ -212,10 +209,12 @@ async def hs_reload(self, ctx: MyContext, *, user: discord.Member): await self.bot._(ctx.guild.id, "hypesquad.not-edited", user=user) ) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: await bot.add_cog(Hypesquad(bot), icon="⚜️") - if plugin_config is not None: - global config - config.update(plugin_config) From b1690bb3fde9a9609a308726a2bd4efd315f6830 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:15:29 +0000 Subject: [PATCH 018/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(invitetracker):=20mo?= =?UTF-8?q?re=20love=20to=20the=20invitetracker=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/inviteTracker/inviteTracker.py | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/inviteTracker/inviteTracker.py b/plugins/inviteTracker/inviteTracker.py index 9faef487..9a0ba3ae 100644 --- a/plugins/inviteTracker/inviteTracker.py +++ b/plugins/inviteTracker/inviteTracker.py @@ -18,13 +18,13 @@ class DatabaseInvite: guild: discord.Guild channel: discord.TextChannel user: int - id: int + invite_id: int code: int uses: int description: str def __init__(self, data: Tuple[Any], parent: Gunibot) -> None: - """Constrcut the classe with the database data + """Construct the classe with the database data Attributes ---------- @@ -37,7 +37,7 @@ def __init__(self, data: Tuple[Any], parent: Gunibot) -> None: self.guild = self.parent.get_guild(data[0]) self.channel = self.guild.get_channel(data[1]) self.user = data[2] - self.id = data[3] + self.invite_id = data[3] self.code = data[4] self.uses = data[5] self.description = data[6] @@ -51,8 +51,8 @@ async def check_use(self) -> bool: If the invitation was used since last update or not """ invites = await self.channel.invites() - if self.id in (invite.id for invite in invites): - invite = [invite for invite in invites if invite.id == self.id][0] + if self.invite_id in (invite.id for invite in invites): + invite = [invite for invite in invites if invite.id == self.invite_id][0] if invite.uses != self.uses: self.update(invite) return True @@ -63,7 +63,7 @@ async def check_use(self) -> bool: def delete(self) -> None: """Delete the invitation in the database""" query = "DELETE FROM invites WHERE id = ?" - self.parent.db_query(query, (self.id,)) + self.parent.db_query(query, (self.invite_id,)) def update(self, invite: discord.Invite) -> None: """Update the invite to match the given in database @@ -73,7 +73,7 @@ def update(self, invite: discord.Invite) -> None: invite: discord.Invite The invitation to update """ - if invite.id != self.id: + if invite.id != self.invite_id: raise ValueError("The invitation is not the current one") self.uses = invite.uses query = "UPDATE invites SET uses=? WHERE id=?;" @@ -81,7 +81,7 @@ def update(self, invite: discord.Invite) -> None: query, ( self.uses, - self.id, + self.invite_id, ), ) @@ -142,20 +142,22 @@ def set_description(self, description: str) -> None: The new description """ query = "UPDATE invites SET description=? WHERE id=?;" - self.parent.db_query(query, (description, self.id)) + self.parent.db_query(query, (description, self.invite_id)) def __eq__(self, item: Union[int, str, "Invite", discord.Invite]) -> bool: if isinstance(item, int): - return self.id == item + return self.invite_id == item elif isinstance(item, str): return self.code == item elif isinstance(item, Invite): - return self.id == item.id + return self.invite_id == item.id elif isinstance(item, discord.Invite): - return self.id == item.id + return self.invite_id == item.id class Invite(commands.Cog): + """Track who invited with which invitation.""" + def __init__(self, bot: Gunibot): self.bot = bot self.config_options = ["invite_log"] @@ -246,7 +248,7 @@ async def on_member_join(self, member: discord.Member) -> None: guild=member.guild, invitations=invites_string, ) - + channel = self.bot.server_configs[member.guild.id]["invite_log"] if channel is not None: channel = self.bot.get_channel(channel) @@ -359,7 +361,7 @@ def get_invite_by_server( query = "SELECT * FROM invites WHERE guild = ?;" datas = self.bot.db_query(query, (guild,), astuple=True) return [DatabaseInvite(data, self.bot) for data in datas] - + async def get_invitation_string( self, invite: DatabaseInvite, From ce230d57c8b8c0ac32652db2e24fcaa2dcc78004 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:30:41 +0000 Subject: [PATCH 019/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(logs):=20remove=20li?= =?UTF-8?q?nter=20warnings=20also=20a=20little=20bit=20of=20love=20to=20th?= =?UTF-8?q?e=20invitetracker=20plugin=20=F0=9F=A5=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/inviteTracker/inviteTracker.py | 2 - plugins/logs/logs.py | 261 ++++++++++++++++++------- 2 files changed, 187 insertions(+), 76 deletions(-) diff --git a/plugins/inviteTracker/inviteTracker.py b/plugins/inviteTracker/inviteTracker.py index 9a0ba3ae..f6f8093d 100644 --- a/plugins/inviteTracker/inviteTracker.py +++ b/plugins/inviteTracker/inviteTracker.py @@ -400,8 +400,6 @@ async def setup(bot:Gunibot=None): :param bot: Le bot :type bot: Gunibot - :param plugin_config: La configuration du plugin - :type plugin_config: dict """ if bot is not None: await bot.add_cog(Invite(bot), icon="👋") diff --git a/plugins/logs/logs.py b/plugins/logs/logs.py index 55bd2d9d..c261ad01 100644 --- a/plugins/logs/logs.py +++ b/plugins/logs/logs.py @@ -5,17 +5,19 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import datetime from typing import List +import datetime import discord from discord.ext import commands + from utils import Gunibot, MyContext from bot.utils.sconfig import SERVER_CONFIG -import args +from bot import args class Logs(commands.Cog): + """Monitor what is happening on the server.""" def __init__(self, bot: Gunibot): self.bot = bot @@ -26,6 +28,7 @@ def __init__(self, bot: Gunibot): @commands.command(name="modlogs_flags") async def config_modlogs_flags(self, ctx: MyContext): + """Edit logs configuration.""" await ctx.send(await self.bot._(ctx.guild.id, "sconfig.modlogs-help", p=ctx.prefix)) @commands.group(name="modlogs") @@ -41,13 +44,15 @@ async def modlogs_enable(self, ctx: MyContext, options: commands.Greedy[args.mod if not options: await ctx.send(await self.bot._(ctx.guild.id, "sconfig.invalid-modlogs")) return - LogsFlags = self.bot.get_cog('ConfigCog').LogsFlags() + logs_flags = self.bot.get_cog('ConfigCog').LogsFlags() flags = self.bot.server_configs[ctx.guild.id]['modlogs_flags'] - flags = LogsFlags.intToFlags(flags) + options + flags = logs_flags.intToFlags(flags) + options flags = list(set(flags)) # remove duplicates await SERVER_CONFIG.edit_config(ctx.guild.id, 'modlogs_flags', - LogsFlags.flagsToInt(flags)) - await ctx.send(await self.bot._(ctx.guild.id, "sconfig.modlogs-enabled", type=', '.join(options))) + logs_flags.flagsToInt(flags)) + await ctx.send( + await self.bot._(ctx.guild.id, "sconfig.modlogs-enabled", type=', '.join(options)) + ) @config_modlogs.command(name="disable") async def modlogs_disable(self, ctx: MyContext, options: commands.Greedy[args.moderatorFlag]): @@ -55,18 +60,26 @@ async def modlogs_disable(self, ctx: MyContext, options: commands.Greedy[args.mo if not options: await ctx.send(await self.bot._(ctx.guild.id, "sconfig.invalid-modlogs")) return - LogsFlags = self.bot.get_cog('ConfigCog').LogsFlags() + logs_flags = self.bot.get_cog('ConfigCog').LogsFlags() flags = self.bot.server_configs[ctx.guild.id]['modlogs_flags'] - flags = LogsFlags.intToFlags(flags) + flags = logs_flags.intToFlags(flags) flags = [x for x in flags if x not in options] - await SERVER_CONFIG.edit_config(ctx.guild.id, 'modlogs_flags', LogsFlags.flagsToInt(flags)) - await ctx.send(await self.bot._(ctx.guild.id, "sconfig.modlogs-disabled", type=', '.join(options))) + await SERVER_CONFIG.edit_config( + ctx.guild.id, + 'modlogs_flags', + logs_flags.flagsToInt(flags) + ) + await ctx.send( + await self.bot._(ctx.guild.id, "sconfig.modlogs-disabled", type=', '.join(options)) + ) @config_modlogs.command(name="list") async def modlogs_list(self, ctx: MyContext): """See available logs categories""" - f = self.bot.get_cog('ConfigCog').LogsFlags.FLAGS.values() - await ctx.send(await self.bot._(ctx.guild.id, "sconfig.modlogs-list", list=" - ".join(f))) + flags = self.bot.get_cog('ConfigCog').LogsFlags.FLAGS.values() + await ctx.send( + await self.bot._(ctx.guild.id, "sconfig.modlogs-list", list=" - ".join(flags)) + ) async def has_logs(self, guild) -> bool: """Check if a Guild has a valid logs channel""" @@ -77,8 +90,8 @@ async def has_logs(self, guild) -> bool: config["logs_channel"]) if not isinstance(logs_channel, discord.TextChannel): return False - p = logs_channel.permissions_for(guild.me) - return p.read_messages and p.send_messages and p.embed_links + permissions = logs_channel.permissions_for(guild.me) + return permissions.read_messages and permissions.send_messages and permissions.embed_links async def send_embed(self, guild, embed: discord.Embed): """Send the embed in a logs channel""" @@ -91,9 +104,10 @@ async def send_embed(self, guild, embed: discord.Embed): return await logs_channel.send(embed=embed) - def get_flags(self, guildID): - f = self.bot.get_cog('ConfigCog').LogsFlags().intToFlags - return f(self.bot.server_configs[guildID]['modlogs_flags']) + def get_flags(self, guild_id): + """Return the log flags for a the given `guild_id`.""" + flags = self.bot.get_cog('ConfigCog').LogsFlags().intToFlags + return flags(self.bot.server_configs[guild_id]['modlogs_flags']) @commands.Cog.listener() async def on_message_delete(self, message: discord.Message): @@ -105,12 +119,22 @@ async def on_message_delete(self, message: discord.Message): embed = discord.Embed( timestamp=message.created_at, title=await self.bot._(message.guild.id, "logs.msg_delete.title"), - description=await self.bot._(message.guild.id, "logs.msg_delete.desc", author=message.author.mention, channel=message.channel.mention), + description=await self.bot._( + message.guild.id, + "logs.msg_delete.desc", + author=message.author.mention, + channel=message.channel.mention, + ), colour=discord.Colour(13632027) ) embed.set_author(name=str(message.author), icon_url=message.author.display_avatar.url) - _footer = await self.bot._(message.guild.id, "logs.footer1", author=message.author.id, message=message.id) + _footer = await self.bot._( + message.guild.id, + "logs.footer1", + author=message.author.id, + message=message.id, + ) embed.set_footer(text=_footer) if len(message.content) > 1024: message.content = message.content[:1020] + '...' @@ -130,12 +154,23 @@ async def on_message_edit(self, before: discord.Message, after: discord.Message) embed = discord.Embed( timestamp=after.created_at, title=await self.bot._(before.guild.id, "logs.msg_edit.title"), - description=await self.bot._(before.guild.id, "logs.msg_edit.desc", url=before.jump_url, author=before.author.mention, channel=before.channel.mention), + description=await self.bot._( + before.guild.id, + "logs.msg_edit.desc", + url=before.jump_url, + author=before.author.mention, + channel=before.channel.mention, + ), colour=discord.Colour(16294684) ) embed.set_author(name=str(before.author), icon_url=before.author.display_avatar.url) - _footer = await self.bot._(before.guild.id, "logs.footer1", author=before.author.id, message=before.id) + _footer = await self.bot._( + before.guild.id, + "logs.footer1", + author=before.author.id, + message=before.id, + ) embed.set_footer(text=_footer) if len(before.content) > 1024: before.content = before.content[:1020] + '...' @@ -159,7 +194,12 @@ async def on_raw_bulk_message_delete(self, payload: discord.RawBulkMessageDelete embed = discord.Embed( timestamp=datetime.datetime.utcnow(), title=await self.bot._(payload.guild_id, "logs.bulk_delete.title"), - description=await self.bot._(payload.guild_id, "logs.bulk_delete.desc", count=count, channel=payload.channel_id), + description=await self.bot._( + payload.guild_id, + "logs.bulk_delete.desc", + count=count, + channel=payload.channel_id, + ), colour=discord.Colour.red() ) await self.send_embed(guild, embed) @@ -174,7 +214,11 @@ async def on_invite_create(self, invite: discord.Invite): embed = discord.Embed( timestamp=invite.created_at, title=await self.bot._(invite.guild.id, "logs.invite_created.title"), - description=await self.bot._(invite.guild.id, "logs.invite_created.desc", channel=invite.channel.mention), + description=await self.bot._( + invite.guild.id, + "logs.invite_created.desc", + channel=invite.channel.mention, + ), colour=discord.Colour.green() ) if invite.inviter: # sometimes Discord doesn't send that info @@ -208,7 +252,11 @@ async def on_invite_delete(self, invite: discord.Invite): return embed = discord.Embed( title=await self.bot._(invite.guild.id, "logs.invite_delete.title"), - description=await self.bot._(invite.guild.id, "logs.invite_delete.desc", channel=invite.channel.mention), + description=await self.bot._( + invite.guild.id, + "logs.invite_delete.desc", + channel=invite.channel.mention, + ), colour=discord.Colour.green() ) if invite.inviter: @@ -240,7 +288,11 @@ async def on_member_join_remove(self, member: discord.Member, join: bool): if join: embed = discord.Embed( title=await self.bot._(member.guild.id, "logs.member_join.title"), - description=await self.bot._(member.guild.id, "logs.member_join.desc", user=member.mention), + description=await self.bot._( + member.guild.id, + "logs.member_join.desc", + user=member.mention, + ), colour=discord.Colour.green() ) date = await self.bot.get_cog("TimeCog").date(member.created_at, year=True) @@ -249,10 +301,20 @@ async def on_member_join_remove(self, member: discord.Member, join: bool): else: embed = discord.Embed( title=await self.bot._(member.guild.id, "logs.member_left.title"), - description=await self.bot._(member.guild.id, "logs.member_left.desc", user=member.mention), + description=await self.bot._( + member.guild.id, + "logs.member_left.desc", + user=member.mention, + ), colour=discord.Colour(15994684) ) - delta = await self.bot.get_cog("TimeCog").time_delta(member.joined_at, datetime.datetime.utcnow(), lang="fr", year=True, precision=0) + delta = await self.bot.get_cog("TimeCog").time_delta( + member.joined_at, + datetime.datetime.utcnow(), + lang="fr", + year=True, + precision=0, + ) _date = await self.bot._(member.guild.id, "logs.member_left.date") embed.add_field(name=_date, value=delta) embed.set_author(name=str(member), icon_url=member.display_avatar.url) @@ -296,7 +358,12 @@ async def on_member_unban(self, guild: discord.Guild, user: discord.User): await self.send_embed(guild, embed) @commands.Cog.listener() - async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): + async def on_voice_state_update( + self, + member: discord.Member, + before: discord.VoiceState, + after: discord.VoiceState, + ): "https://discordpy.readthedocs.io/en/latest/api.html#discord.on_voice_state_update" if not await self.has_logs(member.guild): return @@ -305,21 +372,25 @@ async def on_voice_state_update(self, member: discord.Member, before: discord.Vo # member joined a channel if before.channel is None and after.channel is not None: _desc = "join" - kw = {'after': after.channel.name} + kw_args = {'after': after.channel.name} # member left a channel elif before.channel is not None and after.channel is None: _desc = "left" - kw = {'before': before.channel.name} + kw_args = {'before': before.channel.name} # member moved in another channel elif before.channel != after.channel: _desc = "move" - kw = {'before': before.channel.name, 'after': after.channel.name} + kw_args = {'before': before.channel.name, 'after': after.channel.name} else: return _title = await self.bot._(member.guild.id, "logs.voice_move.title") embed = discord.Embed( title=_title, - description=await self.bot._(member.guild.id, "logs.voice_move." + _desc, user=member.mention, **kw) + description=await self.bot._( + member.guild.id, + "logs.voice_move." + _desc, user=member.mention, + **kw_args, + ), ) embed.colour = discord.Color.light_gray() embed.set_author(name=str(member), icon_url=member.display_avatar.url) @@ -347,8 +418,13 @@ async def on_guild_update(self, before: discord.Guild, after: discord.Guild): description=await self.bot._(before.guild.id, "logs.boost.desc-lost") ) if before.premium_tier != after.premium_tier: - _desc = await self.bot._(before.guild.id, "logs.boost.change", before=before.premium_tier, after=after.premium_tier) - embed.description += "\n" + _desc + description = await self.bot._( + before.guild.id, + "logs.boost.change", + before=before.premium_tier, + after=after.premium_tier, + ) + embed.description += "\n" + description embed.color = discord.Color(0xf47fff) await self.send_embed(after, embed) @@ -361,7 +437,12 @@ async def on_guild_role_create(self, role: discord.Role): return embed = discord.Embed( title=await self.bot._(role.guild.id, "logs.role_created.title"), - description=await self.bot._(role.guild.id, "logs.role_created.desc", mention=role.mention, name=role.name) + description=await self.bot._( + role.guild.id, + "logs.role_created.desc", + mention=role.mention, + name=role.name, + ) ) _no = await self.bot._(role.guild.id, "logs._no") _yes = await self.bot._(role.guild.id, "logs._yes") @@ -370,7 +451,10 @@ async def on_guild_role_create(self, role: discord.Role): data = [_pos + f' {role.position}', _ment + ' ' + (_yes if role.mentionable else _no)] if role.color != discord.Color.default(): - data.append(await self.bot._(role.guild.id, "logs.role_created.color") + f' {role.color}') + data.append(await self.bot._( + role.guild.id, + "logs.role_created.color", + ) + f' {role.color}') if role.hoist: data.append(await self.bot._(role.guild.id, "logs.role_created.hoisted")) if role.permissions.administrator: @@ -398,7 +482,10 @@ async def on_guild_role_delete(self, role: discord.Role): data = [_pos + f' {role.position}', _ment + ' ' + (_yes if role.mentionable else _no)] if role.color != discord.Color.default(): - data.append(await self.bot._(role.guild.id, "logs.role_created.color") + f' {role.color}') + data.append(await self.bot._( + role.guild.id, + "logs.role_created.color", + ) + f' {role.color}') if role.hoist: data.append(await self.bot._(role.guild.id, "logs.role_created.hoisted")) if role.permissions.administrator: @@ -420,7 +507,12 @@ async def on_guild_role_update(self, before: discord.Role, after: discord.Role): return embed = discord.Embed( title=await self.bot._(after.guild.id, "logs.role_updated.title"), - description=await self.bot._(after.guild.id, "logs.role_updated.desc", mention=after.mention, name=after.name) + description=await self.bot._( + after.guild.id, + "logs.role_updated.desc", + mention=after.mention, + name=after.name, + ) ) _no = await self.bot._(after.guild.id, "logs._no") _yes = await self.bot._(after.guild.id, "logs._yes") @@ -432,20 +524,20 @@ async def on_guild_role_update(self, before: discord.Role, after: discord.Role): _name = await self.bot._(after.guild.id, "logs.role_updated.name") data.append(_name + f" {before.name} -> {after.name}") if before.permissions.administrator != after.permissions.administrator: - b1 = _yes if before.permissions.administrator else _no - b2 = _yes if after.permissions.administrator else _no + state_1 = _yes if before.permissions.administrator else _no + state_2 = _yes if after.permissions.administrator else _no _admin = await self.bot._(after.guild.id, "logs.role_created.admin") - data.append(_admin + f": {b1} -> {b2}") + data.append(_admin + f": {state_1} -> {state_2}") if before.mentionable != after.mentionable: - b1 = _yes if before.mentionable else _no - b2 = _yes if after.mentionable else _no + state_1 = _yes if before.mentionable else _no + state_2 = _yes if after.mentionable else _no _ment = await self.bot._(after.guild.id, "logs.role_created.mentionnable") - data.append(_ment + f": {b1} -> {b2}") + data.append(_ment + f": {state_1} -> {state_2}") if before.hoist != after.hoist: - b1 = _yes if before.hoist else _no - b2 = _yes if after.hoist else _no + state_1 = _yes if before.hoist else _no + state_2 = _yes if after.hoist else _no _hoist = await self.bot._(after.guild.id, "logs.role_created.hoisted") - data.append(_hoist + f": {b1} -> {b2}") + data.append(_hoist + f": {state_1} -> {state_2}") if len(data) == 0: return _changes = await self.bot._(after.guild.id, "logs.role_updated.changes") @@ -469,10 +561,18 @@ async def on_member_update(self, before: discord.Member, after: discord.Member): got = [r.mention for r in after.roles if r not in before.roles] lost = [r.mention for r in before.roles if r not in after.roles] if got: - _added = await self.bot._(after.guild.id, "logs.member_update.roles_added", count=len(got)) + _added = await self.bot._( + after.guild.id, + "logs.member_update.roles_added", + count=len(got), + ) embed.add_field(name=_added, value=" ".join(got), inline=False) if lost: - _removed = await self.bot._(after.guild.id, "logs.member_update.roles_removed", count=len(lost)) + _removed = await self.bot._( + after.guild.id, + "logs.member_update.roles_removed", + count=len(lost), + ) embed.add_field( name=_removed, value=" ".join(lost), inline=False) if len(embed.fields) == 0: @@ -484,7 +584,12 @@ async def on_member_update(self, before: discord.Member, after: discord.Member): await self.send_embed(before.guild, embed) @commands.Cog.listener() - async def on_guild_emojis_update(self, guild: discord.Guild, before: List[discord.Emoji], after: List[discord.Emoji]): + async def on_guild_emojis_update( + self, + guild: discord.Guild, + before: List[discord.Emoji], + after: List[discord.Emoji], + ): """https://discordpy.readthedocs.io/en/latest/api.html#discord.on_guild_emojis_update""" if not await self.has_logs(guild): return @@ -494,39 +599,47 @@ async def on_guild_emojis_update(self, guild: discord.Guild, before: List[discor color=discord.Color(0xf8d71c)) new = [str(e) for e in after if e not in before] lost = [str(e) for e in before if e not in after] - renamed = list() - restrict = list() - for b in before: - for a in after: - if a.id == b.id: - if a.name != b.name: - renamed.append(f'{a} :{b.name}: -> :{a.name}:') - if a.roles != b.roles: - a_roles = ' '.join([x.mention for x in a.roles]) - b_roles = ' '.join([x.mention for x in b.roles]) - restrict.append(f'{a} {b_roles} -> {a_roles}') + renamed = [] + restrict = [] + for emoji_before in before: + for emoji_after in after: + if emoji_after.id == emoji_before.id: + if emoji_after.name != emoji_before.name: + renamed.append( + f'{emoji_after} :{emoji_before.name}: -> :{emoji_after.name}:' + ) + if emoji_after.roles != emoji_before.roles: + a_roles = ' '.join([x.mention for x in emoji_after.roles]) + b_roles = ' '.join([x.mention for x in emoji_before.roles]) + restrict.append(f'{emoji_after} {b_roles} -> {a_roles}') if not (new or lost or renamed): # can happen when Discord fetch emojis from Twitch without any # change return if new: - n = await self.bot._(guild.id, "logs.emoji_update.added", count=len(new)) - embed.add_field(name=n, value="".join(new), inline=False) + field_name = await self.bot._(guild.id, "logs.emoji_update.added", count=len(new)) + embed.add_field(name=field_name, value="".join(new), inline=False) if lost: - n = await self.bot._(guild.id, "logs.emoji_update.removed", count=len(lost)) - embed.add_field(name=n, value="".join(lost), inline=False) + field_name = await self.bot._(guild.id, "logs.emoji_update.removed", count=len(lost)) + embed.add_field(name=field_name, value="".join(lost), inline=False) if renamed: - n = await self.bot._(guild.id, "logs.emoji_update.renamed", count=len(renamed)) - embed.add_field(name=n, value="\n".join(renamed), inline=False) + field_name = await self.bot._(guild.id, "logs.emoji_update.renamed", count=len(renamed)) + embed.add_field(name=field_name, value="\n".join(renamed), inline=False) if restrict: - n = await self.bot._(guild.id, "logs.emoji_update.restrict", count=len(restrict)) - embed.add_field(name=n, value="\n".join(restrict), inline=False) + field_name = await self.bot._( + guild.id, + "logs.emoji_update.restrict", + count=len(restrict), + ) + embed.add_field(name=field_name, value="\n".join(restrict), inline=False) await self.send_embed(guild, embed) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: await bot.add_cog(Logs(bot), icon="📜") - if plugin_config is not None: - global config - config.update(plugin_config) From 037c06ec68e0bd87b83ee3f57ab7daf783120826 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:36:01 +0000 Subject: [PATCH 020/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(messagemanager):=20f?= =?UTF-8?q?ix=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/messageManager/messageManager.py | 52 +++++++++++++++--------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/plugins/messageManager/messageManager.py b/plugins/messageManager/messageManager.py index b3d42d4f..1713fa1c 100644 --- a/plugins/messageManager/messageManager.py +++ b/plugins/messageManager/messageManager.py @@ -5,21 +5,31 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ +from typing import Union + import discord from discord.ext import commands + from utils import Gunibot -from typing import Union # Moves a message from its original channel to a parameterized channel # using a given webhook -async def moveMessage(msg: discord.Message, webhook: discord.Webhook, thread: discord.Thread = None): +async def move_message( + msg: discord.Message, + webhook: discord.Webhook, + thread: discord.Thread = None, +): + """ + Copy the discord message `msg` to a new channel (or thread if `thread` is specified) using + `webhook`. + """ files = [await x.to_file() for x in msg.attachments] # grab mentions from the source message mentions = discord.AllowedMentions( everyone=msg.mention_everyone, users=msg.mentions, roles=msg.role_mentions ) - + kargs = { "content": msg.content, "files": files, @@ -41,6 +51,8 @@ async def moveMessage(msg: discord.Message, webhook: discord.Webhook, thread: di class MessageManager(commands.Cog): + """Imitate someone, copy messages or an entire conversation with one or two commands.""" + def __init__(self, bot: Gunibot): self.bot = bot self.file = "messageManager" @@ -90,12 +102,12 @@ async def move( if isinstance(channel, str): try: channel = self.bot.get_channel(int(channel.replace("<#", "").replace(">", ""))) - except BaseException: + except ValueError: await ctx.send( await self.bot._(ctx.guild.id, "message_manager.no-channel") ) return - + if not isinstance(channel, discord.abc.Messageable): await ctx.send(await self.bot._(ctx.guild.id, "message_manager.no-channel")) return @@ -121,7 +133,7 @@ async def move( return # Check permission - if (not channel.permissions_for(author).manage_messages): + if not channel.permissions_for(author).manage_messages: embed = discord.Embed( description=await self.bot._( ctx.guild.id, "message_manager.permission" @@ -139,7 +151,7 @@ async def move( # Creates a webhook to resend the message to another channel webhook = await channel.create_webhook(name="Gunipy Hook") - await moveMessage(msg, webhook, thread=thread) + await move_message(msg, webhook, thread=thread) await webhook.delete() if confirm: @@ -189,12 +201,12 @@ async def moveall( if isinstance(channel, str): try: channel = self.bot.get_channel(int(channel.replace("<#", "").replace(">", ""))) - except BaseException: + except ValueError: await ctx.send( await self.bot._(ctx.guild.id, "message_manager.no-channel") ) return - + if not isinstance(channel, discord.abc.Messageable): await ctx.send(await self.bot._(ctx.guild.id, "message_manager.no-channel")) return @@ -221,7 +233,7 @@ async def moveall( return # Check member permissions - if (not channel.permissions_for(author).manage_messages): + if not channel.permissions_for(author).manage_messages: embed = discord.Embed( description=await self.bot._( ctx.guild.id, "message_manager.permission" @@ -296,13 +308,13 @@ async def moveall( counter = 0 # Retrieves the message list from msg1 to msg2 - await moveMessage(msg1, webhook, thread=thread) + await move_message(msg1, webhook, thread=thread) async for msg in msg1.channel.history( limit=200, after=msg1.created_at, before=msg2, oldest_first=True ): - await moveMessage(msg, webhook, thread=thread) + await move_message(msg, webhook, thread=thread) counter += 1 - await moveMessage(msg2, webhook, thread=thread) + await move_message(msg2, webhook, thread=thread) if confirm: # Creates an embed to notify that the messages have been moved @@ -326,12 +338,12 @@ async def moveall( await webhook.delete() -# The End. -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: await bot.add_cog(MessageManager(bot), icon="📋") - if plugin_config is not None: - global config - config.update(plugin_config) - From 44866d5e6b532933a6fe159a6ef6c4964b53cfaa Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:41:24 +0000 Subject: [PATCH 021/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(misc):=20fix=20linte?= =?UTF-8?q?r=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/misc/misc.py | 53 ++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/plugins/misc/misc.py b/plugins/misc/misc.py index bf42739c..35bfd499 100644 --- a/plugins/misc/misc.py +++ b/plugins/misc/misc.py @@ -5,16 +5,19 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ +from typing import Union import random from datetime import datetime import discord from discord.ext import commands -from utils import Gunibot, MyContext -from typing import Union +from utils import Gunibot, MyContext class Misc(commands.Cog): + """Random commands. + What do you except from a plugin called misc. + """ CONTAINS_TIMESTAMP = Union[ int, @@ -43,7 +46,8 @@ async def cookie(self, ctx: MyContext, *, user: discord.User = None): Usage: - Get a cookie: cookie - Give a cookie to someone: cookie - - user: the member you want to give the cookie to. Can be a mention, an id or simply it's name.""" + - user: the member you want to give the cookie to. Can be a mention, an id or simply it's + name.""" # If the cookie is given if user: @@ -68,7 +72,8 @@ async def cookie(self, ctx: MyContext, *, user: discord.User = None): # Sending the message await webhook.send( content=message, - avatar_url="https://d31sxl6qgne2yj.cloudfront.net/wordpress/wp-content/uploads/20190121140737/Minecraft-Villager-Head.jpg", + avatar_url="https://d31sxl6qgne2yj.cloudfront.net/wordpress/wp-content/uploads/"\ + "20190121140737/Minecraft-Villager-Head.jpg", ) # Cleaning webhook & command @@ -118,12 +123,12 @@ async def flip(self, ctx: MyContext): - Flip a coin: coin """ - a = random.randint(-100, 100) + state = random.randint(-100, 100) # The sign of the number define the result. 0 correspond to the side of the coin - if a > 0: + if state > 0: description = await self.bot._(ctx.guild.id, "misc.flipacoin.tails") - elif a < 0: + elif state < 0: description = await self.bot._(ctx.guild.id, "misc.flipacoin.heads") else: description = await self.bot._(ctx.guild.id, "misc.flipacoin.side") @@ -154,12 +159,15 @@ async def rolladice(self, ctx: MyContext, *, dice:int = 6): # Test if the parameter is an integer try: dice = int(dice) - except: + except ValueError: embed = discord.Embed( description=await self.bot._(ctx.guild.id, "misc.dice.error.not_int"), colour=0xe74c3c, ) - embed.set_author(name="Error", icon_url="https://cdn-icons-png.flaticon.com/512/738/738884.png") + embed.set_author( + name="Error", + icon_url="https://cdn-icons-png.flaticon.com/512/738/738884.png", + ) ctx.send(embed=embed) # Test if the parameter is upper than 0 @@ -168,7 +176,10 @@ async def rolladice(self, ctx: MyContext, *, dice:int = 6): description=await self.bot._(ctx.guild.id, "misc.dice.error.not_positive"), colour=0xe74c3c, ) - embed.set_author(name="Error", icon_url="https://cdn-icons-png.flaticon.com/512/738/738884.png") + embed.set_author( + name="Error", + icon_url="https://cdn-icons-png.flaticon.com/512/738/738884.png", + ) ctx.send(embed=embed) # Generate the random value and print it @@ -200,7 +211,7 @@ async def dataja(self, ctx: MyContext): colour=0x2F3136, ) embed.set_thumbnail(url="https://cdn-icons-png.flaticon.com/512/1180/1180260.png") - + await ctx.send(embed = embed) # Deleting command @@ -244,16 +255,16 @@ async def kill(self, ctx: MyContext, *, target: str = None): msg = await self.bot._(ctx.channel, f"misc.kills.{choice}") tries += 1 - footer = self.bot._(ctx.channel, f"misc.kills.footer") + footer = self.bot._(ctx.channel, "misc.kills.footer") # Building the result message embed = discord.Embed( description=msg.format(author, victime, ex), colour=0x2F3136, - footer=footer ) + embed.set_footer(text=footer) embed.set_thumbnail(url="https://cdn-icons-png.flaticon.com/512/3074/3074476.png") - + # Send it await ctx.send( embed=embed, @@ -339,12 +350,12 @@ async def create( ) -# The end. -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: await bot.add_cog(Misc(bot), icon="🍪") - if plugin_config is not None: - global config - config.update(plugin_config) - From bdfc2a72f9a421b40074994423d6dbf5f253382c Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 20:49:07 +0000 Subject: [PATCH 022/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(rolelink):=20fix=20l?= =?UTF-8?q?inter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/roleLink/roleLink.py | 117 +++++++++++++++++------------------ 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/plugins/roleLink/roleLink.py b/plugins/roleLink/roleLink.py index ca1bb647..ada67cbe 100644 --- a/plugins/roleLink/roleLink.py +++ b/plugins/roleLink/roleLink.py @@ -5,24 +5,21 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -from utils import Gunibot -from discord.ext import commands -import discord -from bot import checks -import args -import asyncio -from marshal import dumps, loads from typing import List, Union +from marshal import dumps, loads +import asyncio -import sys +import discord +from discord.ext import commands -sys.path.append("./bot") -sys.path.append("./bot") +from bot import args +from utils import Gunibot # /rolelink when class ActionType(commands.Converter): + """Converter for grant or revoke action types.""" types = ["grant", "revoke"] def __init__(self, action: Union[str, int] = None): @@ -34,13 +31,14 @@ def __init__(self, action: Union[str, int] = None): return self.name = self.types[self.type] - async def convert(self, ctx: commands.Context, argument: str): + async def convert(self, ctx: commands.Context, argument: str): # pylint: disable=unused-argument if argument in self.types: return ActionType(argument) raise commands.errors.BadArgument("Unknown dependency action type") class TriggerType(commands.Converter): + """Converter for trigger type like `get-one` or `loose-all`.""" types = ["get-one", "get-all", "loose-one", "loose-all"] def __init__(self, trigger: Union[str, int] = None): @@ -52,7 +50,7 @@ def __init__(self, trigger: Union[str, int] = None): return self.name = self.types[self.type] - async def convert(self, ctx: commands.Context, argument: str): + async def convert(self, ctx: commands.Context, argument: str): # pylint: disable=unused-argument if argument in self.types: return TriggerType(argument) raise commands.errors.BadArgument("Unknown dependency trigger type") @@ -73,32 +71,31 @@ def __init__( self.trigger_roles = trigger_roles self.b_trigger_roles = dumps(trigger_roles) self.guild = guild - self.id = None + self.dependency_id = None - def to_str(self, useID: bool = True) -> str: + def to_str(self, user_id: bool = True) -> str: triggers = " ".join([f"<@&{r}>" for r in self.trigger_roles]) target = f"<@&{self.target_role}>" - ID = f"{self.id}. " if useID else "" - return f"{ID}{self.action.name} {target} when {self.trigger.name.replace('-', ' ')} of {triggers}" + depedency_id = f"{self.dependency_id}. " if user_id else "" + return f"{depedency_id}{self.action.name} {target} when"\ + f"{self.trigger.name.replace('-', ' ')} of {triggers}" class ConflictingCyclicDependencyError(Exception): """Used when a loop is found when analyzing a role dependencies system""" - pass - class GroupRoles(commands.Cog): def __init__(self, bot: Gunibot): self.bot = bot self.file = "" - def db_get_config(self, guildID: int) -> List[Dependency]: + def db_get_config(self, guild_id: int) -> List[Dependency]: """Get every dependencies of a specific guild""" query = "SELECT rowid, * FROM group_roles WHERE guild=?" # comes as: (rowid, guild, action, target, trigger, trigger-roles) res = list() - liste = self.bot.db_query(query, (guildID,)) + liste = self.bot.db_query(query, (guild_id,)) for row in liste: temp = ( ActionType(row["action"]), @@ -121,19 +118,20 @@ def db_add_action(self, action: Dependency) -> int: action.trigger.type, action.b_trigger_roles, ) - query = "INSERT INTO group_roles (guild, action, target, trigger, `trigger-roles`) VALUES (?, ?, ?, ?, ?)" + query = "INSERT INTO group_roles (guild, action, target, trigger, `trigger-roles`)"\ + "VALUES (?, ?, ?, ?, ?)" lastrowid = self.bot.db_query(query, data) return lastrowid - def db_delete_action(self, guildID: int, actionID: int) -> bool: + def db_delete_action(self, guild_id: int, action_id: int) -> bool: """Delete an action from a guild, based on its row ID Return True if a row was deleted, False else""" query = "DELETE FROM group_roles WHERE guild=? AND rowid=?" - rowcount = self.bot.db_query(query, (guildID, actionID)) + rowcount = self.bot.db_query(query, (guild_id, action_id)) return rowcount == 1 async def filter_allowed_roles( - self, guild: discord.Guild, roles: List[discord.Role] + self, guild: discord.Guild, roles: List[discord.Role], ) -> List[discord.Role]: """Return every role that the bot is allowed to give/remove IE: role exists, role is under bot's highest role @@ -176,11 +174,11 @@ async def check_got_roles(self, member: discord.Member, roles: List[discord.Role actions = self.db_get_config(member.guild.id) if actions is None: return - for action in actions: + for action in actions: # pylint: disable=not-an-iterable if action.trigger.type == 0: # if trigger is 'get-one' - for r in roles: + for role in roles: if ( - r.id in action.trigger_roles + role.id in action.trigger_roles ): # if one given role triggers that action alwd_roles = await self.filter_allowed_roles( member.guild, [action.target_role] @@ -188,9 +186,9 @@ async def check_got_roles(self, member: discord.Member, roles: List[discord.Role await self.give_remove_roles(member, alwd_roles, action.action) break elif action.trigger.type == 1: # if trigger is 'get-all' - for r in roles: + for role in roles: if ( - r.id in action.trigger_roles + role.id in action.trigger_roles ): # if one given role triggers that action member_roles = [x.id for x in member.roles] if all([(x in member_roles) for x in action.trigger_roles]): @@ -207,11 +205,11 @@ async def check_lost_roles(self, member: discord.Member, roles: List[discord.Rol actions = self.db_get_config(member.guild.id) if actions is None: return - for action in actions: + for action in actions: # pylint: disable=not-an-iterable if action.trigger.type == 2: # if trigger is 'loose-one' - for r in roles: + for role in roles: if ( - r.id in action.trigger_roles + role.id in action.trigger_roles ): # if one lost role triggers that action alwd_roles = await self.filter_allowed_roles( member.guild, [action.target_role] @@ -219,9 +217,9 @@ async def check_lost_roles(self, member: discord.Member, roles: List[discord.Rol await self.give_remove_roles(member, alwd_roles, action.action) break elif action.trigger.type == 3: # if trigger is 'loose-all' - for r in roles: + for role in roles: if ( - r.id in action.trigger_roles + role.id in action.trigger_roles ): # if one lost role triggers that action member_roles = [x.id for x in member.roles] if all([(x not in member_roles) for x in action.trigger_roles]): @@ -239,20 +237,20 @@ async def get_triggers( """Get every dependency which will directly trigger a selected action""" triggers = list() unwanted_action = 0 if action.trigger.type <= 1 else 1 - for a in actions: - if a.id == action.id: + for action in actions: + if action.dependency_id == action.dependency_id: continue # if a will trigger action if ( - a.action.type == unwanted_action - and a.target_role in action.trigger_roles + action.action.type == unwanted_action + and action.target_role in action.trigger_roles ): - triggers.append(a) + triggers.append(action) if action.trigger.type in (1, 3): # get-all or loose-all roles = list(action.trigger_roles) - for a in triggers: - if a in roles: - roles.remove(a) + for action in triggers: + if action in roles: + roles.remove(action) if len(roles) > 0: return triggers return triggers @@ -300,31 +298,31 @@ async def rolelink_create( all_actions = self.db_get_config(ctx.guild.id) if all_actions is not None: await self.compute_actions(action, list(), all_actions + [action]) - except ConflictingCyclicDependencyError as e: + except ConflictingCyclicDependencyError as exc: timeout = 20 await ctx.send( await self.bot._( ctx.guild.id, "grouproles.infinite", - dep=e.args[0].to_str(False), + dep=exc.args[0].to_str(False), t=timeout, ) ) - def check(m: discord.Message): + def check(msg: discord.Message): return ( - m.author == ctx.author - and m.channel == ctx.channel - and m.content.lower() in ("oui", "yes") + msg.author == ctx.author + and msg.channel == ctx.channel + and msg.content.lower() in ("oui", "yes") ) try: await self.bot.wait_for("message", check=check, timeout=timeout) except asyncio.TimeoutError: return - actionID = self.db_add_action(action) + action_id = self.db_add_action(action) await ctx.send( - await self.bot._(ctx.guild.id, "grouproles.dep-added", id=actionID) + await self.bot._(ctx.guild.id, "grouproles.dep-added", id=action_id) ) @rolelink_main.command(name="list") @@ -339,15 +337,15 @@ async def rolelink_list(self, ctx: commands.Context): ) return txt = "**" + await self.bot._(ctx.guild.id, "grouproles.list") + "**\n" - for action in actions: + for action in actions: # pylint: disable=not-an-iterable txt += action.to_str() + "\n" await ctx.send(txt) @rolelink_main.command(name="remove") @commands.has_permissions(manage_guild=True) - async def rolelink_delete(self, ctx: commands.Context, id: int): + async def rolelink_delete(self, ctx: commands.Context, rolelink_id: int): """Delete one of your roles-links""" - deleted = self.db_delete_action(ctx.guild.id, id) + deleted = self.db_delete_action(ctx.guild.id, rolelink_id) if deleted: await ctx.send(await self.bot._(ctx.guild.id, "grouproles.dep-deleted")) else: @@ -356,11 +354,12 @@ async def rolelink_delete(self, ctx: commands.Context, id: int): ) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin + + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: await bot.add_cog(GroupRoles(bot), icon="🔰") - if plugin_config is not None: - global config - config.update(plugin_config) - From 0216543206d21ee272b641ba072252039a2552e4 Mon Sep 17 00:00:00 2001 From: ascpial Date: Wed, 22 Feb 2023 21:39:31 +0000 Subject: [PATCH 023/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(rss):=20fix=20linter?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/rss/rss.py | 908 ++++++++++++++++++++++----------------------- 1 file changed, 449 insertions(+), 459 deletions(-) diff --git a/plugins/rss/rss.py b/plugins/rss/rss.py index d5c4d2cc..f858a4f8 100644 --- a/plugins/rss/rss.py +++ b/plugins/rss/rss.py @@ -5,30 +5,36 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -from utils import Gunibot, MyContext -from feedparser.util import FeedParserDict -from discord.ext import commands, tasks -from aiohttp.client import ClientSession -from aiohttp import client_exceptions -import twitter -import feedparser -import discord -from bot import checks -import async_timeout -import args import asyncio import datetime import html import re import time import typing +from feedparser.util import FeedParserDict from marshal import dumps, loads +import async_timeout + +import discord +from aiohttp.client import ClientSession +from aiohttp import client_exceptions +from discord.ext import commands, tasks +import twitter +import feedparser + +from bot import checks, args +from utils import Gunibot, MyContext import core -async def setup(bot:Gunibot): await bot.add_cog(Rss(bot), icon="📰") +async def setup(bot:Gunibot): + await bot.add_cog(Rss(bot), icon="📰") class Rss(commands.Cog): - """Cog which deals with everything related to rss flows. Whether it is to add automatic tracking to a stream, or just to see the latest video released by Discord, it is this cog that will be used.""" + """ + Cog which deals with everything related to rss flows. Whether it is to add automatic tracking + to a stream, or just to see the latest video released by Discord, it is this cog that will be + used. + """ def __init__(self, bot: Gunibot): self.config = core.config.get("rss") @@ -40,41 +46,34 @@ def __init__(self, bot: Gunibot): self.embed_color = discord.Color(6017876) self.loop_processing = False - self.twitterAPI = twitter.Api(**self.config["twitter"], tweet_mode="extended") + self.twitter_api = twitter.Api(**self.config["twitter"], tweet_mode="extended") self.twitter_over_capacity = False self.min_time_between_posts = {"web": 120, "tw": 15, "yt": 120} - self.cache = dict() + self.cache = {} self.table = "rss_flows" - try: - self.date = bot.get_cog("TimeCog").date - except BaseException: - pass - # launch rss loop - self.loop_child.change_interval(minutes=self.time_loop) - self.loop_child.start() - @commands.Cog.listener() - async def on_ready(self): - self.date = self.bot.get_cog("TimeCog").date + # launch rss loop + self.loop_child.change_interval(minutes=self.time_loop) # pylint: disable=no-member + self.loop_child.start() # pylint: disable=no-member - def cog_unload(self): - self.loop_child.cancel() + async def cog_unload(self): + self.loop_child.cancel() # pylint: disable=no-member - class rssMessage: + class RSSMessage: def __init__( self, bot: Gunibot, - Type, + message_type, url, title, date=datetime.datetime.now(), author=None, - Format=None, + message_format=None, channel=None, image=None, ): self.bot = bot - self.Type = Type + self.message_type = message_type self.url = url self.title = title self.embed = False # WARNING COOKIES WARNINNG @@ -88,7 +87,7 @@ def __init__( else: date = None self.author = author - self.format = Format + self.message_format = message_format self.logo = ":newspaper:" self.channel = channel self.mentions = [] @@ -98,7 +97,7 @@ def __init__( def fill_embed_data(self, flow: dict): if not flow["use_embed"]: return - self.embed_data = { + self.embed_data = { # pylint: disable=attribute-defined-outside-init "color": discord.Colour(0).default(), "footer": "", "title": None, @@ -118,29 +117,29 @@ async def fill_mention( self, guild: discord.Guild, roles: typing.List[str], translate ): if roles == []: - r = await translate(guild.id, "keywords.none") + roles = await translate(guild.id, "keywords.none") else: - r = list() + roles = list() for item in roles: if item == "": continue role = discord.utils.get(guild.roles, id=int(item)) if role is not None: - r.append(role.mention) + roles.append(role.mention) else: - r.append(item) - self.mentions = r + roles.append(item) + self.mentions = roles return self async def create_msg(self, language, Format=None): if Format is None: - Format = self.format + Format = self.message_format if not isinstance(self.date, str): - d = await self.bot.get_cog("TimeCog").date( + date = await self.bot.get_cog("TimeCog").date( self.date, lang=language, year=False, hour=True, digital=True ) else: - d = self.date + date = self.date Format = Format.replace("\\n", "\n") _channel = discord.utils.escape_markdown(self.channel) _author = discord.utils.escape_markdown(self.author) @@ -148,7 +147,7 @@ async def create_msg(self, language, Format=None): self.bot.SafeDict( channel=_channel, title=self.title, - date=d, + date=date, url=self.url, link=self.url, mentions=", ".join(self.mentions), @@ -158,29 +157,29 @@ async def create_msg(self, language, Format=None): ) if not self.embed: return text - else: - emb = discord.Embed( - description=text, - timestamp=self.date, - color=self.embed_data.get("color", 0), - ) - if footer := self.embed_data.get("footer", None): - emb.set_footer(text=footer) - if self.embed_data.get("title", None) is None: - if self.Type != "tw": - emb.title = self.title - else: - emb.title = self.author + + embed = discord.Embed( + description=text, + timestamp=self.date, + color=self.embed_data.get("color", 0), + ) + if footer := self.embed_data.get("footer", None): + embed.set_footer(text=footer) + if self.embed_data.get("title", None) is None: + if self.message_type != "tw": + embed.title = self.title else: - emb.title = self.embed_data["title"] - emb.add_field(name="URL", value=self.url, inline=False) - if self.image is not None: - emb.set_thumbnail(url=self.image) - return emb + embed.title = self.author + else: + embed.title = self.embed_data["title"] + embed.add_field(name="URL", value=self.url, inline=False) + if self.image is not None: + embed.set_thumbnail(url=self.image) + return embed async def get_lang(self, guild: typing.Optional[discord.Guild]) -> str: - guildID = guild.id if guild else None - return await self.bot.get_cog("Languages").get_lang(guildID, True) + guild_id = guild.id if guild else None + return await self.bot.get_cog("Languages").get_lang(guild_id, True) @commands.group(name="rss") @commands.cooldown(2, 15, commands.BucketType.channel) @@ -243,8 +242,8 @@ async def request_tw(self, ctx: MyContext, name): name = await self.parse_tw_url(name) try: text = await self.rss_tw(ctx.channel, name) - except Exception as e: - return await self.bot.get_cog("Errors").on_error(e, ctx) + except Exception as exc: # pylint: disable=broad-exception-caught + return await self.bot.get_cog("Errors").on_error(exc, ctx) if isinstance(text, str): await ctx.send(text) else: @@ -313,62 +312,62 @@ async def system_add(self, ctx: MyContext, link): ) return identifiant = await self.parse_yt_url(link) - Type = None + rss_type = None if identifiant is not None: - Type = "yt" + rss_type = "yt" display_type = "youtube" if identifiant is None: identifiant = await self.parse_tw_url(link) if identifiant is not None: - Type = "tw" + rss_type = "tw" display_type = "twitter" if identifiant is None: identifiant = await self.parse_twitch_url(link) if identifiant is not None: - Type = "twitch" + rss_type = "twitch" display_type = "twitch" if identifiant is None: identifiant = await self.parse_deviant_url(link) if identifiant is not None: - Type = "deviant" + rss_type = "deviant" display_type = "deviantart" if identifiant is not None and not link.startswith("https://"): link = "https://" + link if identifiant is None and link.startswith("http"): identifiant = link - Type = "web" + rss_type = "web" display_type = "website" elif not link.startswith("http"): await ctx.send(await self.bot._(ctx.guild, "rss.invalid-link")) return - if Type is None or not await self.check_rss_url(link): + if rss_type is None or not await self.check_rss_url(link): return await ctx.send(await self.bot._(ctx.guild.id, "rss.invalid-flow")) try: - ID = await self.db_add_flow(ctx.guild.id, ctx.channel.id, Type, identifiant) + feed_id = await self.db_add_flow(ctx.guild.id, ctx.channel.id, rss_type, identifiant) await ctx.send( str(await self.bot._(ctx.guild, "rss.success-add")).format( display_type, link, ctx.channel.mention ) ) self.bot.log.info( - "RSS feed added into server {} ({} - {})".format(ctx.guild.id, link, ID) + f"RSS feed added into server {ctx.guild.id} ({link} - {feed_id})" ) await self.send_log( - "Feed added into server {} ({})".format(ctx.guild.id, ID), ctx.guild + f"Feed added into server {ctx.guild.id} ({feed_id})", ctx.guild, ) - except Exception as e: + except Exception as exception: # pylint: disable=broad-exception-caught await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) - await self.bot.get_cog("Errors").on_error(e, ctx) + await self.bot.get_cog("Errors").on_error(exception, ctx) @rss_main.command(name="remove", aliases=["delete"]) @commands.guild_only() @commands.check(commands.has_guild_permissions(manage_webhooks=True)) - async def systeme_rm(self, ctx: MyContext, ID: int = None): + async def systeme_rm(self, ctx: MyContext, rss_id: int = None): """Delete an rss feed from the list Example: rss remove""" - flow = await self.askID( - ID, + flow = await self.ask_id( + rss_id, ctx, await self.bot._(ctx.guild.id, "rss.choose-delete"), allow_mc=True, @@ -378,16 +377,16 @@ async def systeme_rm(self, ctx: MyContext, ID: int = None): return try: await self.db_remove_flow(flow[0]["ID"]) - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) - await self.bot.get_cog("Errors").on_error(e, ctx) + await self.bot.get_cog("Errors").on_error(exc, ctx) return await ctx.send(await self.bot._(ctx.guild, "rss.delete-success")) self.bot.log.info( - "RSS feed deleted into server {} ({})".format(ctx.guild.id, flow[0]["ID"]) + f"RSS feed deleted into server {ctx.guild.id} ({flow[0]['ID']})" ) await self.send_log( - "Feed deleted into server {} ({})".format(ctx.guild.id, flow[0]["ID"]), + f"Feed deleted into server {ctx.guild.id} ({flow[0]['ID']})", ctx.guild, ) @@ -403,26 +402,26 @@ async def list_flows(self, ctx: MyContext): return title = await self.bot._(ctx.guild.id, "rss.list-title", server=ctx.guild.name) translation = await self.bot._(ctx.guild.id, "rss.list-result") - l = list() - for x in liste: - c = self.bot.get_channel(x["channel"]) - if c is not None: - c = c.mention + rss_feeds = [] + for feed in liste: + channel = self.bot.get_channel(feed["channel"]) + if channel is not None: + channel = channel.mention else: - c = x["channel"] - if len(x["roles"]) == 0: - r = await self.bot._(ctx.guild.id, "keywords.none") + channel = feed["channel"] + if len(feed["roles"]) == 0: + roles = await self.bot._(ctx.guild.id, "keywords.none") else: - r = list() - for item in x["roles"]: + roles = [] + for item in feed["roles"]: role = discord.utils.get(ctx.guild.roles, id=int(item)) if role is not None: - r.append(role.mention) + roles.append(role.mention) else: - r.append(item) - r = ", ".join(r) - Type = await self.bot._(ctx.guild.id, "rss." + x["type"]) - if len(l) > 20: + roles.append(item) + roles = ", ".join(roles) + rss_type = await self.bot._(ctx.guild.id, "rss." + feed["type"]) + if len(rss_feeds) > 20: embed = discord.Embed( title=title, color=self.embed_color, @@ -431,23 +430,30 @@ async def list_flows(self, ctx: MyContext): embed.set_footer( text=str(ctx.author), icon_url=ctx.author.display_avatar ) - for text in l: + for text in rss_feeds: embed.add_field(name="\uFEFF", value=text, inline=False) await ctx.send(embed=embed) - l.clear() - l.append(translation.format(Type, c, x["link"], r, x["ID"], x["date"])) - if len(l) > 0: + rss_feeds.clear() + rss_feeds.append(translation.format( + rss_type, + channel, + feed["link"], + roles, + feed["ID"], + feed["date"] + )) + if len(rss_feeds) > 0: embed = discord.Embed( title=title, color=self.embed_color, timestamp=ctx.message.created_at ) embed.set_footer(text=str(ctx.author), icon_url=ctx.author.display_avatar) - for x in l: - embed.add_field(name="\uFEFF", value=x, inline=False) + for feed in rss_feeds: + embed.add_field(name="\uFEFF", value=feed, inline=False) await ctx.send(embed=embed) - async def askID( + async def ask_id( self, - ID, + feed_id, ctx: MyContext, title: str, allow_mc: bool = False, @@ -455,63 +461,63 @@ async def askID( ): """Request the ID of an rss stream""" flow = list() - if ID is not None: - flow = await self.db_get_flow(ID) + if feed_id is not None: + flow = await self.db_get_flow(feed_id) if flow == []: - ID = None + feed_id = None elif str(flow[0]["guild"]) != str(ctx.guild.id): - ID = None + feed_id = None elif (not allow_mc) and flow[0]["type"] == "mc": - ID = None - userID = ctx.author.id - if ID is None: - gl = await self.db_get_guild_flows(ctx.guild.id) - if len(gl) == 0: + feed_id = None + user_id = ctx.author.id + if feed_id is None: + guild_feeds = await self.db_get_guild_flows(ctx.guild.id) + if len(guild_feeds) == 0: await ctx.send(await self.bot._(ctx.guild.id, "rss.no-feed")) return if display_mentions: text = [await self.bot._(ctx.guild.id, "rss.list")] else: text = [await self.bot._(ctx.guild.id, "rss.list2")] - list_of_IDs = list() + feed_ids = [] iterator = 1 - translations = dict() - for x in gl: - if (not allow_mc) and x["type"] == "mc": + translations = {} + for feed in guild_feeds: + if (not allow_mc) and feed["type"] == "mc": continue - if x["type"] == "tw" and x["link"].isnumeric(): + if feed["type"] == "tw" and feed["link"].isnumeric(): try: - x["link"] = self.twitterAPI.GetUser( - user_id=int(x["link"]) + feed["link"] = self.twitter_api.GetUser( + user_id=int(feed["link"]) ).screen_name - except twitter.TwitterError as e: + except twitter.TwitterError: pass - list_of_IDs.append(x["ID"]) - c = self.bot.get_channel(x["channel"]) - if c is not None: - c = c.mention + feed_ids.append(feed["ID"]) + channel = self.bot.get_channel(feed["channel"]) + if channel is not None: + channel = channel.mention else: - c = x["channel"] - Type = translations.get( - x["type"], await self.bot._(ctx.guild.id, "rss." + x["type"]) + channel = feed["channel"] + feed_type = translations.get( + feed["type"], await self.bot._(ctx.guild.id, "rss." + feed["type"]) ) if display_mentions: - if len(x["roles"]) == 0: - r = await self.bot._(ctx.guild.id, "keywords.none") + if len(feed["roles"]) == 0: + roles = await self.bot._(ctx.guild.id, "keywords.none") else: - r = list() - for item in x["roles"]: + roles = list() + for item in feed["roles"]: role = discord.utils.get(ctx.guild.roles, id=int(item)) if role is not None: - r.append(role.mention) + roles.append(role.mention) else: - r.append(item) - r = ", ".join(r) + roles.append(item) + roles = ", ".join(roles) text.append( - "{}) {} - {} - {} - {}".format(iterator, Type, x["link"], c, r) + f"{iterator}) {feed_type} - {feed['link']} - {channel} - {roles}", ) else: - text.append("{}) {} - {} - {}".format(iterator, Type, x["link"], c)) + text.append(f"{iterator}) {feed_type} - {feed['link']} - {channel}") iterator += 1 if len("\n".join(text)) < 2048: desc = "\n".join(text) @@ -534,14 +540,14 @@ async def askID( ) embed.set_footer(text=str(ctx.author), icon_url=ctx.author.display_avatar) if fields is not None: - for f in fields: - embed.add_field(**f) + for field in fields: + embed.add_field(**field) emb_msg: discord.Message = await ctx.send(embed=embed) def check(msg): if not msg.content.isnumeric(): return False - return msg.author.id == userID and int(msg.content) in range( + return msg.author.id == user_id and int(msg.content) in range( 1, iterator ) @@ -553,20 +559,20 @@ def check(msg): await ctx.send(await self.bot._(ctx.guild.id, "rss.too-long")) await emb_msg.delete() return - flow = await self.db_get_flow(list_of_IDs[int(msg.content) - 1]) + flow = await self.db_get_flow(feed_ids[int(msg.content) - 1]) if len(flow) == 0: await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) return return flow def parse_output(self, arg): - r = re.findall(r"((? 0: + pattern = re.findall(r"((? 0: def flatten(l): return [item for sublist in l for item in sublist] - params = [[x for x in group if x != '"'] for group in r] + params = [[x for x in group if x != '"'] for group in pattern] return flatten(params) else: return arg.split(" ") @@ -577,7 +583,7 @@ def flatten(l): async def roles_flows( self, ctx: MyContext, - ID: int = None, + flow_id: int = None, mentions: commands.Greedy[discord.Role] = None, ): """Configures a role to be notified when a news is posted @@ -590,12 +596,12 @@ async def roles_flows( """ try: # ask for flow ID - flow = await self.askID( - ID, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") + flow = await self.ask_id( + flow_id, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") ) - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught flow = [] - await self.bot.get_cog("Errors").on_error(e, ctx) + await self.bot.get_cog("Errors").on_error(exc, ctx) if flow is None: return if len(flow) == 0: @@ -607,15 +613,15 @@ async def roles_flows( if flow["roles"] == "": text = await self.bot._(ctx.guild.id, "rss.no-roles") else: - r = list() + roles = [] for item in flow["roles"]: role = discord.utils.get(ctx.guild.roles, id=int(item)) if role is not None: - r.append(role.mention) + roles.append(role.mention) else: - r.append(item) - r = ", ".join(r) - text = str(await self.bot._(ctx.guild.id, "rss.roles-list")).format(r) + roles.append(item) + roles = ", ".join(roles) + text = str(await self.bot._(ctx.guild.id, "rss.roles-list")).format(roles) # ask for roles embed = discord.Embed( title=await self.bot._(ctx.guild.id, "rss.choose-roles"), @@ -625,34 +631,34 @@ async def roles_flows( ) emb_msg = await ctx.send(embed=embed) err = await self.bot._(ctx.guild.id, "find.role-0") - userID = ctx.author.id + user_id = ctx.author.id def check2(msg): - return msg.author.id == userID + return msg.author.id == user_id cond = False - while cond == False: + while cond is False: try: msg = await self.bot.wait_for("message", check=check2, timeout=30.0) if ( msg.content.lower() in no_role ): # if no role should be mentionned - IDs = [None] + id_list = [None] else: - l = self.parse_output(msg.content) - IDs = list() - Names = list() - for x in l: - x = x.strip() + roles = self.parse_output(msg.content) + id_list = list() + name_list = list() + for role in roles: + role = role.strip() try: - r = await commands.RoleConverter().convert(ctx, x) - IDs.append(str(r.id)) - Names.append(r.name) - except BaseException: + roles = await commands.RoleConverter().convert(ctx, role) + id_list.append(str(roles.id)) + name_list.append(roles.name) + except BaseException: # pylint: disable=broad-exception-caught await ctx.send(err) - IDs = [] + id_list = [] break - if len(IDs) > 0: + if len(id_list) > 0: cond = True except asyncio.TimeoutError: await ctx.send(await self.bot._(ctx.guild.id, "rss.too-long")) @@ -660,30 +666,29 @@ def check2(msg): return else: # if roles were specified if mentions in no_role: # if no role should be mentionned - IDs = None + id_list = None else: - IDs = list() - Names = list() - for r in mentions: - IDs.append(r.id) - Names.append(r.name) - if len(IDs) == 0: + id_list = list() + name_list = list() + for roles in mentions: + id_list.append(roles.id) + name_list.append(roles.name) + if len(id_list) == 0: await ctx.send(await self.bot._(ctx.guild.id, "find.role-0")) return try: - if IDs is None: + if id_list is None: await self.db_update_flow(flow["ID"], values=[("roles", None)]) await ctx.send(await self.bot._(ctx.guild.id, "rss.roles-1")) else: - await self.db_update_flow(flow["ID"], values=[("roles", dumps(IDs))]) - txt = ", ".join(Names) + await self.db_update_flow(flow["ID"], values=[("roles", dumps(id_list))]) + txt = ", ".join(name_list) await ctx.send( str(await self.bot._(ctx.guild.id, "rss.roles-0")).format(txt) ) - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) - await self.bot.get_cog("Errors").on_error(e, ctx) - return + await self.bot.get_cog("Errors").on_error(exc, ctx) @rss_main.command(name="reload") @commands.guild_only() @@ -692,7 +697,7 @@ def check2(msg): async def reload_guild_flows(self, ctx: MyContext): """Reload every rss feeds from your server""" try: - t = time.time() + start = time.time() msg: discord.Message = await ctx.send( str(await self.bot._(ctx.guild.id, "rss.guild-loading")).format("...") ) @@ -700,13 +705,13 @@ async def reload_guild_flows(self, ctx: MyContext): await self.main_loop(ctx.guild.id) await ctx.send( str(await self.bot._(ctx.guild.id, "rss.guild-complete")).format( - len(liste), round(time.time() - t, 1) + len(liste), round(time.time() - start, 1) ) ) await msg.delete() - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught await ctx.send( - str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(e) + str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(exc) ) @rss_main.command(name="move") @@ -715,7 +720,7 @@ async def reload_guild_flows(self, ctx: MyContext): async def move_guild_flow( self, ctx: MyContext, - ID: typing.Optional[int] = None, + flow_id: typing.Optional[int] = None, channel: discord.TextChannel = None, ): """Move a rss feed in another channel @@ -730,18 +735,18 @@ async def move_guild_flow( if channel is None: channel = ctx.channel try: - flow = await self.askID( - ID, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") + flow = await self.ask_id( + flow_id, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") ) - e = None - except Exception as e: + error = None + except Exception: # pylint: disable=broad-exception-caught flow = [] if flow is None: return if len(flow) == 0: await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) - if e is not None: - await self.bot.get_cog("Errors").on_error(e, ctx) + if error is not None: + await self.bot.get_cog("Errors").on_error(error, ctx) return flow = flow[0] await self.db_update_flow(flow["ID"], [("channel", channel.id)]) @@ -750,16 +755,16 @@ async def move_guild_flow( flow["ID"], channel.mention ) ) - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught await ctx.send( - str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(e) + str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(exc) ) @rss_main.command(name="text") @commands.guild_only() @commands.check(commands.has_permissions(manage_webhooks=True)) async def change_text_flow( - self, ctx: MyContext, ID: typing.Optional[int] = None, *, text=None + self, ctx: MyContext, flow_id: typing.Optional[int] = None, *, text=None ): """Change the text of an rss feed @@ -779,16 +784,16 @@ async def change_text_flow( """ try: try: - flow = await self.askID( - ID, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") + flow = await self.ask_id( + flow_id, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") ) - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught, unused-variable flow = [] if flow is None: return if len(flow) == 0: await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) - await self.bot.get_cog("Errors").on_error(e, ctx) + await self.bot.get_cog("Errors").on_error(exc, ctx) # pylint: disable=used-before-assignment return flow = flow[0] if text is None: @@ -814,11 +819,11 @@ def check(msg): flow["ID"], text ) ) - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught await ctx.send( - str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(e) + str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(exc) ) - await ctx.bot.get_cog("Errors").on_error(e, ctx) + await ctx.bot.get_cog("Errors").on_error(exc, ctx) @rss_main.command(name="use_embed", aliases=["embed"]) @commands.guild_only() @@ -826,13 +831,14 @@ def check(msg): async def change_use_embed( self, ctx: MyContext, - ID: typing.Optional[int] = None, + flow_id: typing.Optional[int] = None, value: bool = None, *, arguments: args.arguments = None, ): """Use an embed (or not) for a flow - You can also provide arguments to change the color/text of the embed. Followed arguments are usable: + You can also provide arguments to change the color/text of the embed. Followed arguments + are usable: - color: color of the embed (hex or decimal value) - title: title override, which will disable the default one (max 256 characters) - footer: small text displayed at the bottom of the embed @@ -843,20 +849,20 @@ async def change_use_embed( - rss embed 6678466620137 1 """ try: - e = None + error = None try: - flow = await self.askID( - ID, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") + flow = await self.ask_id( + flow_id, ctx, await self.bot._(ctx.guild.id, "rss.choose-mentions-1") ) - except Exception as e: + except Exception as error: # pylint: disable=broad-exception-caught flow = [] - await self.bot.get_cog("Errors").on_error(e, ctx) + await self.bot.get_cog("Errors").on_error(error, ctx) if flow is None: return if len(flow) == 0: await ctx.send(await self.bot._(ctx.guild, "rss.fail-add")) - if e is not None: - await self.bot.get_cog("Errors").on_error(e, ctx) + if error is not None: + await self.bot.get_cog("Errors").on_error(error, ctx) return if arguments is None or len(arguments.keys()) == 0: arguments = None @@ -875,8 +881,8 @@ async def change_use_embed( def check(msg): try: - _ = commands.core._convert_to_bool(msg.content) - except BaseException: + _ = commands.core._convert_to_bool(msg.content) # pylint: disable=protected-access, no-member + except BaseException: # pylint: disable=broad-exception-caught return False return msg.author == ctx.author and msg.channel == ctx.channel @@ -886,7 +892,7 @@ def check(msg): return await ctx.send( await self.bot._(ctx.guild.id, "rss.too-long") ) - value = commands.core._convert_to_bool(msg.content) + value = commands.core._convert_to_bool(msg.content) # pylint: disable=protected-access, no-member if value is not None and value != flow["use_embed"]: embed_data["use_embed"] = value txt.append( @@ -899,11 +905,11 @@ def check(msg): return if arguments is not None: if "color" in arguments.keys(): - c = await commands.ColourConverter().convert( + color = await commands.ColourConverter().convert( ctx, arguments["color"] ) - if c is not None: - embed_data["color"] = c.value + if color is not None: + embed_data["color"] = color.value if "title" in arguments.keys(): embed_data["title"] = arguments["title"] if "footer" in arguments.keys(): @@ -914,139 +920,136 @@ def check(msg): flow["ID"], [("embed_structure", dumps(embed_data))] ) await ctx.send("\n".join(txt)) - except Exception as e: + except Exception as error: # pylint: disable=broad-exception-caught await ctx.send( - str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(e) + str(await self.bot._(ctx.guild.id, "rss.guild-error")).format(error) ) - await ctx.bot.get_cog("Errors").on_error(e, ctx) + await ctx.bot.get_cog("Errors").on_error(error, ctx) @rss_main.command(name="test") @commands.check(checks.is_bot_admin) - async def test_rss(self, ctx: MyContext, url, *, args=None): + async def test_rss(self, ctx: MyContext, url, *, arguments=None): """Test if an rss feed is usable""" url = url.replace("<", "").replace(">", "") try: feeds = await self.feed_parse(url, 8) - txt = "feeds.keys()\n```py\n{}\n```".format(feeds.keys()) + txt = f"feeds.keys()\n```py\n{feeds.keys()}\n```" if "bozo_exception" in feeds.keys(): - txt += "\nException ({}): {}".format( - feeds["bozo"], str(feeds["bozo_exception"]) - ) + txt += f"\nException ({feeds['bozo']}): {str(feeds['bozo_exception'])}" return await ctx.send(txt) if len(str(feeds.feed)) < 1400 - len(txt): - txt += "feeds.feed\n```py\n{}\n```".format(feeds.feed) + txt += f"feeds.feed\n```py\n{feeds.feed}\n```" else: - txt += "feeds.feed.keys()\n```py\n{}\n```".format(feeds.feed.keys()) + txt += f"feeds.feed.keys()\n```py\n{feeds.feed.keys()}\n```" if len(feeds.entries) > 0: if len(str(feeds.entries[0])) < 1950 - len(txt): - txt += "feeds.entries[0]\n```py\n{}\n```".format(feeds.entries[0]) + txt += f"feeds.entries[0]\n```py\n{feeds.entries[0]}\n```" else: - txt += "feeds.entries[0].keys()\n```py\n{}\n```".format( - feeds.entries[0].keys() - ) - if args is not None and "feeds" in args and "ctx" not in args: - txt += "\n{}\n```py\n{}\n```".format(args, eval(args)) + txt += f"feeds.entries[0].keys()\n```py\n{feeds.entries[0].keys()}\n```" + if arguments is not None and "feeds" in arguments and "ctx" not in arguments: + txt += f"\n{arguments}\n```py\n{eval(arguments)}\n```" # we checked that the user is a bot admin pylint: disable=eval-used try: await ctx.send(txt) - except Exception as e: - print("[rss_test] Error:", e) - await ctx.send("`Error`: " + str(e)) + except Exception as exc: # pylint: disable=broad-exception-caught + print("[rss_test] Error:", exc) + await ctx.send("`Error`: " + str(exc)) print(txt) - if args is None: - ok = "✅" - notok = "❌" + if arguments is None: + good = "✅" + notgood = "❌" nothing = "\t" txt = ["**__Analyse :__**", ""] - yt = await self.parse_yt_url(feeds.feed["link"]) - if yt is None: - tw = await self.parse_tw_url(feeds.feed["link"]) - if tw is not None: - txt.append("<:twitter:437220693726330881> " + tw) + youtube = await self.parse_yt_url(feeds.feed["link"]) + if youtube is None: + tw_url = await self.parse_tw_url(feeds.feed["link"]) + if tw_url is not None: + txt.append("<:twitter:437220693726330881> " + tw_url) elif "link" in feeds.feed.keys(): txt.append(":newspaper: <" + feeds.feed["link"] + ">") else: txt.append(":newspaper: No 'link' var") else: - txt.append("<:youtube:447459436982960143> " + yt) - txt.append("Entrées : {}".format(len(feeds.entries))) + txt.append("<:youtube:447459436982960143> " + youtube) + txt.append(f"Entrées : {len(feeds.entries)}") if len(feeds.entries) > 0: entry = feeds.entries[0] if "title" in entry.keys(): - txt.append(nothing + ok + " title: ") + txt.append(nothing + good + " title: ") if len(entry["title"].split("\n")) > 1: txt[-1] += entry["title"].split("\n")[0] + "..." else: txt[-1] += entry["title"] else: - txt.append(nothing + notok + " title") + txt.append(nothing + notgood + " title") if "published_parsed" in entry.keys(): - txt.append(nothing + ok + " published_parsed") + txt.append(nothing + good + " published_parsed") elif "published" in entry.keys(): - txt.append(nothing + ok + " published") + txt.append(nothing + good + " published") elif "updated_parsed" in entry.keys(): - txt.append(nothing + ok + " updated_parsed") + txt.append(nothing + good + " updated_parsed") else: - txt.append(nothing + notok + " date") + txt.append(nothing + notgood + " date") if "author" in entry.keys(): - txt.append(nothing + ok + " author: " + entry["author"]) + txt.append(nothing + good + " author: " + entry["author"]) else: - txt.append(nothing + notok + " author") + txt.append(nothing + notgood + " author") await ctx.send("\n".join(txt)) - except Exception as e: - await ctx.bot.get_cog("Errors").on_command_error(ctx, e) + except Exception as exc: # pylint: disable=broad-exception-caught + await ctx.bot.get_cog("Errors").on_command_error(ctx, exc) async def check_rss_url(self, url): - r = await self.parse_yt_url(url) - if r is not None: + result = await self.parse_yt_url(url) + if result is not None: return True - r = await self.parse_tw_url(url) - if r is not None: + result = await self.parse_tw_url(url) + if result is not None: return True - r = await self.parse_twitch_url(url) - if r is not None: + result = await self.parse_twitch_url(url) + if result is not None: return True - r = await self.parse_deviant_url(url) - if r is not None: + result = await self.parse_deviant_url(url) + if result is not None: return True try: - f = await self.feed_parse(url, 8) - _ = f.entries[0] + feed = await self.feed_parse(url, 8) + _ = feed.entries[0] return True - except BaseException: + except BaseException: # pylint: disable=broad-exception-caught return False async def parse_yt_url(self, url): - r = r"(?:http.*://)?(?:www.)?(?:youtube.com|youtu.be)(?:(?:/channel/|/user/)(.+)|/[\w-]+$)" - match = re.search(r, url) + pattern = r"(?:http.*://)?(?:www.)?(?:youtube.com|youtu.be)"\ + r"(?:(?:/channel/|/user/)(.+)|/[\w-]+$)" + match = re.search(pattern, url) if match is None: return None else: return match.group(1) async def parse_tw_url(self, url): - r = r"(?:http.*://)?(?:www.)?(?:twitter.com/)([^?\s]+)" - match = re.search(r, url) + pattern = r"(?:http.*://)?(?:www.)?(?:twitter.com/)([^?\s]+)" + match = re.search(pattern, url) if match is None: return None else: name = match.group(1) try: - user = self.twitterAPI.GetUser(screen_name=name) + user = self.twitter_api.GetUser(screen_name=name) except twitter.TwitterError: return None return user.id async def parse_twitch_url(self, url): - r = r"(?:http.*://)?(?:www.)?(?:twitch.tv/)([^?\s]+)" - match = re.search(r, url) + pattern = r"(?:http.*://)?(?:www.)?(?:twitch.tv/)([^?\s]+)" + match = re.search(pattern, url) if match is None: return None else: return match.group(1) async def parse_deviant_url(self, url): - r = r"(?:http.*://)?(?:www.)?(?:deviantart.com/)([^?\s]+)" - match = re.search(r, url) + pattern = r"(?:http.*://)?(?:www.)?(?:deviantart.com/)([^?\s]+)" + match = re.search(pattern, url) if match is None: return None else: @@ -1059,9 +1062,9 @@ async def feed_parse( # if session is provided, we have to not close it _session = session or ClientSession() try: - async with async_timeout.timeout(timeout) as cm: + async with async_timeout.timeout(timeout) as clientconnection: async with _session.get(url) as response: - html = await response.text() + response_html = await response.text() headers = response.raw_headers except ( client_exceptions.ClientConnectorCertificateError, @@ -1079,12 +1082,12 @@ async def feed_parse( return None if session is None: await _session.close() - if cm.expired: + if clientconnection.expired: # request was cancelled by timeout self.bot.info("[RSS] feed_parse got a timeout") return None headers = {k.decode("utf-8").lower(): v.decode("utf-8") for k, v in headers} - return feedparser.parse(html, response_headers=headers) + return feedparser.parse(response_html, response_headers=headers) async def rss_yt( self, @@ -1111,9 +1114,9 @@ async def rss_yt( img_url = None if "media_thumbnail" in feed.keys() and len(feed["media_thumbnail"]) > 0: img_url = feed["media_thumbnail"][0]["url"] - obj = self.rssMessage( + obj = self.RSSMessage( bot=self.bot, - Type="yt", + message_type="yt", url=feed["link"], title=feed["title"], date=feed["published_parsed"], @@ -1141,9 +1144,9 @@ async def rss_yt( and len(feed["media_thumbnail"]) > 0 ): img_url = feed["media_thumbnail"][0]["url"] - obj = self.rssMessage( + obj = self.RSSMessage( bot=self.bot, - Type="yt", + message_type="yt", url=feed["link"], title=feed["title"], date=feed["published_parsed"], @@ -1162,39 +1165,37 @@ async def rss_tw( return await self.bot._(channel, "rss.tw-help") try: if name.isnumeric(): - posts = self.twitterAPI.GetUserTimeline( + posts = self.twitter_api.GetUserTimeline( user_id=int(name), exclude_replies=True ) - username = self.twitterAPI.GetUser(user_id=int(name)).screen_name + username = self.twitter_api.GetUser(user_id=int(name)).screen_name else: - posts = self.twitterAPI.GetUserTimeline( + posts = self.twitter_api.GetUserTimeline( screen_name=name, exclude_replies=True ) username = name - except twitter.error.TwitterError as e: - if e.message == "Not authorized.": + except twitter.error.TwitterError as exc: + if exc.message == "Not authorized.": return await self.bot._(channel, "rss.nothing") - if "Unknown error" in e.message: + if "Unknown error" in exc.message: return await self.bot._(channel, "rss.nothing") - if "The twitter.Api instance must be authenticated." in e.message: + if "The twitter.Api instance must be authenticated." in exc.message: return await self.bot._(channel, "rss.wrong-token") - if e.message[0]["code"] == 34: + if exc.message[0]["code"] == 34: return await self.bot._(channel, "rss.nothing") - raise e + raise exc if not date: if len(posts) == 0: return [] lastpost = posts[0] text = html.unescape(getattr(lastpost, "full_text", lastpost.text)) - url = "https://twitter.com/{}/status/{}".format( - username.lower(), lastpost.id - ) + url = f"https://twitter.com/{username.lower()}/status/{lastpost.id}" img = None if lastpost.media: # if exists and is not empty img = lastpost.media[0].media_url_https - obj = self.rssMessage( + obj = self.RSSMessage( bot=self.bot, - Type="tw", + message_type="tw", url=url, title=text, date=datetime.datetime.fromtimestamp(lastpost.created_at_in_seconds), @@ -1215,13 +1216,13 @@ async def rss_tw( text = html.unescape(getattr(post, "full_text", post.text)) if r := re.search(r"https://t.co/([^\s]+)", text): text = text.replace(r.group(0), "") - url = "https://twitter.com/{}/status/{}".format(name.lower(), post.id) + url = f"https://twitter.com/{name.lower()}/status/{post.id}" img = None if post.media: # if exists and is not empty img = post.media[0].media_url_https - obj = self.rssMessage( + obj = self.RSSMessage( bot=self.bot, - Type="tw", + message_type="tw", url=url, title=text, date=datetime.datetime.fromtimestamp(post.created_at_in_seconds), @@ -1248,13 +1249,13 @@ async def rss_twitch( return await self.bot._(channel, "rss.nothing") if not date: feed = feeds.entries[0] - r = re.search(r'', feed["summary"]) + img_src = re.search(r'', feed["summary"]) img_url = None - if r is not None: - img_url = r.group(1) - obj = self.rssMessage( + if img_src is not None: + img_url = img_src.group(1) + obj = self.RSSMessage( bot=self.bot, - Type="twitch", + message_type="twitch", url=feed["link"], title=feed["title"], date=feed["published_parsed"], @@ -1270,13 +1271,13 @@ async def rss_twitch( break if datetime.datetime(*feed["published_parsed"][:6]) <= date: break - r = re.search(r'', feed["summary"]) + img_src = re.search(r'', feed["summary"]) img_url = None - if r is not None: - img_url = r.group(1) - obj = self.rssMessage( + if img_src is not None: + img_url = img_src.group(1) + obj = self.RSSMessage( bot=self.bot, - Type="twitch", + message_type="twitch", url=feed["link"], title=feed["title"], date=feed["published_parsed"], @@ -1321,11 +1322,11 @@ async def rss_web( else: datz = feed[published] if "link" in feed.keys(): - l = feed["link"] + link = feed["link"] elif "link" in feeds.keys(): - l = feeds["link"] + link = feeds["link"] else: - l = url + link = url if "author" in feed.keys(): author = feed["author"] elif "author" in feeds.keys(): @@ -1341,15 +1342,15 @@ async def rss_web( else: title = "?" img = None - r = re.search( + img_src = re.search( r"(http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png|webp)", str(feed) ) - if r is not None: - img = r.group(0) - obj = self.rssMessage( + if img_src is not None: + img = img_src.group(0) + obj = self.RSSMessage( bot=self.bot, - Type="web", - url=l, + message_type="web", + url=link, title=title, date=datz, author=author, @@ -1362,58 +1363,55 @@ async def rss_web( for feed in feeds.entries: if len(liste) > 10: break - try: - datz = feed[published] - if ( - feed[published] is None - or ( - datetime.datetime(*feed[published][:6]) - date - ).total_seconds() - < self.min_time_between_posts["web"] - ): - break - if "link" in feed.keys(): - l = feed["link"] - elif "link" in feeds.keys(): - l = feeds["link"] - else: - l = url - if "author" in feed.keys(): - author = feed["author"] - elif "author" in feeds.keys(): - author = feeds["author"] - elif "title" in feeds["feed"].keys(): - author = feeds["feed"]["title"] - else: - author = "?" - if "title" in feed.keys(): - title = feed["title"] - elif "title" in feeds.keys(): - title = feeds["title"] - else: - title = "?" - img = None - r = re.search( - r"(http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png|webp)", - str(feed), - ) - if r is not None: - img = r.group(0) - obj = self.rssMessage( - bot=self.bot, - Type="web", - url=l, - title=title, - date=datz, - author=author, - channel=feeds.feed["title"] - if "title" in feeds.feed.keys() - else "?", - image=img, - ) - liste.append(obj) - except BaseException: - pass + datz = feed[published] + if ( + feed[published] is None + or ( + datetime.datetime(*feed[published][:6]) - date + ).total_seconds() + < self.min_time_between_posts["web"] + ): + break + if "link" in feed.keys(): + link = feed["link"] + elif "link" in feeds.keys(): + link = feeds["link"] + else: + link = url + if "author" in feed.keys(): + author = feed["author"] + elif "author" in feeds.keys(): + author = feeds["author"] + elif "title" in feeds["feed"].keys(): + author = feeds["feed"]["title"] + else: + author = "?" + if "title" in feed.keys(): + title = feed["title"] + elif "title" in feeds.keys(): + title = feeds["title"] + else: + title = "?" + img = None + img_src = re.search( + r"(http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png|webp)", + str(feed), + ) + if img_src is not None: + img = img_src.group(0) + obj = self.RSSMessage( + bot=self.bot, + message_type="web", + url=link, + title=title, + date=datz, + author=author, + channel=feeds.feed["title"] + if "title" in feeds.feed.keys() + else "?", + image=img, + ) + liste.append(obj) liste.reverse() return liste @@ -1436,9 +1434,9 @@ async def rss_deviant( title = re.search( r"DeviantArt: ([^ ]+)'s gallery", feeds.feed["title"] ).group(1) - obj = self.rssMessage( + obj = self.RSSMessage( bot=self.bot, - Type="deviant", + message_type="deviant", url=feed["link"], title=feed["title"], date=feed["published_parsed"], @@ -1455,9 +1453,9 @@ async def rss_deviant( title = re.search( r"DeviantArt: ([^ ]+)'s gallery", feeds.feed["title"] ).group(1) - obj = self.rssMessage( + obj = self.RSSMessage( bot=self.bot, - Type="deviant", + message_type="deviant", url=feed["link"], title=feed["title"], date=feed["published_parsed"], @@ -1492,70 +1490,69 @@ async def transform_feed(self, data: dict) -> dict: ) return data - async def db_get_flow(self, ID: int): + async def db_get_flow(self, flow_id: int): query = f"SELECT rowid as ID, * FROM {self.table} WHERE `rowid`=?" - liste = self.bot.db_query(query, (ID,)) - for e in range(len(liste)): - liste[e] = await self.transform_feed(liste[e]) + liste = self.bot.db_query(query, (flow_id,)) + for index, value in enumerate(liste): + liste[index] = await self.transform_feed(value) return liste - async def db_get_guild_flows(self, guildID: int): + async def db_get_guild_flows(self, guild_id: int): """Get every flow of a guild""" query = f"SELECT rowid as ID, * FROM {self.table} WHERE `guild`=?" - liste = self.bot.db_query(query, (guildID,)) - for e in range(len(liste)): - liste[e] = await self.transform_feed(liste[e]) + liste = self.bot.db_query(query, (guild_id,)) + for index, value in enumerate(liste): + liste[index] = await self.transform_feed(value) return liste - async def db_add_flow(self, guildID: int, channelID: int, _type: str, link: str): + async def db_add_flow(self, guild_id: int, channel_id: int, _type: str, link: str): """Add a flow in the database""" if _type == "mc": form = "" else: - form = await self.bot._(guildID, "rss." + _type + "-default-flow") - query = "INSERT INTO `{}` (`guild`,`channel`,`type`,`link`,`structure`) VALUES (:g, :c, :t, :l, :f)".format( - self.table + form = await self.bot._(guild_id, "rss." + _type + "-default-flow") + query = f"INSERT INTO `{self.table}` (`guild`,`channel`,`type`,`link`,`structure`)"\ + "VALUES (:g, :c, :t, :l, :f)" + flow_id = self.bot.db_query( + query, {"g": guild_id, "c": channel_id, "t": _type, "l": link, "f": form} ) - ID = self.bot.db_query( - query, {"g": guildID, "c": channelID, "t": _type, "l": link, "f": form} - ) - return ID + return flow_id - async def db_remove_flow(self, ID: int): + async def db_remove_flow(self, flow_id: int): """Remove a flow from the database""" - if not isinstance(ID, int): + if not isinstance(flow_id, int): raise ValueError query = f"DELETE FROM {self.table} WHERE rowid=?" - self.bot.db_query(query, (ID,)) + self.bot.db_query(query, (flow_id,)) return True async def db_get_all_flows(self): """Get every flow of the database""" - query = "SELECT rowid as ID, * FROM `{}` WHERE `guild` in ({})".format( - self.table, ",".join(["'{}'".format(x.id) for x in self.bot.guilds]) + query = "SELECT rowid as ID, * FROM `{}` WHERE `guild` in ({})".format( # pylint: disable=consider-using-f-string + self.table, ",".join([f"'{x.id}'" for x in self.bot.guilds]) ) liste = self.bot.db_query(query, ()) - for e in range(len(liste)): - liste[e] = await self.transform_feed(liste[e]) + for index, value in enumerate(liste): + liste[index] = await self.transform_feed(value) return liste async def db_get_count(self, get_disabled: bool = False): """Get the number of rss feeds""" - query = "SELECT COUNT(*) FROM `{}`".format(self.table) + query = f"SELECT COUNT(*) FROM `{self.table}`" if not get_disabled: query += ( " WHERE `guild` in (" - + ",".join(["'{}'".format(x.id) for x in self.bot.guilds]) + + ",".join([f"'{x.id}'" for x in self.bot.guilds]) + ")" ) result = self.bot.db_query(query, (), fetchone=True) return result[0] - async def db_update_flow(self, ID: int, values=[(None, None)]): + async def db_update_flow(self, flow_id: int, values=[(None, None)]): # pylint: disable=dangerous-default-value """Update a flow in the database""" temp = ", ".join([f"{v[0]}=?" for v in values]) values = [v[1] for v in values] - query = f"UPDATE `{self.table}` SET {temp} WHERE rowid={ID}" + query = f"UPDATE `{self.table}` SET {temp} WHERE rowid={flow_id}" self.bot.db_query(query, values) async def send_rss_msg( @@ -1589,11 +1586,9 @@ async def send_rss_msg( if send_stats: if statscog := self.bot.get_cog("BotStats"): statscog.rss_stats["messages"] += 1 - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught self.bot.log.info( - "[send_rss_msg] Cannot send message on channel {}: {}".format( - channel.id, e - ) + f"[send_rss_msg] Cannot send message on channel {channel.id}: {exc}" ) async def check_flow( @@ -1601,7 +1596,7 @@ async def check_flow( ): try: guild = self.bot.get_guild(flow["guild"]) - if flow["link"] in self.cache.keys(): + if flow["link"] in self.cache: objs = self.cache[flow["link"]] else: funct = getattr(self, f"rss_{flow['type']}") @@ -1623,61 +1618,58 @@ async def check_flow( if isinstance(objs, (str, type(None), int)) or len(objs) == 0: return True elif isinstance(objs, list): - for o in objs: + for obj in objs: guild = self.bot.get_guild(flow["guild"]) if guild is None: self.bot.log.info( - "[send_rss_msg] Can not send message on server {} (unknown)".format( - flow["guild"] - ) + f"[send_rss_msg] Can not send message on server {flow['guild']}"\ + "(unknown)" ) return False chan = guild.get_channel(flow["channel"]) if guild is None: self.bot.log.info( - "[send_rss_msg] Can not send message on channel {} (unknown)".format( - flow["channel"] - ) + f"[send_rss_msg] Can not send message on channel {flow['channel']}"\ + "(unknown)" ) return False - o.format = flow["structure"] - o.embed = bool(flow["use_embed"]) - if o.embed: - o.fill_embed_data(flow) - await o.fill_mention(guild, flow["roles"], self.bot._) - await self.send_rss_msg(o, chan, flow["roles"], send_stats) + obj.format = flow["structure"] + obj.embed = bool(flow["use_embed"]) + if obj.embed: + obj.fill_embed_data(flow) + await obj.fill_mention(guild, flow["roles"], self.bot._) + await self.send_rss_msg(obj, chan, flow["roles"], send_stats) await self.db_update_flow( flow["ID"], - [("date", o.date)], + [("date", obj.date)], # pylint: disable=undefined-loop-variable ) return True else: return True - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught await self.bot.get_cog("Errors").senf_err_msg( - "Erreur rss sur le flux {} (type {} - salon {})".format( - flow["link"], flow["type"], flow["channel"] - ) + f"Erreur rss sur le flux {flow['link']} (type {flow['type']} -"\ + f"salon {flow['channel']})" ) - await self.bot.get_cog("Errors").on_error(e, None) + await self.bot.get_cog("Errors").on_error(exc, None) return False - async def main_loop(self, guildID: int = None): + async def main_loop(self, guild_id: int = None): if not self.config["rss_loop_enabled"]: return - t = time.time() + start = time.time() if self.loop_processing: return - if guildID is None: + if guild_id is None: self.bot.log.info("Check RSS lancé") self.loop_processing = True liste = await self.db_get_all_flows() else: - self.bot.log.info(f"Check RSS lancé pour le serveur {guildID}") - liste = await self.db_get_guild_flows(guildID) + self.bot.log.info(f"Check RSS lancé pour le serveur {guild_id}") + liste = await self.db_get_guild_flows(guild_id) check = 0 errors = [] - if guildID is None: + if guild_id is None: if statscog := self.bot.get_cog("BotStats"): statscog.rss_stats["messages"] = 0 session = ClientSession() @@ -1686,46 +1678,44 @@ async def main_loop(self, guildID: int = None): if flow["type"] == "tw" and self.twitter_over_capacity: continue if flow["type"] == "mc": - if MCcog := self.bot.get_cog("Minecraft"): - await MCcog.check_flow(flow, send_stats=(guildID is None)) + if minecraft_cog := self.bot.get_cog("Minecraft"): + await minecraft_cog.check_flow(flow, send_stats=guild_id is None) check += 1 else: if await self.check_flow( - flow, session, send_stats=(guildID is None) + flow, session, send_stats=(guild_id is None) ): check += 1 else: errors.append(flow["ID"]) - except Exception as e: - await self.bot.get_cog("Errors").on_error(e, None) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc, None) await asyncio.sleep(self.time_between_flows_check) await session.close() - if MCcog := self.bot.get_cog("Minecraft"): - MCcog.flows = dict() - d = [ - "**RSS loop done** in {}s ({}/{} flows)".format( - round(time.time() - t, 3), check, len(liste) - ) + if minecraft_cog := self.bot.get_cog("Minecraft"): + minecraft_cog.flows = dict() + done = [ + f"**RSS loop done** in {round(time.time() - start, 3)}s ({check}/{len(liste)} flows)" ] - if guildID is None: + if guild_id is None: if statscog := self.bot.get_cog("BotStats"): statscog.rss_stats["checked"] = check statscog.rss_stats["errors"] = len(errors) if len(errors) > 0: - d.append( - "{} errors: {}".format(len(errors), " ".join([str(x) for x in errors])) + done.append( + "{} errors: {}".format(len(errors), " ".join([str(x) for x in errors])) # pylint: disable=consider-using-f-string ) emb = discord.Embed( - description="\n".join(d), + description="\n".join(done), color=1655066, timestamp=datetime.datetime.utcnow(), ) emb.set_author(name=str(self.bot.user), icon_url=self.bot.user.display_avatar) # await self.bot.get_cog("Embeds").send([emb],url="loop") - self.bot.log.debug(d[0]) + self.bot.log.debug(done[0]) if len(errors) > 0: - self.bot.log.warn("[Rss loop] " + d[1]) - if guildID is None: + self.bot.log.warn("[Rss loop] " + done[1]) + if guild_id is None: self.loop_processing = False self.twitter_over_capacity = False self.cache = dict() @@ -1733,10 +1723,10 @@ async def main_loop(self, guildID: int = None): @tasks.loop(minutes=20) async def loop_child(self): self.bot.log.info(" Boucle rss commencée !") - t1 = time.time() + start = time.time() await self.bot.get_cog("Rss").main_loop() self.bot.log.info( - " Boucle rss terminée en {}s!".format(round(time.time() - t1, 2)) + f" Boucle rss terminée en {round(time.time() - start, 2)}s!" ) @loop_child.before_loop @@ -1751,13 +1741,13 @@ async def rss_loop_admin(self, ctx: MyContext, new_state: str = "start"): new_state can be start, stop or once""" if new_state == "start": try: - await self.loop_child.start() + await self.loop_child.start() # pylint: disable=no-member except RuntimeError: await ctx.send("La boucle est déjà en cours !") else: await ctx.send("Boucle rss relancée !") elif new_state == "stop": - await self.loop_child.cancel() + await self.loop_child.cancel() # pylint: disable=no-member self.bot.log.info(" Boucle rss arrêtée de force par un admin") await ctx.send("Boucle rss arrêtée de force !") elif new_state == "once": @@ -1772,7 +1762,7 @@ async def rss_loop_admin(self, ctx: MyContext, new_state: str = "start"): "Option `new_start` invalide - choisissez start, stop ou once" ) - async def send_log(self, text: str, guild: discord.Guild): + async def send_log(self, text: str, guild: discord.Guild): # pylint: disable=unused-argument """Send a log to the logging channel""" return # try: From afde1a476a0cb3d7cc260988e7c7651fb0822930 Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 08:21:45 +0000 Subject: [PATCH 024/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(thanks):=20fix=20lin?= =?UTF-8?q?ter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/thanks/thanks.py | 195 +++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 109 deletions(-) diff --git a/plugins/thanks/thanks.py b/plugins/thanks/thanks.py index 67b0080e..06d123c8 100644 --- a/plugins/thanks/thanks.py +++ b/plugins/thanks/thanks.py @@ -5,18 +5,15 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import args -from utils import Gunibot, MyContext -from discord.ext import commands -import discord -from bot import checks +from typing import Dict, List, Optional, Tuple import asyncio import datetime -from typing import Dict, List, Optional, Tuple -import sys +import discord +from discord.ext import commands -sys.path.append("./bot") +from bot import checks, args +from utils import Gunibot, MyContext class Thanks(commands.Cog): @@ -57,10 +54,10 @@ async def config_thanks_duration( ) return duration = None - x = await self.bot.sconfig.edit_config( + message = await self.bot.sconfig.edit_config( ctx.guild.id, "thanks_duration", duration ) - await ctx.send(x) + await ctx.send(message) @commands.group(name="thanks", aliases=["thx"], enabled=False) async def thanks_main(self, ctx: MyContext): @@ -73,23 +70,6 @@ async def thanks_list(self, ctx: MyContext): """List your current thanks levels""" await self.bot.get_cog("Thanks").thankslevels_list(ctx) - ''' - @thanks_main.command(name="add") - async def thanks_add(self, ctx: MyContext, amount: int, role: discord.Role): - """Add a role to give when someone reaches a certain amount of thanks""" - #await self.bot.get_cog("Thanks").thankslevel_add(ctx, amount, role) - pass - - @thanks_main.command(name="reset") - async def thanks_reset(self, ctx: MyContext, amount: int = None): - """Remove every role given for a certain amount of thanks - If no amount is specified, it will reset the whole configuration""" - if amount is None: - await self.bot.get_cog("Thanks").thankslevel_reset(ctx) - else: - await self.bot.get_cog("Thanks").thankslevel_remove(ctx, amount) - ''' - @commands.Cog.listener() async def on_ready(self): self.schedule_tasks() @@ -105,12 +85,12 @@ def schedule_tasks(self): delta = (task[2] - now).total_seconds() delta += self.bot.server_configs[task[0]]["thanks_duration"] if delta > 0: - T = self.bot.get_cog("TimeCog").add_task( + time_cog = self.bot.get_cog("TimeCog").add_task( delta, self.reload_roles, *task ) - self.tasks.append(T) + self.tasks.append(time_cog) - def cog_unload(self): + async def cog_unload(self): for task in self.tasks: task.cancel() if self.bot.get_cog("Sconfig"): @@ -121,71 +101,72 @@ async def _create_config( ) -> List[Tuple[str, str]]: """Create a list of (key,value) for the /config command""" roles: dict = self.db_get_roles(ctx.guild.id) - result = list() - for k, v in sorted(roles.items()): - subroles = [ctx.guild.get_role(r) for r in v] + result = [] + for key, value in sorted(roles.items()): + subroles = [ctx.guild.get_role(r) for r in value] if mentions: subroles = [r.mention for r in subroles if r is not None] else: subroles = [r.name for r in subroles if r is not None] - result.append((f"{k} remerciements", " ".join(subroles))) + result.append((f"{key} remerciements", " ".join(subroles))) return result - def db_get_user(self, guildID: int, userID: int) -> Optional[dict]: + def db_get_user(self, guild_id: int, user_id: int) -> Optional[dict]: query = "SELECT * FROM thanks WHERE guild=? AND user=?" - res = self.bot.db_query(query, (guildID, userID)) + res = self.bot.db_query(query, (guild_id, user_id)) return res if len(res) > 0 else None def db_get_last( - self, guildID: int, userID: int, authorID: int = None + self, guild_id: int, user_id: int, author_id: int = None ) -> Optional[dict]: - if authorID is None: - res = self.db_get_user(guildID, userID) + if author_id is None: + res = self.db_get_user(guild_id, user_id) else: query = "SELECT * FROM thanks WHERE guild=? AND user=? AND author=?" - res = self.bot.db_query(query, (guildID, userID, authorID)) + res = self.bot.db_query(query, (guild_id, user_id, author_id)) return res[-1] if len(res) > 0 else None - def db_get_amount(self, guildID: int, userID: int, duration: int = None) -> int: + def db_get_amount(self, guild_id: int, user_id: int, duration: int = None) -> int: query = "SELECT COUNT(*) as count FROM thanks WHERE guild=? AND user=?" if duration: query += f" AND timestamp >= datetime('now','-{duration} seconds')" - res = self.bot.db_query(query, (guildID, userID), fetchone=True) + res = self.bot.db_query(query, (guild_id, user_id), fetchone=True) return res["count"] - def db_add_thanks(self, guildID: int, userID: int, authorID: int): + def db_add_thanks(self, guild_id: int, user_id: int, author_id: int): query = "INSERT INTO thanks (guild,user,author) VALUES (?, ?, ?)" - self.bot.db_query(query, (guildID, userID, authorID)) + self.bot.db_query(query, (guild_id, user_id, author_id)) - def db_cleanup_thanks(self, guildID: int, duration: int): + def db_cleanup_thanks(self, guild_id: int, duration: int): if not isinstance(duration, (int, float)): return - query = f"DELETE FROM thanks WHERE guild=? AND timestamp < datetime('now','-{duration} seconds')" - self.bot.db_query(query, (guildID,)) + query = "DELETE FROM thanks WHERE guild=? AND"\ + f"timestamp < datetime('now','-{duration} seconds')" + self.bot.db_query(query, (guild_id,)) - def db_set_role(self, guildID: int, roleID: int, level: int): + def db_set_role(self, guild_id: int, role_id: int, level: int): query = "INSERT INTO thanks_levels (guild, role, level) VALUES (?, ?, ?)" - self.bot.db_query(query, (guildID, roleID, level)) + self.bot.db_query(query, (guild_id, role_id, level)) - def db_get_roles(self, guildID: int, level: int = None): + def db_get_roles(self, guild_id: int, level: int = None): if level: query = "SELECT role, level FROM thanks_levels WHERE guild=? AND level=?" - liste = self.bot.db_query(query, (guildID, level)) + liste = self.bot.db_query(query, (guild_id, level)) else: query = "SELECT role, level FROM thanks_levels WHERE guild=?" - liste = self.bot.db_query(query, (guildID,)) - res = dict() + liste = self.bot.db_query(query, (guild_id,)) + res = {} for lvl in liste: res[lvl["level"]] = res.get(lvl["level"], list()) + [lvl["role"]] return res - def db_remove_level(self, guildID: int, level: int): + def db_remove_level(self, guild_id: int, level: int): query = "DELETE FROM thanks_levels WHERE guild=? AND level=?" - self.bot.db_query(query, (guildID, level)) + self.bot.db_query(query, (guild_id, level)) - def db_reset_level(self, guildID: int): + def db_reset_level(self, guild_id: int): query = "DELETE FROM thanks_levels WHERE guild=?" - self.bot.db_query(query, (guildID,)) + self.bot.db_query(query, (guild_id,)) async def has_allowed_roles( self, guild: discord.Guild, member: discord.Member @@ -194,8 +175,8 @@ async def has_allowed_roles( if config is None: return False roles = [guild.get_role(x) for x in config] - for r in member.roles: - if r in roles: + for role in member.roles: + if role in roles: return True return False @@ -212,19 +193,19 @@ async def give_remove_roles( f'Module - Thanks: Missing "manage_roles" permission on guild "{member.guild.name}"' ) return False - g: discord.Guild = member.guild - pos: int = g.me.top_role.position + guild: discord.Guild = member.guild + pos: int = guild.me.top_role.position if roles_conf is None: - roles_conf = self.db_get_roles(g.id) - for k, v in roles_conf.items(): - if all(isinstance(x, discord.Role) for x in v): # roles already initialized + roles_conf = self.db_get_roles(guild.id) + for key, value in roles_conf.items(): + if all(isinstance(x, discord.Role) for x in value): # roles already initialized continue - r = [g.get_role(x) for x in v] - roles_conf[k] = list( - filter(lambda x: (x is not None) and (x.position < pos), r) + role = [guild.get_role(x) for x in value] + roles_conf[key] = list( + filter(lambda x: (x is not None) and (x.position < pos), role) ) - if len(roles_conf[k]) == 0: - del roles_conf[k] + if len(roles_conf[key]) == 0: + del roles_conf[key] if duration is None: duration = self.bot.server_configs[member.guild.id]["thanks_duration"] amount = self.db_get_amount(member.guild.id, member.id, duration) @@ -235,9 +216,7 @@ async def give_remove_roles( if len(roles) > 0: await member.add_roles(*roles, reason="Thanks system") self.bot.log.debug( - "[Thanks] Rôles {0} ajoutés à {1} ({1.id})".format( - roles, member - ) + f"[Thanks] Rôles {roles} ajoutés à {member} ({member.id})" ) gave_anything = True else: # should remove roles @@ -245,22 +224,20 @@ async def give_remove_roles( if len(roles) > 0: await member.remove_roles(*roles, reason="Thanks system") self.bot.log.debug( - "[Thanks] Rôles {0} enlevés à {1} ({1.id})".format( - roles, member - ) + f"[Thanks] Rôles {roles} enlevés à {member} ({member.id})" ) gave_anything = True return gave_anything - async def reload_roles(self, guildID: int, memberID: int, date: datetime.datetime): + async def reload_roles(self, guild_id: int, member_id: int, date: datetime.datetime): """Remove roles if needed""" - delta = self.bot.server_configs[guildID]["thanks_duration"] + delta = self.bot.server_configs[guild_id]["thanks_duration"] if (datetime.datetime.now() - date).total_seconds() < delta: return - guild: discord.Guild = self.bot.get_guild(guildID) + guild: discord.Guild = self.bot.get_guild(guild_id) if guild is None: return - member = guild.get_member(memberID) + member = guild.get_member(member_id) if member is None: return await self.give_remove_roles(member, duration=delta) @@ -294,21 +271,21 @@ async def thanks(self, ctx: MyContext, *, user: discord.User): await ctx.send( await self.bot._(ctx.guild.id, "thanks.add.done", user=user, amount=amount) ) - T = self.bot.get_cog("TimeCog").add_task( + time_cog = self.bot.get_cog("TimeCog").add_task( duration, self.reload_roles, ctx.guild.id, user.id, datetime.datetime.utcnow(), ) - self.tasks.append(T) + self.tasks.append(time_cog) member = ctx.guild.get_member(user.id) if member is not None: await self.give_remove_roles(member) @commands.command(name="thankslist", aliases=["thanks-list", "thxlist"]) @commands.guild_only() - async def thanks_list(self, ctx: MyContext, *, user: discord.User = None): + async def thankslist(self, ctx: MyContext, *, user: discord.User = None): """Get the list of thanks given to a user (or you by default)""" you = user is None if you: @@ -321,12 +298,12 @@ async def thanks_list(self, ctx: MyContext, *, user: discord.User = None): txt = await self.bot._(ctx.guild.id, "thanks.list.nothing-them") await ctx.send(txt) return - for e, l in enumerate(liste): - liste[e] = [ - self.bot.get_guild(l["guild"]), - self.bot.get_user(l["user"]), - self.bot.get_user(l["author"]), - datetime.datetime.strptime(l["timestamp"], "%Y-%m-%d %H:%M:%S"), + for index, value in enumerate(liste): + liste[index] = [ + self.bot.get_guild(value["guild"]), + self.bot.get_user(value["user"]), + self.bot.get_user(value["author"]), + datetime.datetime.strptime(value["timestamp"], "%Y-%m-%d %H:%M:%S"), ] duration = self.bot.server_configs[ctx.guild.id]["thanks_duration"] current = [ @@ -341,11 +318,11 @@ async def thanks_list(self, ctx: MyContext, *, user: discord.User = None): ctx.guild.id, "thanks.list.active", count=len(current) ) if len(current) > 0: - t = [ - "• {} ({})".format(x[2].mention, x[3].strftime("%d/%m/%y %HH%M")) - for x in current + users = [ + f"• {user[2].mention} ({user[3].strftime('%d/%m/%y %HH%M')})" + for user in current ] - emb.add_field(name=_active, value="\n".join(t)) + emb.add_field(name=_active, value="\n".join(users)) else: emb.add_field(name=_active, value="0") old = len(liste) - len(current) @@ -358,14 +335,14 @@ async def thanks_list(self, ctx: MyContext, *, user: discord.User = None): else: txt = "```md\n" if len(current) > 0: - t = [ - "- {} ({})".format(str(x[2]), x[3].strftime("%d/%m/%y %HH%M")) - for x in current + users = [ + f"- {str(user[2])} ({user[3].strftime('%d/%m/%y %HH%M')})" + for user in current ] _active = await self.bot._( ctx.guild.id, "thanks.list.active", count=len(current) ) - txt += "# " + _active + "\n{}\n".format(len(current), "\n".join(t)) + txt += "# " + _active + "\n{}\n".format("\n".join(users)) old = len(liste) - len(current) if old > 0: _inactive = await self.bot._( @@ -386,10 +363,10 @@ async def thanks_reload( if len(users) == 0: await ctx.send(await self.bot._(ctx.guild.id, "thanks.reload.no-member")) return - rolesID = self.db_get_roles(ctx.guild.id) + roles_id = self.db_get_roles(ctx.guild.id) roles = list() - for r in rolesID.values(): - roles += [ctx.guild.get_role(x) for x in r] + for role in roles_id.values(): + roles += [ctx.guild.get_role(x) for x in role] roles = list(filter(None, roles)) if not roles: await ctx.send(await self.bot._(ctx.guild.id, "thanks.reload.no-role")) @@ -400,8 +377,8 @@ async def thanks_reload( del roles delta = self.bot.server_configs[ctx.guild.id]["thanks_duration"] i = 0 - for m in users: - if await self.give_remove_roles(m, rolesID, delta): + for thanks_user in users: + if await self.give_remove_roles(thanks_user, roles_id, delta): i += 1 if i == 0: txt = await self.bot._( @@ -418,11 +395,11 @@ async def thanks_reload( async def thankslevels_list(self, ctx: MyContext): roles: dict = self.db_get_roles(ctx.guild.id) - async def g(k: int) -> str: + async def get_level(k: int) -> str: return await self.bot._(ctx.guild.id, "thanks.thanks", count=k) text = "\n".join( - [await g(k) + " ".join([f"<@&{r}>" for r in v]) for k, v in roles.items()] + [await get_level(k) + " ".join([f"<@&{r}>" for r in v]) for k, v in roles.items()] ) if text == "": text = await self.bot._(ctx.guild.id, "thanks.no-role") @@ -487,12 +464,12 @@ def check(reaction, user): else: await ctx.send(await self.bot._(ctx.guild.id, "thanks.went-wrong")) +async def setup(bot:Gunibot=None): + """ + Fonction d'initialisation du plugin -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): + :param bot: Le bot + :type bot: Gunibot + """ if bot is not None: await bot.add_cog(Thanks(bot), icon="❤️") - if plugin_config is not None: - global config - config.update(plugin_config) - From 6d2cdbac25d12e825d0cafa9188df96eb38850d1 Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 08:29:41 +0000 Subject: [PATCH 025/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(voice):=20fix=20lint?= =?UTF-8?q?er=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/voice/voice.py | 44 +++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/plugins/voice/voice.py b/plugins/voice/voice.py index 50b088cb..92024b21 100644 --- a/plugins/voice/voice.py +++ b/plugins/voice/voice.py @@ -112,12 +112,12 @@ async def give_roles(self, member: discord.Member, remove=False): f'Module - Voice: Missing "manage_roles" permission on guild "{member.guild.name}"' ) return - g = member.guild - rolesID = self.bot.server_configs[g.id]["voice_roles"] - if not rolesID: + member_guild = member.guild + roles_id = self.bot.server_configs[member_guild.id]["voice_roles"] + if not roles_id: return - roles = [g.get_role(x) for x in rolesID] - pos = g.me.top_role.position + roles = [member_guild.get_role(x) for x in roles_id] + pos = member_guild.me.top_role.position roles = filter(lambda x: (x is not None) and (x.position < pos), roles) if remove: await member.remove_roles(*roles, reason="Left the voice chat") @@ -148,7 +148,7 @@ async def on_voice_state_update( if ( before.channel is not None and len(before.channel.members) == 0 ): # move from another channel which is now empty - if (member.guild.id in self.channels.keys()) and ( + if (member.guild.id in self.channels) and ( before.channel.id in self.channels[member.guild.id] ): # if they come from an automated channel, we move them back @@ -158,7 +158,7 @@ async def on_voice_state_update( await self.create_channel(member, config) if ( (before.channel is not None) - and (member.guild.id in self.channels.keys()) + and (member.guild.id in self.channels) and (before.channel.id in self.channels[member.guild.id]) ): await self.delete_channel(before.channel) @@ -181,14 +181,15 @@ async def create_channel(self, member: discord.Member, config: dict): # S'il manque des perms au bot: abort if not (perms.manage_channels and perms.move_members): self.bot.log.info( - f'Module - Voice: Missing "manage_channels, move_members" permission on guild "{member.guild.name}"' + 'Module - Voice: Missing "manage_channels, move_members"'\ + f'permission on guild "{member.guild.name}"' ) return - p = len(voice_category.channels) + channels_len = len(voice_category.channels) # try to calculate the correct permissions - d = member.guild.me.guild_permissions - d = {k: v for k, v in dict(d).items() if v} - over = {member: discord.PermissionOverwrite(**d)} + guild_perms = member.guild.me.guild_permissions + guild_perms = {k: v for k, v in dict(guild_perms).items() if v} + over = {member: discord.PermissionOverwrite(**guild_perms)} # remove manage roles cuz DISCOOOOOOOOOOORD over[member].manage_roles = None # build channel name from config and random @@ -201,7 +202,7 @@ async def create_channel(self, member: discord.Member, config: dict): chan_name = chan_name.format_map(self.bot.SafeDict(args)) # actually create the channel new_channel = await voice_category.create_voice_channel( - name=chan_name, position=p, overwrites=over + name=chan_name, position=channels_len, overwrites=over ) # move user await member.move_to(new_channel) @@ -221,12 +222,15 @@ async def get_names(self, source="random"): # If we have some names in cache, we use one of them if len(self.names[source]) != 0: return self.names[source].pop() - - # If we don't have any names in cache, we get some new ones - randommer_api_key = self.config.get("randommer_api_key") - if source != "asterix" and randommer_api_key != '': - headers = {"X-Api-Key": randommer_api_key} - async with aiohttp.ClientSession() as session: + async with aiohttp.ClientSession() as session: + headers = {"X-Api-Key": self.bot.config["random_api_token"]} + if source == "asterix": + with open( + "plugins/voice/rsrc/asterix_names.txt", "r", encoding="utf-8" + ) as file: + self.names["asterix"] = file.readlines() + random.shuffle(self.names[source]) + else: async with session.get( "https://randommer.io/api/Name?nameType=surname&quantity=20", headers=headers, @@ -250,7 +254,7 @@ async def get_names(self, source="random"): async def voice_clean(self, ctx: commands.Context): """Delete every unusued voice channels previously generated by the bot""" if ( - ctx.guild.id not in self.channels.keys() + ctx.guild.id not in self.channels or len(self.channels[ctx.guild.id]) == 0 ): await ctx.send(await self.bot._(ctx.guild.id, "voices.no-channel")) From 5375e878a7ec904977ef9a0181d66fb0ab2b02cd Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 08:32:03 +0000 Subject: [PATCH 026/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(welcome):=20fix=20li?= =?UTF-8?q?nter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/welcome/welcome.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/plugins/welcome/welcome.py b/plugins/welcome/welcome.py index 3231dafe..d66b6496 100644 --- a/plugins/welcome/welcome.py +++ b/plugins/welcome/welcome.py @@ -30,26 +30,25 @@ async def config_welcome_roles( ) async def give_welcome_roles(self, member: discord.Member): - g = member.guild - config = self.bot.server_configs[g.id] - rolesID = config["welcome_roles"] - if not rolesID: # if nothing has been setup + config = self.bot.server_configs[member.guild.id] + roles_id = config["welcome_roles"] + if not roles_id: # if nothing has been setup return - roles = [g.get_role(x) for x in rolesID] - pos = g.me.top_role.position + roles = [member.guild.get_role(x) for x in roles_id] + pos = member.guild.me.top_role.position roles = filter(lambda x: (x is not None) and (x.position < pos), roles) await member.add_roles(*roles, reason="New members roles") @commands.Cog.listener() async def on_member_join(self, member: discord.Member): """Called when a member joins a guild""" - g = member.guild - if not g.me.guild_permissions.manage_roles: # if not allowed to manage roles + if not member.guild.me.guild_permissions.manage_roles: # if not allowed to manage roles self.bot.log.info( - f'Module - Welcome: Missing "manage_roles" permission on guild "{g.name}"' + 'Module - Welcome: Missing "manage_roles" permission'\ + f'on guild "{member.guild.name}"' ) return - if "MEMBER_VERIFICATION_GATE_ENABLED" not in g.features: + if "MEMBER_VERIFICATION_GATE_ENABLED" not in member.guild.features: # we give new members roles if the verification gate is disabled await self.give_welcome_roles(member) @@ -61,11 +60,6 @@ async def on_member_update(self, before: discord.Member, after: discord.Member): await self.give_welcome_roles(after) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(Welcome(bot), icon="👋") - if plugin_config is not None: - global config - config.update(plugin_config) - From f25ddfe1b552e112e343f5083848f7b79d02af28 Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 08:40:46 +0000 Subject: [PATCH 027/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(wormhole):=20fix=20l?= =?UTF-8?q?inter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/wormhole/wormhole.py | 112 ++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/plugins/wormhole/wormhole.py b/plugins/wormhole/wormhole.py index 0afc6275..6f4ba02c 100644 --- a/plugins/wormhole/wormhole.py +++ b/plugins/wormhole/wormhole.py @@ -5,19 +5,18 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import difflib -from aiohttp import ClientSession -from utils import Gunibot, MyContext -from bot import checks from typing import List, Union +import difflib + import discord from discord.ext import commands -import sys +from LRFutils import logs +from aiohttp import ClientSession -sys.path.append("./bot") +from utils import Gunibot, MyContext +from bot import checks -from LRFutils import logs # Check if a message is similar to another one with 80% similarity @@ -40,7 +39,7 @@ async def get_corresponding_answer( return None -async def sendMessage( +async def send_message( msg: discord.Message, webhook: discord.Webhook, username: str, @@ -108,7 +107,7 @@ def __init__(self, action: Union[str, int] = None): return self.name = self.types[self.type] - async def convert(self, ctx: commands.Context, argument: str): + async def convert(self, ctx: commands.Context, argument: str): # pylint: disable=unused-argument if argument in self.types: return PermissionType(argument) raise commands.errors.BadArgument("Unknown permission type") @@ -135,10 +134,10 @@ def to_str(self) -> str: class WormholeChannel: - def __init__(self, name: str, channelID: int, guildID: int, perms: str): - self.wh = name - self.channelID = channelID - self.guildID = guildID + def __init__(self, name: str, channel_id: int, guild_id: int, perms: str): + self.wormhole = name + self.channel_id = channel_id + self.guild_id = guild_id self.perms = perms def to_str(self) -> str: @@ -150,7 +149,8 @@ def to_str(self) -> str: if self.perms == "r" else "Write" ) - return f"Channel: <#{self.channelID}>\n┗━▷ Linked to **{self.wh}** - Permissions: *{perms}*" + return f"Channel: <#{self.channel_id}>\n┗━▷ Linked to"\ + f"**{self.wormhole}** - Permissions: *{perms}*" class Wormholes(commands.Cog): @@ -169,18 +169,18 @@ def db_get_wormholes(self) -> List[Wormhole]: owners = self.bot.db_query(query, (row[1],), astuple=True) # come as: (rowid, name, admin) owner_list: List[int] = [] - for o in owners: - owner_list.append(o[2]) + for owner in owners: + owner_list.append(owner[2]) query = "SELECT * FROM wormhole_channel WHERE name = ?" channels = len(self.bot.db_query(query, (row[1],), astuple=True)) res.append(Wormhole(*row[1:3], owner_list, self.bot, channels)) res[-1].id = row[0] return res if len(res) > 0 else None - def db_get_channels(self, guildID: int): + def db_get_channels(self, guild_id: int): """Get every channel linked to a wormhole in this channel""" query = "SELECT rowid, * FROM wormhole_channel WHERE guildID = ?" - channels = self.bot.db_query(query, (guildID,), astuple=True) + channels = self.bot.db_query(query, (guild_id,), astuple=True) # come as: (rowid, name, channelID, guildID, type, webhookID, # webhookTOKEN) res: List[WormholeChannel] = [] @@ -201,7 +201,7 @@ def check_wh_exists(self, wormhole: str): query_res = self.bot.db_query(query, (wormhole,), astuple=True) # comes as: (name, privacy, webhook_name, webhook_pp_guild) return len(query_res) > 0 - + async def update_webhook( self, channel: Union[discord.TextChannel, discord.Thread], @@ -218,7 +218,8 @@ async def update_webhook( new_webhook: discord.Webhook = await channel.create_webhook( name=wormhole_name, ) - query = "UPDATE wormhole_channel SET webhookID=?, webhookTOKEN=? WHERE name=? AND channelID=?;" + query = "UPDATE wormhole_channel SET webhookID=?,"\ + "webhookTOKEN=? WHERE name=? AND channelID=?;" self.bot.db_query( query, @@ -245,13 +246,14 @@ async def on_message_delete(self, message): if "w" not in wh_channel[1]: return # Check if the current channel as Write permission wh_name = wh_channel[0] - query = "SELECT * FROM wormhole_channel WHERE name = ? AND type LIKE '%r%' AND NOT channelID = ?" + query = "SELECT * FROM wormhole_channel WHERE name = ? AND type"\ + "LIKE '%r%' AND NOT channelID = ?" wh_targets = self.bot.db_query( query, (wh_name, message.channel.id), astuple=True ) # come as: (name, channelID, guildID, type, webhookID, webhookTOKEN) query = "SELECT webhook_name, webhook_pp FROM wormhole_list WHERE name = ?" - wormhole = self.bot.db_query(query, (wh_name,), astuple=True, fetchone=True) + # come as: (webhook_name, webhook_pp) async with ClientSession() as session: for row in wh_targets: @@ -262,7 +264,8 @@ async def on_message_delete(self, message): webhook = discord.Webhook.partial(row[4], row[5], session=session) oldmessage = await get_corresponding_answer(channel, message) if oldmessage: - # The webhook try to delete the message (will work only if the message belong to the webhook) + # The webhook try to delete the message (will work only if the message + # belong to the webhook) try: await webhook.delete_message(oldmessage.id) except (discord.errors.NotFound, discord.errors.Forbidden): @@ -299,13 +302,14 @@ async def on_message_edit(self, message:discord.Message, newmessage:discord.Mess if message.author.id == wh_local[4]: # sender id is the webhook used here return - query = "SELECT * FROM wormhole_channel WHERE name = ? AND type LIKE '%r%' AND NOT channelID = ?" + query = "SELECT * FROM wormhole_channel WHERE name = ? AND type LIKE"\ + "'%r%' AND NOT channelID = ?" wh_targets = self.bot.db_query( query, (wh_name, message.channel.id), astuple=True ) # come as: (name, channelID, guildID, type, webhookID, webhookTOKEN) query = "SELECT webhook_name, webhook_pp FROM wormhole_list WHERE name = ?" - wormhole = self.bot.db_query(query, (wh_name,), astuple=True, fetchone=True) + # come as: (webhook_name, webhook_pp) async with ClientSession() as session: for row in wh_targets: @@ -370,11 +374,14 @@ async def on_message_edit(self, message:discord.Message, newmessage:discord.Mess ) except discord.NotFound: # the webhook has been deleted logs.info( - f"The webhook for channel {row[1]} for wormhole {wh_name} has been deleted and a message has not been edited." + f"The webhook for channel {row[1]} for wormhole {wh_name} has"\ + "been deleted and a message has not been edited." ) - except discord.Forbidden: # the webhook has changed, should not be possible due to checks before + except discord.Forbidden: # the webhook has changed, should not be possible due + # to checks before logs.info( - f"The webhook for channel {row[1]} for wormhole {wh_name} has changed and a message has not been edited." + f"The webhook for channel {row[1]} for wormhole {wh_name} has changed"\ + "and a message has not been edited." ) @@ -410,7 +417,8 @@ async def on_message(self, message: discord.Message): return # Getting all the other channels linked to the wormhole - query = "SELECT * FROM wormhole_channel WHERE name = ? AND type LIKE '%r%' AND NOT channelID = ?" + query = "SELECT * FROM wormhole_channel WHERE name = ? AND type LIKE '%r%' AND NOT"\ + "channelID = ?" wh_targets = self.bot.db_query( query, (wh_name, message.channel.id), astuple=True ) @@ -423,11 +431,17 @@ async def on_message(self, message: discord.Message): # We're starting to send the message in all the channels linked to that wormhole for connected_channel in wh_targets: - channel: Union[discord.TextChannel, discord.Thread] = self.bot.get_channel(connected_channel[1]) + channel: Union[discord.TextChannel, discord.Thread] = self.bot.get_channel( + connected_channel[1], + ) if channel: # Get the webhook associated to the wormhole - webhook = discord.Webhook.partial(connected_channel[4], connected_channel[5], session=session) + webhook = discord.Webhook.partial( + connected_channel[4], + connected_channel[5], + session=session, + ) embed_reply = None if message.reference is not None: @@ -458,9 +472,9 @@ async def on_message(self, message: discord.Message): ).set_footer( text=content, icon_url=reply.author.display_avatar ) - + try: - await sendMessage( + await send_message( message, webhook, wormhole[0], @@ -474,7 +488,7 @@ async def on_message(self, message: discord.Message): wh_name, ) - await sendMessage( + await send_message( message, new_webhook, wormhole[0], @@ -517,7 +531,8 @@ async def add( ) ) return - query = "INSERT INTO wormhole_list (name, privacy, webhook_name, webhook_pp) VALUES (?, ?, ?, ?)" + query = "INSERT INTO wormhole_list (name, privacy, webhook_name, webhook_pp)"\ + "VALUES (?, ?, ?, ?)" self.bot.db_query(query, (name, privacy, webhook_name, webhook_pp_guild)) query = "INSERT INTO wormhole_admin (name, admin) VALUES (?,?)" self.bot.db_query(query, (name, ctx.author.id)) @@ -560,7 +575,9 @@ async def link( await self.bot._(ctx.guild.id, "wormhole.error.not-admin") ) return - query = "INSERT INTO wormhole_channel (name, channelID, guildID, type, webhookID, webhookTOKEN) VALUES (?, ?, ?, ?, ?, ?)" + query = "INSERT INTO wormhole_channel"\ + "(name, channelID, guildID, type, webhookID, webhookTOKEN)"\ + "VALUES (?, ?, ?, ?, ?, ?)" if isinstance(ctx.channel, discord.Thread): webhook: discord.Webhook = await ctx.channel.parent.create_webhook(name=wormhole) @@ -667,7 +684,9 @@ async def modify_webhook_name( For example: "!wh modify MyWH webhook_name {user} from {guild}" - If fantomitechno send a message in a Gunivers channel linked to the wormhole "MyWH", the other connected channels will see the message from a webhook called "fantomitechno from Gunivers". + If fantomitechno send a message in a Gunivers channel linked to the wormhole "MyWH", + the other connected channels will see the message from a webhook called + "fantomitechno from Gunivers". """ if not self.check_wh_exists(wormhole): @@ -722,8 +741,8 @@ async def admin_add(self, ctx: MyContext, wormhole: str, user: discord.User): await ctx.send(await self.bot._(ctx.guild.id, "wormhole.error.not-admin")) return query = "SELECT 1 FROM wormhole_admin WHERE name = ? AND admin = ?" - isAlready = len(self.bot.db_query(query, (wormhole, user.id))) > 0 - if not isAlready: + is_alread = len(self.bot.db_query(query, (wormhole, user.id))) > 0 + if not is_alread: query = "INSERT INTO wormhole_admin (name, admin) VALUES (?, ?)" self.bot.db_query(query, (wormhole, user.id)) await ctx.send( @@ -750,8 +769,8 @@ async def admin_remove(self, ctx: MyContext, wormhole: str, user: discord.User): await ctx.send(await self.bot._(ctx.guild.id, "wormhole.error.not-admin")) return query = "SELECT 1 FROM wormhole_admin WHERE name = ? AND admin = ?" - isAlready = len(self.bot.db_query(query, (wormhole, user.id))) > 0 - if isAlready: + is_already = len(self.bot.db_query(query, (wormhole, user.id))) > 0 + if is_already: query = "DELETE FROM wormhole_admin WHERE admin = ? AND name = ?" self.bot.db_query(query, (user.id, wormhole)) await ctx.send( @@ -781,7 +800,7 @@ async def list_wh(self, ctx: MyContext): ) ) return - txt = "\n".join([w.to_str() for w in wormholes]) + txt = "\n".join([w.to_str() for w in wormholes]) # pylint: disable=not-an-iterable await ctx.send(txt) @list.command(name="channel") @@ -795,15 +814,10 @@ async def list_channel(self, ctx: MyContext): ) ) return - txt = "\n".join([c.to_str() for c in channels]) + txt = "\n".join([c.to_str() for c in channels]) # pylint: disable=not-an-iterable await ctx.send(txt) -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(Wormholes(bot), icon="🌀") - if plugin_config is not None: - global config - config.update(plugin_config) - From 08f5fd1ae8b16ef04e4bcef3ab4d3f34fb92d30c Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 09:03:56 +0000 Subject: [PATCH 028/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(xp):=20fix=20linter?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/xp/xp.py | 350 +++++++++++++++++++++++++---------------------- 1 file changed, 189 insertions(+), 161 deletions(-) diff --git a/plugins/xp/xp.py b/plugins/xp/xp.py index 76476c94..621f27c4 100644 --- a/plugins/xp/xp.py +++ b/plugins/xp/xp.py @@ -5,19 +5,19 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ +from typing import Dict, List, Tuple, Union import asyncio import re import string import time import typing from math import ceil -from typing import Dict, List, Tuple, Union import discord import emoji from discord.ext import commands, tasks + from utils import Gunibot, MyContext -import distutils class XP(commands.Cog): @@ -50,7 +50,7 @@ def __init__(self, bot: Gunibot): bot.get_command("config").add_command(self.config_levelup_reaction) bot.get_command("config").add_command(self.config_levelup_reaction_emoji) - self.xp_reduction.start() + self.xp_reduction.start() # pylint: disable=no-member @commands.command(name="enable_xp") async def config_enable_xp(self, ctx: MyContext, value: bool): @@ -68,9 +68,9 @@ async def config_noxp_channels( channels = None else: channels = [channel.id for channel in channels] - x = await self.bot.sconfig.edit_config(ctx.guild.id, "noxp_channels", channels) + noxp_channels = await self.bot.sconfig.edit_config(ctx.guild.id, "noxp_channels", channels) self.xp_channels_cache[ctx.guild.id] = channels if channels is not None else [] - await ctx.send(x) + await ctx.send(noxp_channels) @commands.command(name="xp_reduction") async def config_xp_reduction(self, ctx: MyContext, enabled:bool): @@ -105,11 +105,11 @@ async def config_levelup_message(self, ctx: MyContext, *, message=None): ) @commands.command(name="levelup_reaction") - async def config_levelup_reaction(self, ctx: MyContext, *, bool: bool = None): + async def config_levelup_reaction(self, ctx: MyContext, *, enabled: bool = None): """If the bot add a reaction to the message or send a message Set to True for the reaction, False for the message""" await ctx.send( - await self.bot.sconfig.edit_config(ctx.guild.id, "levelup_reaction", bool) + await self.bot.sconfig.edit_config(ctx.guild.id, "levelup_reaction", enabled) ) @commands.command(name="reaction_emoji") @@ -139,23 +139,23 @@ async def _create_config( ) -> List[Tuple[str, str]]: """Create a list of (key,value) for the /config command""" roles = await self.rr_list_role(ctx.guild.id) - sorted_dict = dict() - for r in roles: - if role := ctx.guild.get_role(r["role"]): - if r["level"] in sorted_dict: - sorted_dict[r["level"]].append(role) + sorted_dict = {} + for role in roles: + if role := ctx.guild.get_role(role["role"]): + if role["level"] in sorted_dict: + sorted_dict[role["level"]].append(role) else: - sorted_dict[r["level"]] = [role] + sorted_dict[role["level"]] = [role] if len(sorted_dict) == 0: - return list() + return [] _lvl = await self.bot._(ctx.guild.id, "xp.card.level") - result = list() - for k, v in sorted(sorted_dict.items()): + result = [] + for key, value in sorted(sorted_dict.items()): if mentions: - subroles = [r.mention for r in v] + subroles = [r.mention for r in value] else: - subroles = [r.name for r in v] - result.append((f"{_lvl} {k}", " ".join(subroles))) + subroles = [r.name for r in value] + result.append((f"{_lvl} {key}", " ".join(subroles))) return result @@ -166,18 +166,34 @@ async def _create_config( @tasks.loop(hours=24*7) async def xp_reduction(self): """Reduce the xp of all members each week""" - + # Compute the XP to remove each week - xp_to_remove = await self.calc_xp(f"Vous savez, moi je ne crois pas qu’il y ait de bonne ou de mauvaise situation. Moi, si je devais résumer ma vie aujourd’hui avec vous, je dirais que c’est d’abord des rencontres. Des gens qui m’ont tendu la main, peut-être à un moment où je ne pouvais pas, où j’étais seul chez moi. Et c’est assez curieux de se dire que les hasards, les rencontres forgent une destinée... Parce que quand on a le goût de la chose, quand on a le goût de la chose bien faite, le beau geste, parfois on ne trouve pas l’interlocuteur en face je dirais, le miroir qui vous aide à avancer. Alors ça n’est pas mon cas, comme je disais là, puisque moi au contraire, j’ai pu ; et je dis merci à la vie, je lui dis merci, je chante la vie, je danse la vie... je ne suis qu’amour ! Et finalement, quand des gens me disent « Mais comment fais-tu pour avoir cette humanité ? », je leur réponds très simplement que c’est ce goût de l’amour, ce goût donc qui m’a poussé aujourd’hui à entreprendre une construction mécanique... mais demain qui sait ? Peut-être simplement à me mettre au service de la communauté, à faire le don, le don de soi.") - + xp_to_remove = await self.calc_xp("""Vous savez, moi je ne crois pas qu’il y ait de bonne ou +de mauvaise situation. Moi, si je devais résumer ma vie aujourd’hui avec vous, je dirais que c’est +d’abord des rencontres. Des gens qui m’ont tendu la main, peut-être à un moment où je ne pouvais +pas, où j’étais seul chez moi. Et c’est assez curieux de se dire que les hasards, les rencontres +forgent une destinée... Parce que quand on a le goût de la chose, quand on a le goût de la chose +bien faite, le beau geste, parfois on ne trouve pas l’interlocuteur en face je dirais, le miroir qui +vous aide à avancer. Alors ça n’est pas mon cas, comme je disais là, puisque moi au contraire, j’ai +pu ; et je dis merci à la vie, je lui dis merci, je chante la vie, je danse la vie... je ne suis +qu’amour ! Et finalement, quand des gens me disent « Mais comment fais-tu pour avoir cette +humanité ? », je leur réponds très simplement que c’est ce goût de l’amour, ce goût donc qui m’a +poussé aujourd’hui à entreprendre une construction mécanique... mais demain qui sait ? Peut-être +simplement à me mettre au service de la communauté, à faire le don, le don de soi.""") + # xp_to_remove *= 1 for guild in self.bot.guilds: if self.bot.server_configs[guild.id]["xp_reduction"]: for member in guild.members: - await self.bdd_set_xp(userID=member.id, points=xp_to_remove, Type="remove", guild=guild.id) + await self.bdd_set_xp( + user_id=member.id, + points=xp_to_remove, + action_type="remove", + guild=guild.id, + ) + + - - async def get_lvlup_chan(self, msg: discord.Message): value = self.bot.server_configs[msg.guild.id]["levelup_channel"] if value is None or value == "none": @@ -223,8 +239,8 @@ async def check_noxp(self, message: discord.Message): async def check_cmd(self, msg: discord.Message): """Checks if a message is a command""" - pr = await self.bot.get_prefix(msg) - return any([msg.content.startswith(p) for p in pr]) + prefix = await self.bot.get_prefix(msg) + return any([msg.content.startswith(p) for p in prefix]) async def check_spam(self, text: str): """Checks if a text contains spam""" @@ -233,16 +249,16 @@ async def check_spam(self, text: str): text[0] in string.punctuation or text[1] in string.punctuation ): return True - d = dict() + chars = {} # count frequency of letters in the message - for c in text: - if c in d: - d[c] += 1 + for char in text: + if char in chars: + chars[char] += 1 else: - d[c] = 1 - for v in d.values(): + chars[char] = 1 + for value in chars.values(): # if frequency is too high: spam detected - if v / len(text) > self.spam_rate: + if value / len(text) > self.spam_rate: return True return False @@ -281,15 +297,12 @@ async def add_xp(self, msg: discord.Message): if msg.author.id in self.cache[msg.guild.id].keys(): prev_points = self.cache[msg.guild.id][msg.author.id][1] else: - try: - # we check in the database for the previous xp - prev_points = await self.bdd_get_xp(msg.author.id, msg.guild.id) - if len(prev_points) > 0: - prev_points = prev_points[0]["xp"] - else: - # if user not in database, it's their first message - prev_points = 0 - except BaseException: + # we check in the database for the previous xp + prev_points = await self.bdd_get_xp(msg.author.id, msg.guild.id) + if len(prev_points) > 0: + prev_points = prev_points[0]["xp"] + else: + # if user not in database, it's their first message prev_points = 0 # we update database with the new xp amount await self.bdd_set_xp(msg.author.id, giv_points, "add", msg.guild.id) @@ -325,7 +338,7 @@ async def calc_xp(self, msg: discord.Message | str): content = content.replace(match.group(0), "") return min(round(len(content) * self.xp_per_char), self.max_xp_per_msg) - async def calc_level(self, xp: int): + async def calc_level(self, xp: int): # pylint: disable=invalid-name """Calculates the level corresponding to a xp amount Returns: Current level, Total XP for the next level, Total XP for the current level @@ -391,7 +404,7 @@ async def give_rr( """Give (and remove?) roles rewards to a member rr_list is a list of dictionnaries containing level and role id put remove as True if you want to remove unneeded roles rewards too""" - c = 0 + counter = 0 # List of roles IDs owned by user has_roles = [x.id for x in member.roles] # for each role that should be given and not already owned by user @@ -399,82 +412,86 @@ async def give_rr( x for x in rr_list if x["level"] <= level and x["role"] not in has_roles ]: try: - r = member.guild.get_role(role["role"]) - if r is None: + member_role = member.guild.get_role(role["role"]) + if member_role is None: continue # finally add the role, with a reason await member.add_roles( - r, reason="Role reward (lvl {})".format(role["level"]) + member_role, reason=f"Role reward (lvl {role['level']})" ) - c += 1 - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) + counter += 1 + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) # if we don't have to remove roles: stop if not remove: - return c + return counter # for each role that should be removed and owned by user for role in [ x for x in rr_list if x["level"] > level and x["role"] in has_roles ]: try: - r = member.guild.get_role(role["role"]) - if r is None: + member_role = member.guild.get_role(role["role"]) + if member_role is None: continue # finally remove the role, with a reason await member.remove_roles( - r, reason="Role reward (lvl {})".format(role["level"]) + member_role, reason=f"Role reward (lvl {role['level']})" ) - c += 1 - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) - return c + counter += 1 + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) + return counter async def bdd_set_xp( - self, userID: int, points: int, Type: str = "add", guild: int = None + self, user_id: int, points: int, action_type: str = "add", guild: int = None ): """Add/reset xp to a user in the database Set guild=None for global leaderboard""" try: try: - xp = await self.bdd_get_xp(userID, guild) - xp = xp[0]["xp"] + xp = await self.bdd_get_xp(user_id, guild) # pylint: disable=invalid-name + xp = xp[0]["xp"] # pylint: disable=invalid-name except IndexError: - xp = 0 + xp = 0 # pylint: disable=invalid-name if points < 0: raise ValueError("You cannot add nor set negative xp") - if Type == "add": - query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p) ON CONFLICT(guild, userid) DO UPDATE SET xp = (xp + :p);" - elif Type == "remove": + if action_type == "add": + query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p)"\ + "ON CONFLICT(guild, userid) DO UPDATE SET xp = (xp + :p);" + elif action_type == "remove": if xp < points: - query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p) ON CONFLICT(guild, userid) DO UPDATE SET xp = 0;" + query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES"\ + "(:g, :u, :p) ON CONFLICT(guild, userid) DO UPDATE SET xp = 0;" else: - query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p) ON CONFLICT(guild, userid) DO UPDATE SET xp = (xp - :p);" + query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p)"\ + "ON CONFLICT(guild, userid) DO UPDATE SET xp = (xp - :p);" else: - query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p) ON CONFLICT(guild, userid) DO UPDATE SET xp = :p;" - - self.bot.db_query(query, {"g": guild, "u": userID, "p": points}) + query = "INSERT INTO xp (`guild`, `userid`,`xp`) VALUES (:g, :u, :p)"\ + "ON CONFLICT(guild, userid) DO UPDATE SET xp = :p;" + + self.bot.db_query(query, {"g": guild, "u": user_id, "p": points}) return True - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) return False - async def bdd_get_xp(self, userID: int, guild: int = None): + async def bdd_get_xp(self, user_id: int, guild: int = None): """Get the xp amount of a user in a guild Set guild=None for global leaderboard""" try: query = "SELECT `xp` FROM `xp` WHERE `userid` = :u AND `guild` = :g" - liste = self.bot.db_query(query, {"u": userID, "g": guild}) + liste = self.bot.db_query(query, {"u": user_id, "g": guild}) if len(liste) == 1: - if userID in self.cache[guild].keys(): - self.cache[guild][userID][1] = liste[0]["xp"] + if user_id in self.cache[guild].keys(): + self.cache[guild][user_id][1] = liste[0]["xp"] else: - self.cache[guild][userID] = [ + self.cache[guild][user_id] = [ round(time.time()) - 60, liste[0]["xp"], ] return liste - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) return list() async def bdd_get_nber(self, guild: int = None): @@ -486,45 +503,47 @@ async def bdd_get_nber(self, guild: int = None): if liste is not None and len(liste) == 1: return liste[0]["count"] return 0 - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) return 0 async def bdd_load_cache(self, guild: int = None): """Load the xp cache for a specific guild Set guild=None for global leaderboard""" try: - self.bot.log.info("Loading XP cache (guild {})".format(guild)) + self.bot.log.info(f"Loading XP cache (guild {guild})") query = "SELECT `userid`,`xp` FROM xp WHERE `guild` = ?" liste = self.bot.db_query(query, (guild,)) - if guild not in self.cache.keys(): - self.cache[guild] = dict() - for l in liste: - self.cache[guild][l["userid"]] = [round(time.time()) - 60, int(l["xp"])] - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) - - async def bdd_get_rank(self, userID: int, guild: discord.Guild = None): + if guild not in self.cache: + self.cache[guild] = {} + for user_data in liste: + self.cache[guild][user_data["userid"]] = [ + round(time.time()) - 60, int(user_data["xp"]) + ] + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) + + async def bdd_get_rank(self, user_id: int, guild: discord.Guild = None): """Get the rank of a user Set guild=None for global leaderboard""" try: - query = f"SELECT `userid`,`xp` FROM xp WHERE guild = ? ORDER BY xp desc;" + query = "SELECT `userid`,`xp` FROM xp WHERE guild = ? ORDER BY xp desc;" liste = self.bot.db_query(query, (guild.id if guild else None,)) - userdata = dict() + userdata = {} i = 0 - users = list() + users = [] if guild is not None: users = [x.id for x in guild.members] - for x in liste: - if guild is None or (guild is not None and x["userid"] in users): + for user_data in liste: + if guild is None or (guild is not None and user_data["userid"] in users): i += 1 - if x["userid"] == userID: - userdata = dict(x) + if user_data["userid"] == user_id: + userdata = dict(user_data) userdata["rank"] = i break return userdata - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) async def bdd_get_top(self, top: int = None, guild: discord.Guild = None): """""" @@ -533,44 +552,57 @@ async def bdd_get_top(self, top: int = None, guild: discord.Guild = None): if top is not None: query += f" LIMIT {top}" return self.bot.db_query(query, (guild.id if guild else None,)) - except Exception as e: - await self.bot.get_cog("Errors").on_error(e) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc) async def get_xp(self, user: discord.User, guild_id: int = None): """Get the xp amount of a user in a guild""" - xp = await self.bdd_get_xp(user.id, guild_id) + xp = await self.bdd_get_xp(user.id, guild_id) # pylint: disable=invalid-name if xp is None or (isinstance(xp, list) and len(xp) == 0): return return xp[0]["xp"] async def send_embed( - self, ctx: MyContext, user: discord.User, xp, rank, ranks_nb, levels_info + self, + ctx: MyContext, + user: discord.User, + xp, # pylint: disable=invalid-name + rank, + ranks_nb, + levels_info, ): """Send the !rank command as an embed""" - LEVEL = await self.bot._(ctx.channel, "xp.card.level") - RANK = await self.bot._(ctx.channel, "xp.card.rank") + level_field_name = await self.bot._(ctx.channel, "xp.card.level") + rank_field_name = await self.bot._(ctx.channel, "xp.card.rank") + if levels_info is None: levels_info = await self.calc_level(xp) emb = discord.Embed(color=self.embed_color) emb.set_author(name=str(user), icon_url=user.display_avatar) emb.add_field(name="XP", value=f"{xp}/{levels_info[1]}") - emb.add_field(name=LEVEL, value=levels_info[0]) - emb.add_field(name=RANK, value=f"{rank}/{ranks_nb}") + emb.add_field(name=level_field_name, value=levels_info[0]) + emb.add_field(name=rank_field_name, value=f"{rank}/{ranks_nb}") await ctx.send(embed=emb) async def send_txt( - self, ctx: MyContext, user: discord.User, xp, rank, ranks_nb, levels_info + self, + ctx: MyContext, + user: discord.User, + xp, # pylint: disable=invalid-name + rank, + ranks_nb, + levels_info, ): """Send the !rank command as a plain text""" - LEVEL = await self.bot._(ctx.channel, "xp.card.level") - RANK = await self.bot._(ctx.channel, "xp.card.rank") + level_field_name = await self.bot._(ctx.channel, "xp.card.level") + rank_field_name = await self.bot._(ctx.channel, "xp.card.rank") if levels_info is None: levels_info = await self.calc_level(xp) msg = f"""__**{user.name}**__ **XP** {xp}/{levels_info[1]} -**{LEVEL}** {levels_info[0]} -**{RANK}** {rank}/{ranks_nb}""" +**{level_field_name}** {levels_info[0]} +**{rank_field_name}** {rank}/{ranks_nb}""" await ctx.send(msg) @commands.command(name="rank") @@ -591,7 +623,7 @@ async def rank(self, ctx: MyContext, *, user: discord.User = None): # if guild cache not done yet if ctx.guild.id not in self.cache: await self.bdd_load_cache(ctx.guild.id) - xp = await self.get_xp(user, ctx.guild.id) + xp = await self.get_xp(user, ctx.guild.id) # pylint: disable=invalid-name if xp is None: if ctx.author == user: return await ctx.send(await self.bot._(ctx.channel, "xp.no-xp-author")) @@ -616,14 +648,14 @@ async def create_top_main( ranks: data pulled from the database nbr: number of users to show page: page number to show""" - txt = list() + txt = [] i = (page - 1) * 20 - for u in ranks[:nbr]: + for user_data in ranks[:nbr]: i += 1 - user = self.bot.get_user(u["userid"]) + user = self.bot.get_user(user_data["userid"]) if user is None: try: - user = await self.bot.fetch_user(u["userid"]) + user = await self.bot.fetch_user(user_data["userid"]) except discord.NotFound: user = await self.bot._(ctx.channel, "xp.del-user") if isinstance(user, discord.User): @@ -632,13 +664,13 @@ async def create_top_main( user_name = user_name[:15] + "..." else: user_name = user - l = await self.calc_level(u["xp"]) + level_data = await self.calc_level(user_data["xp"]) txt.append( - "{} • **{} |** `lvl {}` **|** `xp {}`".format( + "{} • **{} |** `lvl {}` **|** `xp {}`".format( # pylint: disable=consider-using-f-string i, "__" + user_name + "__" if user == ctx.author else user_name, - l[0], - u["xp"], + level_data[0], + user_data["xp"], ) ) return txt, i @@ -700,15 +732,15 @@ async def top(self, ctx: MyContext, page: typing.Optional[int] = 1): lvl = lvl[0] your_rank = { "name": "__" + await self.bot._(ctx.channel, "xp.top.your") + "__", - "value": "**#{} |** `lvl {}` **|** `xp {}`".format( + "value": "**#{} |** `lvl {}` **|** `xp {}`".format( # pylint: disable=consider-using-f-string rank["rank"] if "rank" in rank.keys() else "?", lvl, rank["xp"] ), } # title - t = await self.bot._(ctx.channel, "xp.top.title") + top_title = await self.bot._(ctx.channel, "xp.top.title") # final embed if ctx.can_send_embed: - emb = discord.Embed(title=t, color=self.embed_color) + emb = discord.Embed(title=top_title, color=self.embed_color) emb.set_author( name=self.bot.user.name, icon_url=self.bot.user.display_avatar ) @@ -718,12 +750,12 @@ async def top(self, ctx: MyContext, page: typing.Optional[int] = 1): else: await ctx.send(f_name + "\n\n" + "\n".join(txt)) - async def rr_add_role(self, guildID: int, roleID: int, level: int): + async def rr_add_role(self, guild_id: int, role_id: int, level: int): """Add a role reward in the database""" query = ( "INSERT INTO `roles_levels` (`guild`,`role`,`level`) VALUES (:g, :r, :l);" ) - self.bot.db_query(query, {"g": guildID, "r": roleID, "l": level}) + self.bot.db_query(query, {"g": guild_id, "r": role_id, "l": level}) return True async def rr_list_role(self, guild: int, level: int = -1) -> List[dict]: @@ -731,14 +763,15 @@ async def rr_list_role(self, guild: int, level: int = -1) -> List[dict]: if level < 0: query = "SELECT rowid AS id, * FROM `roles_levels` WHERE guild = :g ORDER BY level;" else: - query = "SELECT rowid AS id, * FROM `roles_levels` WHERE guild=:g AND level=:l ORDER BY level;" + query = "SELECT rowid AS id, * FROM `roles_levels` WHERE guild=:g"\ + "AND level=:l ORDER BY level;" liste = self.bot.db_query(query, {"g": guild, "l": level}) return liste - async def rr_remove_role(self, ID: int): + async def rr_remove_role(self, role_id: int): """Remove a role reward from the database""" query = "DELETE FROM `roles_levels` WHERE rowid = ?;" - self.bot.db_query(query, (ID,)) + self.bot.db_query(query, (role_id,)) return True @commands.group(name="roles_levels") @@ -757,14 +790,14 @@ async def rr_add(self, ctx: MyContext, level: int, *, role: discord.Role): try: if role.name == "@everyone": raise commands.BadArgument(f'Role "{role.name}" not found') - l = await self.rr_list_role(ctx.guild.id) - if len([x for x in l if x["level"] == level]) > 0: + role_rewards = await self.rr_list_role(ctx.guild.id) + if len([x for x in role_rewards if x["level"] == level]) > 0: return await ctx.send( await self.bot._(ctx.guild.id, "xp.rr.already-exist") ) await self.rr_add_role(ctx.guild.id, role.id, level) - except Exception as e: - await self.bot.get_cog("Errors").on_command_error(ctx, e) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_command_error(ctx, exc) else: await ctx.send( await self.bot._( @@ -778,17 +811,17 @@ async def rr_list(self, ctx: MyContext): if not ctx.can_send_embed: return await ctx.send(await self.bot._(ctx.guild.id, "xp.cant-send-embed")) try: - l = await self.rr_list_role(ctx.guild.id) - except Exception as e: - await self.bot.get_cog("Errors").on_command_error(ctx, e) + role_rewards = await self.rr_list_role(ctx.guild.id) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_command_error(ctx, exc) else: - LVL = await self.bot._(ctx.guild.id, "xp.card.level") + level_field_name = await self.bot._(ctx.guild.id, "xp.card.level") desc = "\n".join( - ["• <@&{}> : {} {}".format(x["role"], LVL, x["level"]) for x in l] + [f"• <@&{x['role']}> : {level_field_name} {x['level']}" for x in role_rewards] ) if len(desc) == 0: desc = await self.bot._(ctx.guild.id, "xp.rr.no-rr-2") - title = await self.bot._(ctx.guild.id, "xp.rr.list-title", nbr=len(l)) + title = await self.bot._(ctx.guild.id, "xp.rr.list-title", nbr=len(role_rewards)) emb = discord.Embed(title=title, description=desc) await ctx.send(embed=emb) @@ -798,12 +831,12 @@ async def rr_remove(self, ctx: MyContext, level: int): """Remove a role reward When a member reaches this level, no role will be given anymore""" try: - l = await self.rr_list_role(ctx.guild.id, level) - if len(l) == 0: + roles = await self.rr_list_role(ctx.guild.id, level) + if len(roles) == 0: return await ctx.send(await self.bot._(ctx.guild.id, "xp.rr.no-rr")) - await self.rr_remove_role(l[0]["id"]) - except Exception as e: - await self.bot.get_cog("Errors").on_command_error(ctx, e) + await self.rr_remove_role(roles[0]["id"]) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_command_error(ctx, exc) else: await ctx.send(await self.bot._(ctx.guild.id, "xp.rr.removed", level=level)) @@ -817,7 +850,7 @@ async def rr_reload(self, ctx: MyContext): return await ctx.send( await self.bot._(ctx.guild.id, "xp.cant-manage-roles") ) - c = 0 + counter = 0 rr_list = await self.rr_list_role(ctx.guild.id) if len(rr_list) == 0: await ctx.send(await self.bot._(ctx.guild, "xp.rr.no-rr-2")) @@ -827,30 +860,25 @@ async def rr_reload(self, ctx: MyContext): for x in await self.bdd_get_top(top=None, guild=ctx.guild) ] for member in xps: - m = ctx.guild.get_member(member["user"]) - if m is not None: + discord_member = ctx.guild.get_member(member["user"]) + if discord_member is not None: level = (await self.calc_level(member["xp"]))[0] - c += await self.give_rr(m, level, rr_list, remove=True) + counter += await self.give_rr(discord_member, level, rr_list, remove=True) await ctx.send( await self.bot._( ctx.guild.id, "xp.rr.reload", - count=c, + count=counter, members=ctx.guild.member_count, ) ) - except Exception as e: - await self.bot.get_cog("Errors").on_command_error(ctx, e) - - def cog_unload(self): - self.xp_reduction.cancel() + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_command_error(ctx, exc) + async def cog_unload(self): + self.xp_reduction.cancel() # pylint: disable=no-member -config = {} -async def setup(bot:Gunibot=None, plugin_config:dict=None): + +async def setup(bot:Gunibot=None): if bot is not None: await bot.add_cog(XP(bot), icon="🪙") - if plugin_config is not None: - global config - config.update(plugin_config) - From 031938df0ce1eca8971f8f0f017c7c4891f697f5 Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 10:24:14 +0000 Subject: [PATCH 029/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(core):=20remove=20li?= =?UTF-8?q?nter=20warnings=20in=20core=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/args.py | 37 +++++++++++++------------- bot/checks.py | 7 ++--- core/config.py | 72 +++++++++++++++++++++++++++++--------------------- setup.py | 67 ++++++++++++++++++++++++---------------------- start.py | 16 ++++------- utils.py | 22 +++++++++------ 6 files changed, 119 insertions(+), 102 deletions(-) diff --git a/bot/args.py b/bot/args.py index 0d44099d..194e20c0 100644 --- a/bot/args.py +++ b/bot/args.py @@ -5,17 +5,18 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import discord import re + from discord.ext import commands + from utils import MyContext -class tempdelta(commands.Converter): - async def convert(self, ctx: MyContext, argument: str) -> int: - d = 0 +class tempdelta(commands.Converter): # pylint: disable=invalid-name + async def convert(self, ctx: MyContext, argument: str) -> int: # pylint: disable=unused-argument + time = 0 found = False - for x in [ + for time_spec in [ ("y", 86400 * 365), ("w", 604800), ("d", 86400), @@ -23,23 +24,23 @@ async def convert(self, ctx: MyContext, argument: str) -> int: ("m", 60), ("min", 60), ]: - r = re.search(r"^(\d+)" + x[0] + "$", argument) - if r is not None: - d += int(r.group(1)) * x[1] + pattern = re.search(r"^(\d+)" + time_spec[0] + "$", argument) + if pattern is not None: + time += int(pattern.group(1)) * time_spec[1] found = True - r = re.search(r"^(\d+)h(\d+)m?$", argument) - if r is not None: - d += int(r.group(1)) * 3600 + int(r.group(2)) * 60 + pattern = re.search(r"^(\d+)h(\d+)m?$", argument) + if pattern is not None: + time += int(pattern.group(1)) * 3600 + int(pattern.group(2)) * 60 found = True if not found: raise commands.errors.BadArgument("Invalid duration: " + argument) - return d + return time -class moderatorFlag(commands.Converter): +class moderatorFlag(commands.Converter): # pylint: disable=invalid-name async def convert(self, ctx: MyContext, argument: str) -> str: - LogsFlags = ctx.bot.get_cog("ConfigCog").LogsFlags.FLAGS - if argument not in LogsFlags.values(): + logs_flags = ctx.bot.get_cog("ConfigCog").LogsFlags.FLAGS + if argument not in logs_flags.values(): raise commands.errors.BadArgument("Invalid moderation flag: " + argument) return argument @@ -48,15 +49,15 @@ def constant(word: str): class Constant(commands.Converter): w = word - async def convert(self, ctx: MyContext, arg: str): + async def convert(self, ctx: MyContext, arg: str): # pylint: disable=unused-argument if arg != self.w: raise commands.errors.BadArgument("Unknown argument") return Constant -class arguments(commands.Converter): - async def convert(self, ctx: MyContext, argument: str) -> dict: +class arguments(commands.Converter): # pylint: disable=invalid-name + async def convert(self, ctx: MyContext, argument: str) -> dict: # pylint: disable=unused-argument answer = dict() for result in re.finditer(r"(\w+) ?= ?\"((?:[^\"\\]|\\\"|\\)+)\"", argument): answer[result.group(1)] = result.group(2).replace('\\"', '"') diff --git a/bot/checks.py b/bot/checks.py index b1db9606..d87669be 100644 --- a/bot/checks.py +++ b/bot/checks.py @@ -6,6 +6,7 @@ """ import discord + from utils import MyContext, CheckException from core import config @@ -48,9 +49,9 @@ async def is_roles_manager(ctx: MyContext): async def can_group(ctx: MyContext): - config = ctx.bot.server_configs[ctx.guild.id] - if config["group_allowed_role"] is None: + server_config = ctx.bot.server_configs[ctx.guild.id] + if server_config["group_allowed_role"] is None: return True - role = discord.utils.get(ctx.message.guild.roles, id=config["group_allowed_role"]) + role = discord.utils.get(ctx.message.guild.roles, id=server_config["group_allowed_role"]) if role in ctx.author.roles: return True diff --git a/core/config.py b/core/config.py index 8a3d92ff..c5cf2a42 100644 --- a/core/config.py +++ b/core/config.py @@ -5,9 +5,11 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import yaml import os import importlib + +import yaml + from LRFutils import color from LRFutils import logs @@ -44,25 +46,25 @@ def reload_config(): Finally, it update the dict' using the config.yaml file wich is defined by the user. Each step overwrite the previus one.""" - with open("core/default_config.yaml", "r") as f: - _global_config.update(yaml.safe_load(f)) + with open("core/default_config.yaml", "r", encoding='utf-8') as file: + _global_config.update(yaml.safe_load(file)) - for plugin in os.listdir(f"plugins"): - if os.path.isfile(file := f"plugins/" + plugin + "/config.yaml"): - with open(file) as f: - _global_config.update({plugin: yaml.safe_load(f)}) + for plugin in os.listdir("plugins"): + if os.path.isfile(file := "plugins/" + plugin + "/config.yaml"): + with open(file, encoding='utf-8') as file: + _global_config.update({plugin: yaml.safe_load(file)}) if os.path.isfile("config.yaml"): - with open("config.yaml", "r") as f: - _global_config.update(yaml.safe_load(f)) + with open("config.yaml", "r", encoding='utf-8') as file: + _global_config.update(yaml.safe_load(file)) # Save config - with open("config.yaml", "w") as f: - yaml.dump(_global_config, f) + with open("config.yaml", "w", encoding='utf-8') as file: + yaml.dump(_global_config, file) # Automatically load config when the file is imported -if _global_config == {}: +if not _global_config: reload_config() ################ @@ -71,13 +73,14 @@ def reload_config(): def setup_plugins(): - """Run the "run" function of each plugin's "setup.py" file in order to allow user to configure the plugins. + """Run the "run" function of each plugin's "setup.py" file in order to allow user to configure + the plugins. Called once in the main setup script.""" - for plugin in os.listdir(f"plugins"): - if os.path.isfile(f"plugins/" + plugin + "/setup.py"): + for plugin in os.listdir("plugins"): + if os.path.isfile("plugins/" + plugin + "/setup.py"): - plugin_setup = importlib.import_module(f"plugins." + plugin + ".setup") + plugin_setup = importlib.import_module("plugins." + plugin + ".setup") choice = input( f"\n{color.fg.blue}🔌 Do you want to configure {plugin} plugin? [Y/n]:{color.stop} " @@ -89,8 +92,8 @@ def setup_plugins(): _global_config.update({plugin: plugin_config}) # Save config - with open("config.yaml", "w") as f: - yaml.dump(_global_config, f) + with open("config.yaml", "w", encoding='utf-8') as file: + yaml.dump(_global_config, file) ############### @@ -99,17 +102,23 @@ def setup_plugins(): def token_set(force_set=False): - """Check if the token is set, if not, ask for it. Return True if the token is set, False if not.""" + """Check if the token is set, if not, ask for it. Return True if the token is set, + False if not.""" if _global_config["bot"]["token"] is not None and not force_set: choice = input( - f"\n🔑 {color.fg.blue}A token is already set. Do you want to edit it? [y/N]:{color.stop} " + f"\n🔑 {color.fg.blue}A token is already set."\ + f"Do you want to edit it? [y/N]:{color.stop} " ) if choice.lower() not in accept: return + # pylint: disable=line-too-long print( - f"\n🔑 You need to set your Discord bot token in the config file.\n To do so, go on {color.fg.blue}https://discord.com/developers/applications{color.stop}, select your application, go in bot section and copy your token.\n To create a bot application, please refere to this page: {color.fg.blue}https://discord.com/developers/docs/intro{color.stop}.\n Also, be sure to anable all intents." + f""" +🔑 You need to set your Discord bot token in the config file. + To do so, go on {color.fg.blue}https://discord.com/developers/applications{color.stop}, select your application, go in bot section and copy your token. + To create a bot application, please refere to this page: {color.fg.blue}https://discord.com/developers/docs/intro{color.stop}.\n Also, be sure to anable all intents.""" ) token = "" @@ -120,8 +129,8 @@ def token_set(force_set=False): else: _global_config["bot"]["token"] = token - with open("config.yaml", "w") as f: - yaml.dump(_global_config, f) + with open("config.yaml", "w", encoding='utf-8') as file: + yaml.dump(_global_config, file) return True @@ -160,7 +169,8 @@ def advanced_setup(): while error: error = False choice = input( - f"\n{color.fg.blue}👑 Bot admins (User ID separated with comma. Let empty to ignore):{color.stop} " + f"\n{color.fg.blue}👑 Bot admins"\ + f"(User ID separated with comma. Let empty to ignore):{color.stop} " ) if choice != "": admins = choice.replace(" ", "").split(",") @@ -168,9 +178,10 @@ def advanced_setup(): for admin in admins: admin = int(admin) _global_config["bot"]["admins"] = admins - except: + except ValueError: print( - f"{color.fg.red}👑 Invalid entry. Only user ID (integers), comma and space are expected.{color.stop}" + f"{color.fg.red}👑 Invalid entry. Only user ID (integers),"\ + f"comma and space are expected.{color.stop}" ) error = True @@ -186,11 +197,12 @@ def advanced_setup(): try: channel = int(choice) _global_config["bot"]["error_channels"] = channel - except: + except ValueError: print( - f"{color.fg.red}🤕 Invalid entry. Only channel ID (integers) are expected.{color.stop}" + f"{color.fg.red}🤕 Invalid entry. Only channel ID (integers) are expected."\ + f"{color.stop}" ) error = True - with open("config.yaml", "w") as f: - yaml.dump(_global_config, f) + with open("config.yaml", "w", encoding='utf-8') as file: + yaml.dump(_global_config, file) diff --git a/setup.py b/setup.py index 6178b5a5..3d7f2807 100644 --- a/setup.py +++ b/setup.py @@ -10,9 +10,13 @@ import sys import os -import pkg_resources import subprocess +import pkg_resources +from LRFutils import color + +from core import config + accept = ["y", "yes", "yep", "yeah"] decline = ["n", "no", "nope", "nah"] @@ -47,20 +51,20 @@ def in_virtualenv(): def check_libs(verbose=False): """Check if the required libraries are installed and can be imported""" - with open("requirements.txt", "r") as file: + with open("requirements.txt", "r", encoding='utf-8') as file: packages = pkg_resources.parse_requirements(file.readlines()) try: pkg_resources.working_set.resolve(packages) - except pkg_resources.VersionConflict as e: + except pkg_resources.VersionConflict as exc: if verbose: - print(f"\n🤕 \033[31mOops, there is a problem in the dependencies.\033[0m") - print(f"\n⚠️ \033[33m{type(e).__name__}: {e}\033[0m\n ") + print("\n🤕 \033[31mOops, there is a problem in the dependencies.\033[0m") + print(f"\n⚠️ \033[33m{type(exc).__name__}: {exc}\033[0m\n ") return False - except Exception as e: + except Exception as exc: # pylint: disable=broad-exception-caught if verbose: - print(f"\n🤕 \033[31mOops, there is a problem in the dependencies.\033[0m") + print("\n🤕 \033[31mOops, there is a problem in the dependencies.\033[0m") print( - f"\n⛔ \u001b[41m\u001b[37;1m{type(e).__name__}\033[0m: \033[31m{e}\033[0m" + f"\n⛔ \u001b[41m\u001b[37;1m{type(exc).__name__}\033[0m: \033[31m{exc}\033[0m" ) return False return True @@ -71,15 +75,16 @@ def check_libs(verbose=False): def setup_venv(): - choice = input( - f"\033[34m\n🏗️ Do you want to create a virtual environment? [Y/n]\033[0m" + user_choice = input( + "\033[34m\n🏗️ Do you want to create a virtual environment? [Y/n]\033[0m" ) - if choice.lower() not in decline: + if user_choice.lower() not in decline: print("Creating virtual environment...") os.system("python3 -m venv venv") print("Done!") print( - "\n🔄️ \033[34mPlease activate the virtual environment using the command below that correspond to your system. Then restart the setup script.\033[0m\n" + "\n🔄️ \033[34mPlease activate the virtual environment using the command below that"\ + "correspond to your system. Then restart the setup script.\033[0m\n" ) print( "\033[32m Linux or MacOS (bash shell)\t:\033[0m source venv/bin/activate" @@ -93,7 +98,9 @@ def setup_venv(): print("\033[32m Windows (in cmd.exe)\t\t:\033[0m venv\\Scripts\\activate.bat") print("\033[32m Windows (in PowerShell)\t:\033[0m venv\\Scripts\\Activate.ps1") print( - ' ⮩ If you have an error like "cannot run script", you may open a new Powershell in administrator mode and run the following command: Set-ExecutionPolicy RemoteSigned\n' + ' ⮩ If you have an error like "cannot run script", you may open a new Powershell'\ + 'in administrator mode and run the following command:'\ + 'Set-ExecutionPolicy RemoteSigned\n' ) exit(0) else: @@ -107,10 +114,10 @@ def setup_venv(): def install_dependencies(): """Install all dependencies needed for the bot to work.""" - choice = input( - f"\033[34m\n🏗️ Do you want to install dependencies on the actual environment? [y/N]\033[0m" + user_choice = input( + "\033[34m\n🏗️ Do you want to install dependencies on the actual environment? [y/N]\033[0m" ) - if choice.lower() in accept: + if user_choice.lower() in accept: print("🏗️ Installing dependencies...") os.system("python3 -m pip install -r requirements.txt") print("Done!") @@ -121,13 +128,14 @@ def install_dependencies(): if __name__ == "__main__": - verbose = False + VERBOSE = False else: - verbose = True + VERBOSE = True -if not check_libs(verbose=verbose): +if not check_libs(verbose=VERBOSE): print( - f"\n🏗️ You need to install the bot dependencies. The automatic script will probably upgrade (or rarely downgrade) some python modules already installed on your machine." + "\n🏗️ You need to install the bot dependencies. The automatic script will probably upgrade"\ + "(or rarely downgrade) some python modules already installed on your machine." ) if not in_virtualenv(): setup_venv() @@ -136,21 +144,14 @@ def install_dependencies(): os.system("python3 setup.py") else: print( - "\n⚠️ \033[33mThe bot can't run without it's dependencies. Please install all the required modules with the following command:\033[1m\n" + "\n⚠️ \033[33mThe bot can't run without it's dependencies. Please install"\ + "all the required modules with the following command:\033[1m\n" ) print( " \u001b[47m\033[30mpython3 -m pip install -r requirements.txt\033[0m\n " ) exit(1) -# ________________________________________________________________________________ -# Import modules - -import subprocess -from LRFutils import color -from core import config -import sys - # ________________________________________________________________________________ # Setup script @@ -181,11 +182,13 @@ def main(): # Start bot print( - f"\n{color.fg.yellow}⚠️ Before starting the bot, you should open the config.yaml file and check that everything is correct.{color.stop} " + f"\n{color.fg.yellow}⚠️ Before starting the bot, you should open the config.yaml file"\ + f"and check that everything is correct.{color.stop} " ) choice = input(f"{color.fg.blue}▶️ Do you want to start the bot? [Y/n]{color.stop} ") if choice.lower() not in decline: print( - " Starting the bot...\n--------------------------------------------------------------------------------" + " Starting the bot..." ) - subprocess.run([sys.executable, "start.py"]) + print("-" * 80) + subprocess.run([sys.executable, "start.py"]) # pylint: disable=subprocess-run-check diff --git a/start.py b/start.py index 30051e35..4b0dd0ab 100644 --- a/start.py +++ b/start.py @@ -11,19 +11,20 @@ import os import asyncio -from utils import Gunibot -import argparse + import discord from LRFutils import color from LRFutils import logs -import setup # do not remove this import, it also check the dependencies +from utils import Gunibot +from core import config + +import setup # do not remove this import, it also check the dependencies pylint: disable=unused-import if not os.path.isdir("plugins"): os.mkdir("plugins") # Check and dispatch the config to all plugins -from core import config config.check() @@ -130,13 +131,6 @@ async def on_ready(): client.add_listener(on_ready) - # Check if the bot must run in beta - parser = argparse.ArgumentParser() - parser.add_argument( - "-b", "--beta", help="Run with the beta bot token", action="store_true" - ) - args = parser.parse_args() - # Launch bot try: client.run(config.get("bot.token")) diff --git a/utils.py b/utils.py index 179657a6..c59e3dff 100644 --- a/utils.py +++ b/utils.py @@ -44,6 +44,8 @@ def can_send_embed(self) -> bool: """If the bot has the right permissions to send an embed in the current context""" return self.bot_permissions.embed_links +# defining allowed default mentions +ALLOWED = discord.AllowedMentions(everyone=False, roles=False) class Gunibot(commands.bot.AutoShardedBot): """ @@ -51,8 +53,6 @@ class Gunibot(commands.bot.AutoShardedBot): """ def __init__(self, case_insensitive=None, status=None, beta=False): - # defining allowed default mentions - ALLOWED = discord.AllowedMentions(everyone=False, roles=False) # defining intents usage intents = discord.Intents.default() intents.message_content = True @@ -136,7 +136,8 @@ async def user_avatar_as( :param size: La taille de l'avatar :return: L'avatar """ - avatar = user.display_avatar.with_size(size) # the avatar always exist, returns the URL to the default one + # the avatar always exist, returns the URL to the default one + avatar = user.display_avatar.with_size(size) if avatar.is_animated(): return avatar.with_format("gif") else: @@ -183,7 +184,8 @@ def db_query( :param query: La requête à faire :param args: Les arguments de la requête :param fetchone: Si la requête est un SELECT, retourne seulement le premier résultat - :param returnrowcount: Si la requête est un INSERT, UPDATE ou DELETE, retourne le nombre de lignes affectées + :param returnrowcount: Si la requête est un INSERT, UPDATE ou DELETE, retourne le nombre + de lignes affectées :param astuple: Si la requête est un SELECT, retourne les résultats sous forme de tuple :return: Le résultat de la requête """ @@ -283,7 +285,7 @@ class CheckException(commands.CommandError): """ def __init__(self, check_id, *args): super().__init__(message=f"Custom check '{check_id}' failed", *args) - self.id = check_id + self.id = check_id # pylint: disable=invalid-name def setup_logger(): @@ -314,9 +316,9 @@ def setup_logger(): stream_handler.setLevel(logging.INFO) stream_handler.setFormatter(formatter) - # supposons que nous voulions collecter les erreurs sur ton site d'analyse d'erreurs comme sentry + # supposons que nous voulions collecter les erreurs sur un site d'analyse d'erreurs comme sentry # sentry_handler = x - # sentry_handler.setLevel(logging.ERROR) # on veut voir que les erreurs et au delà, pas en dessous + # sentry_handler.setLevel(logging.ERROR) # sentry_handler.setFormatter(format) # log.debug("message de debug osef") @@ -357,5 +359,9 @@ def setup_logger(): for plugin in os.listdir("./plugins/"): if plugin[0] != "_": if os.path.isfile("./plugins/" + plugin + "/config/options.json"): - with open("./plugins/" + plugin + "/config/options.json", "r", encoding="utf8") as config: + with open( + "./plugins/" + plugin + "/config/options.json", + "r", + encoding="utf8" + ) as config: CONFIG_OPTIONS.update(json.load(config)) From 70f74888c1ff119eab897105dc922f7b7d9d19f7 Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 10:42:16 +0000 Subject: [PATCH 030/148] =?UTF-8?q?=F0=9F=9A=A8=20fix(core):=20fix=20linte?= =?UTF-8?q?r=20warnings=20for=20cogs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/utils/configManager.py | 38 +++++----- bot/utils/errors.py | 114 ++++++++++++++-------------- bot/utils/gunivers.py | 12 +-- bot/utils/languages.py | 20 ++--- bot/utils/sconfig.py | 2 +- bot/utils/timeclass.py | 148 +++++++++++++++++++------------------ plugins/logs/logs.py | 10 +-- 7 files changed, 175 insertions(+), 169 deletions(-) diff --git a/bot/utils/configManager.py b/bot/utils/configManager.py index 962738ac..32263a65 100644 --- a/bot/utils/configManager.py +++ b/bot/utils/configManager.py @@ -7,49 +7,49 @@ import os from json import dump, load -from utils import Gunibot from discord.ext import commands -from utils import CONFIG_OPTIONS + +from utils import Gunibot, CONFIG_OPTIONS CONFIG_FOLDER = "configs" CONFIG_TEMPLATE = {k: v["default"] for k, v in CONFIG_OPTIONS.items() if "default" in v} -class serverConfig(dict): +class ServerConfig(dict): def __init__(self, manager, serverID, value): super().__init__(value) self.manager = manager - self.serverID = serverID + self.server_id = serverID def __getitem__(self, key): try: return super().__getitem__(key) - except KeyError as e: + except KeyError as exc: if key in CONFIG_TEMPLATE.keys(): return CONFIG_TEMPLATE[key] - raise e + raise exc def __setitem__(self, key, item): if key in CONFIG_TEMPLATE.keys(): super().__setitem__(key, item) - self.manager[self.serverID] = self + self.manager[self.server_id] = self else: raise ValueError("Invalid config key") def __delitem__(self, key): super().__setitem__(key, CONFIG_TEMPLATE[key]) - self.manager[self.serverID] = self + self.manager[self.server_id] = self class ConfigCog(commands.Cog): def __init__(self, bot): self.bot = bot self.file = "configManager" - self.confManager = self.configManager() + self.conf_manager = self.ConfigManager() - class configManager(dict): + class ConfigManager(dict): def __init__(self): super().__init__() self.cache = dict() @@ -67,7 +67,7 @@ def __getitem__(self, key): if not (isinstance(key, int) or key.isnumeric()): raise ValueError("Key need to be a valid guild ID") result = dict(CONFIG_TEMPLATE) - if key not in self.cache.keys(): + if key not in self.cache: try: with open(f"{CONFIG_FOLDER}/{key}.json", "r", encoding="utf8") as f: self.cache[key] = load(f) @@ -76,7 +76,7 @@ def __getitem__(self, key): result.update(self.cache[key]) allowed_keys = CONFIG_TEMPLATE.keys() result = {k: v for k, v in result.items() if k in allowed_keys} - return serverConfig(self, key, result) + return ServerConfig(self, key, result) def __repr__(self): return "" @@ -95,11 +95,11 @@ def has_key(self, k): def update(self, *args, **kwargs): for arg in args: if isinstance(arg, dict): - for k, v in arg.items(): - self.__setitem__(k, v) + for key, value in arg.items(): + self[key] = value for kwarg in kwargs: - for k, v in kwarg.items(): - self.__setitem__(k, v) + for key, value in kwarg.items(): + self[key] = value def keys(self): return [name for name in os.listdir(CONFIG_FOLDER) if os.path.isfile(name)] @@ -129,16 +129,16 @@ class LogsFlags: 1 << 8: "emojis", } - def flagsToInt(self, flags: list) -> int: + def flags_to_int(self, flags: list) -> int: r = 0 for k, v in self.FLAGS.items(): if v in flags: r |= k return r - def intToFlags(self, i: int) -> list: + def int_to_flags(self, i: int) -> list: return [v for k, v in self.FLAGS.items() if i & k == k] -async def setup(bot: Gunibot = None, plugin_config: dict = None): +async def setup(bot: Gunibot = None): await bot.add_cog(ConfigCog(bot)) diff --git a/bot/utils/errors.py b/bot/utils/errors.py index ffcdb834..4af9566a 100644 --- a/bot/utils/errors.py +++ b/bot/utils/errors.py @@ -5,17 +5,15 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -from utils import CheckException, Gunibot, MyContext -from discord.ext import commands -import discord -from bot import checks import re import traceback -from core import config -import sys +import discord +from discord.ext import commands -sys.path.append("./bot") +from utils import CheckException, Gunibot, MyContext +from bot import checks +from core import config class Errors(commands.Cog): @@ -83,89 +81,89 @@ async def on_command_error(self, ctx: MyContext, error: Exception): ) # Could not convert "limit" into int. OR Converting to "int" failed # for parameter "number". - r = re.search( + result = re.search( r"Could not convert \"(?P[^\"]+)\" into (?P[^.\n]+)", raw_error, ) - if r is None: - r = re.search( + if result is None: + result = re.search( r"Converting to \"(?P[^\"]+)\" failed for parameter \"(?P[^.\n]+)\"", raw_error, ) - if r is not None: + if result is not None: return await ctx.send( await self.bot._( ctx.channel, "errors.unknown-arg", - p=r.group("arg"), - t=r.group("type"), + p=result.group("arg"), + t=result.group("type"), ) ) # zzz is not a recognised boolean option - r = re.search( + result = re.search( r"(?P[^\"]+) is not a recognised (?P[^.\n]+) option", raw_error, ) - if r is not None: + if result is not None: return await ctx.send( await self.bot._( ctx.channel, "errors.invalid-type", - p=r.group("arg"), - t=r.group("type"), + p=result.group("arg"), + t=result.group("type"), ) ) # Member "Z_runner" not found - r = re.search(r"Member \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"Member \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( - await self.bot._(ctx.channel, "errors.unknown-member", m=r.group(1)) + await self.bot._(ctx.channel, "errors.unknown-member", m=result.group(1)) ) # User "Z_runner" not found - r = re.search(r"User \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"User \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( - await self.bot._(ctx.channel, "errors.unknown-user", u=r.group(1)) + await self.bot._(ctx.channel, "errors.unknown-user", u=result.group(1)) ) # Role "Admin" not found - r = re.search(r"Role \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"Role \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( - await self.bot._(ctx.channel, "errors.unknown-role", r=r.group(1)) + await self.bot._(ctx.channel, "errors.unknown-role", r=result.group(1)) ) # Emoji ":shock:" not found - r = re.search(r"Emoji \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"Emoji \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( - await self.bot._(ctx.channel, "errors.unknown-emoji", e=r.group(1)) + await self.bot._(ctx.channel, "errors.unknown-emoji", e=result.group(1)) ) # Colour "blue" is invalid - r = re.search(r"Colour \"([^\"]+)\" is invalid", raw_error) - if r is not None: + result = re.search(r"Colour \"([^\"]+)\" is invalid", raw_error) + if result is not None: return await ctx.send( - await self.bot._(ctx.channel, "errors.invalid-color", c=r.group(1)) + await self.bot._(ctx.channel, "errors.invalid-color", c=result.group(1)) ) # Channel "twitter" not found. - r = re.search(r"Channel \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"Channel \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( await self.bot._( - ctx.channel, "errors.unknown-channel", c=r.group(1) + ctx.channel, "errors.unknown-channel", c=result.group(1) ) ) # Message "1243" not found. - r = re.search(r"Message \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"Message \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( await self.bot._( - ctx.channel, "errors.unknown-message", m=r.group(1) + ctx.channel, "errors.unknown-message", m=result.group(1) ) ) # Group "twitter" not found. - r = re.search(r"Group \"([^\"]+)\" not found", raw_error) - if r is not None: + result = re.search(r"Group \"([^\"]+)\" not found", raw_error) + if result is not None: return await ctx.send( - await self.bot._(ctx.channel, "errors.unknown-group", g=r.group(1)) + await self.bot._(ctx.channel, "errors.unknown-group", g=result.group(1)) ) # Too many text channels if raw_error == "Too many text channels": @@ -173,34 +171,34 @@ async def on_command_error(self, ctx: MyContext, error: Exception): await self.bot._(ctx.channel, "errors.too-many-text-channels") ) # Invalid duration: 2d - r = re.search(r"Invalid duration: ([^\" ]+)", raw_error) - if r is not None: + result = re.search(r"Invalid duration: ([^\" ]+)", raw_error) + if result is not None: return await ctx.send( await self.bot._( - ctx.channel, "errors.invalid-duration", d=r.group(1) + ctx.channel, "errors.invalid-duration", d=result.group(1) ) ) # Invalid invite: nope - r = re.search(r"Invalid invite: (\S+)", raw_error) - if r is not None: + result = re.search(r"Invalid invite: (\S+)", raw_error) + if result is not None: return await ctx.send( await self.bot._(ctx.channel, "errors.invalid-invite") ) # Invalid guild: test - r = re.search(r"Invalid guild: (\S+)", raw_error) - if r is not None: + result = re.search(r"Invalid guild: (\S+)", raw_error) + if result is not None: return await ctx.send( await self.bot._(ctx.channel, "errors.unknown-server") ) # Invalid url: nou - r = re.search(r"Invalid url: (\S+)", raw_error) - if r is not None: + result = re.search(r"Invalid url: (\S+)", raw_error) + if result is not None: return await ctx.send( await self.bot._(ctx.channel, "errors.invalid-url") ) # Invalid emoji: lmao - r = re.search(r"Invalid emoji: (\S+)", raw_error) - if r is not None: + result = re.search(r"Invalid emoji: (\S+)", raw_error) + if result is not None: return await ctx.send( await self.bot._(ctx.channel, "errors.invalid-emoji") ) @@ -223,7 +221,7 @@ async def on_command_error(self, ctx: MyContext, error: Exception): # All other Errors not returned come here... And we can just print the # default TraceBack. self.bot.log.warning( - "Ignoring exception in command {}:".format(ctx.message.content), + f"Ignoring exception in command {ctx.message.content}:", exc_info=(type(error), error, error.__traceback__), ) await self.on_error(error, ctx) @@ -233,8 +231,8 @@ async def on_error(self, error, ctx=None): try: if isinstance(ctx, discord.Message): ctx = await self.bot.get_context(ctx) - tr = traceback.format_exception(type(error), error, error.__traceback__) - msg = "```python\n{}\n```".format(" ".join(tr)[:1900]) + trace = traceback.format_exception(type(error), error, error.__traceback__) + msg = f"```python\n{' '.join(trace)[:1900]}\n```" if ctx is None: await self.senf_err_msg(f"Internal Error\n{msg}") elif ctx.guild is None: @@ -245,8 +243,8 @@ async def on_error(self, error, ctx=None): await self.senf_err_msg( ctx.guild.name + " | " + ctx.channel.name + "\n" + msg ) - except Exception as e: - self.bot.log.warn(f"[on_error] {e}", exc_info=True) + except Exception as exc: # pylint: disable=broad-exception-caught + self.bot.log.warn(f"[on_error] {exc}", exc_info=True) async def senf_err_msg(self, msg): """Sends a message to the error channel""" @@ -257,5 +255,5 @@ async def senf_err_msg(self, msg): return True -async def setup(bot: Gunibot = None, plugin_config: dict = None): +async def setup(bot: Gunibot = None): await bot.add_cog(Errors(bot)) diff --git a/bot/utils/gunivers.py b/bot/utils/gunivers.py index f22378ef..74751e38 100644 --- a/bot/utils/gunivers.py +++ b/bot/utils/gunivers.py @@ -5,19 +5,19 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import discord from discord.ext import tasks, commands -from utils import Gunibot, MyContext + +from utils import Gunibot class Gunivers(commands.Cog): def __init__(self, bot: Gunibot): self.bot = bot self.file = "gunivers" - self.update_loop.start() + self.update_loop.start() # pylint: disable=no-member - def cog_unload(self): - self.update_loop.cancel() + async def cog_unload(self): + self.update_loop.cancel() # pylint: disable=no-member @tasks.loop(minutes=60.0 * 24.0) async def update_loop(self): @@ -26,5 +26,5 @@ async def update_loop(self): await channel.send("Bon, qu'est-ce qu'on peut poster aujourd'hui ?") -async def setup(bot: Gunibot = None, plugin_config: dict = None): +async def setup(bot: Gunibot = None): await bot.add_cog(Gunivers(bot)) diff --git a/bot/utils/languages.py b/bot/utils/languages.py index e74ff258..303ddcd7 100644 --- a/bot/utils/languages.py +++ b/bot/utils/languages.py @@ -5,11 +5,13 @@ de la licence CeCILL diffusée sur le site "http://www.cecill.info". """ -import discord +import os + import i18n +import discord from discord.ext import commands + from utils import Gunibot -import os i18n.translations.container.clear() # invalidate old cache i18n.set("filename_format", "{locale}.{format}") @@ -29,7 +31,7 @@ def __init__(self, bot: Gunibot): self.languages = ["fr", "en"] self.config_options = ["language"] - async def tr(self, ctx, key: str, **kwargs): + async def tr(self, ctx, key: str, **kwargs): # pylint: disable=invalid-name """Translate something Ctx can be either a Context, a guild, a guild id, a channel or a lang directly""" lang = self.languages[0] @@ -49,21 +51,21 @@ async def tr(self, ctx, key: str, **kwargs): lang = self.languages[0] return i18n.t(key, locale=lang, **kwargs) - async def get_lang(self, guildID: int, use_str: bool = False) -> int: - if guildID is None: + async def get_lang(self, guild_id: int, use_str: bool = False) -> int: + if guild_id is None: as_int = 0 else: # migration for old format - if isinstance(self.bot.server_configs[guildID]["language"], int): - as_int = self.bot.server_configs[guildID]["language"] + if isinstance(self.bot.server_configs[guild_id]["language"], int): + as_int = self.bot.server_configs[guild_id]["language"] else: as_int = self.languages.index( - self.bot.server_configs[guildID]["language"] + self.bot.server_configs[guild_id]["language"] ) if use_str: return self.languages[as_int] return as_int -async def setup(bot: Gunibot = None, plugin_config: dict = None): +async def setup(bot: Gunibot = None): await bot.add_cog(Languages(bot)) diff --git a/bot/utils/sconfig.py b/bot/utils/sconfig.py index 41d2e084..a3b16d23 100644 --- a/bot/utils/sconfig.py +++ b/bot/utils/sconfig.py @@ -159,7 +159,7 @@ def emojis_convert( value = [value] if isinstance(value, str) else value return " ".join([emojis_convert(x, self.bot.emojis) for x in value]) if config["type"] == "modlogsFlags": - flags = self.bot.get_cog("ConfigCog").LogsFlags().intToFlags(value) + flags = self.bot.get_cog("ConfigCog").LogsFlags().int_to_flags(value) return " - ".join(flags) if len(flags) > 0 else None if config["type"] == "language": cog = self.bot.get_cog("Languages") diff --git a/bot/utils/timeclass.py b/bot/utils/timeclass.py index 1b74099b..2ecabc75 100644 --- a/bot/utils/timeclass.py +++ b/bot/utils/timeclass.py @@ -7,11 +7,12 @@ import datetime import time -from utils import Gunibot import discord from discord.ext import tasks +from utils import Gunibot + fr_months = [ "Janvier", "Février", @@ -71,20 +72,18 @@ async def launch(task, coro, *args, **kwargs): if task.current_loop != 0: await self.bot.wait_until_ready() self.bot.log.info( - "[TaskManager] Tâche {} arrivée à terme".format(coro.__func__) + f"[TaskManager] Tâche {coro.__func__} arrivée à terme" ) try: await coro(*args, **kwargs) - except Exception as e: - self.bot.get_cog("Errors").on_error(e) + except Exception as exc: # pylint: disable=broad-exception-caught + self.bot.get_cog("Errors").on_error(exc) a = tasks.loop(seconds=delay, count=2)(launch) a.error(self.bot.get_cog("Errors").on_error) a.start(a, coro, *args, **kwargs) self.bot.log.info( - "[TaskManager] Nouvelle tâche {} programmée pour dans {}s".format( - coro.__func__, delay - ) + f"[TaskManager] Nouvelle tâche {coro.__func__} programmée pour dans {delay}s" ) return a @@ -142,41 +141,45 @@ async def time_delta( if date2 is not None: if isinstance(date2, datetime.datetime): delta = abs(date2 - date1) - t = await self.time_interval(delta, precision) + inputed_time = await self.time_interval(delta, precision) else: raise ValueError else: - t = self.timedelta(total_seconds=date1, precision=precision) - t.set_from_seconds() + inputed_time = self.timedelta(total_seconds=date1, precision=precision) + inputed_time.set_from_seconds() if form == "digital": if hour: - h = "{}:{}:{}".format(t.hours, t.minutes, t.seconds) + inputed_hours = f"{inputed_time.hours}:{inputed_time.minutes}:"\ + f"{inputed_time.seconds}" else: - h = "" + inputed_hours = "" if lang == "fr": - text = "{}/{}{} {}".format( - t.days, t.months, "/" + str(t.years) if year else "", h + text = "{}/{}{} {}".format( # pylint: disable=consider-using-f-string + inputed_time.days, inputed_time.months, "/" + str(inputed_time.years) if year\ + else "", inputed_hours ) else: - text = "{}/{}{} {}".format( - t.months, t.days, "/" + str(t.years) if year else "", h + text = "{}/{}{} {}".format( # pylint: disable=consider-using-f-string + inputed_time.months, inputed_time.days, "/" + str(inputed_time.years) if year\ + else "", inputed_hours ) elif form == "temp": text = str() - if t.days + t.months * 365 / 12 + t.years * 365 > 0: - d = round(t.days + t.months * 365 / 12) + if inputed_time.days + inputed_time.months * 365 / 12 + inputed_time.years * 365 > 0: + inputed_days = round(inputed_time.days + inputed_time.months * 365 / 12) if not year: - d += round(t.years * 365) - elif year and t.years > 0: - text += str(t.years) + "a " if lang == "fr" else str(t.years) + "y " - text += str(d) + "j " if lang == "fr" else str(d) + "d " + inputed_days += round(inputed_time.years * 365) + elif year and inputed_time.years > 0: + text += str(inputed_time.years) + "a " if lang == "fr"\ + else str(inputed_time.years) + "y " + text += str(inputed_days) + "j " if lang == "fr" else str(inputed_days) + "d " if hour: - if t.hours > 0: - text += str(t.hours) + "h " - if t.minutes > 0: - text += str(t.minutes) + "m " - if t.seconds > 0: - text += str(t.seconds) + "s " + if inputed_time.hours > 0: + text += str(inputed_time.hours) + "h " + if inputed_time.minutes > 0: + text += str(inputed_time.minutes) + "m " + if inputed_time.seconds > 0: + text += str(inputed_time.seconds) + "s " text = text.strip() else: text = str() @@ -240,47 +243,50 @@ async def time_delta( "seconds", "second", ] - if year and t.years != 0: - if t.years > 1: - text += str(t.years) + " " + lib[0] + if year and inputed_time.years != 0: + if inputed_time.years > 1: + text += str(inputed_time.years) + " " + lib[0] else: - text += str(t.years) + " " + lib[1] + text += str(inputed_time.years) + " " + lib[1] text += " " - if t.months > 1: - text += str(t.months) + " " + lib[2] - elif t.months == 1: - text += str(t.months) + " " + lib[3] + if inputed_time.months > 1: + text += str(inputed_time.months) + " " + lib[2] + elif inputed_time.months == 1: + text += str(inputed_time.months) + " " + lib[3] text += " " - if t.days > 1: - text += str(t.days) + " " + lib[4] - elif t.days == 1: - text += str(t.days) + " " + lib[5] + if inputed_time.days > 1: + text += str(inputed_time.days) + " " + lib[4] + elif inputed_time.days == 1: + text += str(inputed_time.days) + " " + lib[5] if hour: - if t.hours > 1: - text += " " + str(t.hours) + " " + lib[6] - elif t.hours == 1: - text += " " + str(t.hours) + " " + lib[7] + if inputed_time.hours > 1: + text += " " + str(inputed_time.hours) + " " + lib[6] + elif inputed_time.hours == 1: + text += " " + str(inputed_time.hours) + " " + lib[7] text += " " - if t.minutes > 1: - text += str(t.minutes) + " " + lib[8] - elif t.minutes == 1: - text += str(t.minutes) + " " + lib[9] + if inputed_time.minutes > 1: + text += str(inputed_time.minutes) + " " + lib[8] + elif inputed_time.minutes == 1: + text += str(inputed_time.minutes) + " " + lib[9] text += " " - if t.seconds > 1: - text += str(t.seconds) + " " + lib[10] - elif t.seconds == 1: - text += str(t.seconds) + " " + lib[11] + if inputed_time.seconds > 1: + text += str(inputed_time.seconds) + " " + lib[10] + elif inputed_time.seconds == 1: + text += str(inputed_time.seconds) + " " + lib[11] return text.strip() async def time_interval(self, tmd, precision=2): """Crée un objet de type timedelta à partir d'un objet datetime.timedelta""" - t = tmd.total_seconds() - obj = self.timedelta(total_seconds=t, precision=precision) + inputed_time = tmd.total_seconds() + obj = self.timedelta(total_seconds=inputed_time, precision=precision) obj.set_from_seconds() return obj async def date(self, date, lang="fr", year=False, hour=True, digital=False): - """Traduit un objet de type datetime.datetime en chaine de caractère lisible. Renvoie un str""" + """ + Traduit un objet de type datetime.datetime en chaine de caractère lisible. + Renvoie un str + """ if isinstance(date, time.struct_time): date = datetime.datetime(*date[:6]) if isinstance(date, datetime.datetime): @@ -288,7 +294,7 @@ async def date(self, date, lang="fr", year=False, hour=True, digital=False): jour = "0" + str(date.day) else: jour = str(date.day) - h = [] + times = [] if lang == "fr": month = fr_months elif lang == "fi": @@ -296,11 +302,11 @@ async def date(self, date, lang="fr", year=False, hour=True, digital=False): else: month = en_months for i in ["hour", "minute", "second"]: - a = eval(str("date." + i)) - if len(str(a)) == 1: - h.append("0" + str(a)) + argument = getattr(date, i) + if len(str(argument)) == 1: + times.append("0" + str(argument)) else: - h.append(str(a)) + times.append(str(argument)) if digital: if date.month < 10: month = "0" + str(date.month) @@ -308,31 +314,31 @@ async def date(self, date, lang="fr", year=False, hour=True, digital=False): month = str(date.month) separator = "/" if lang == "fr": - df = "{d}/{m}{y} {h}" + formated_date = "{d}/{m}{y} {h}" elif lang == "fi": - df = "{d}.{m}{y} {h}" + formated_date = "{d}.{m}{y} {h}" separator = "." else: - df = "{m}/{d}{y} {h}" - df = df.format( + formated_date = "{m}/{d}{y} {h}" + formated_date = formated_date.format( d=jour, m=month, y=separator + str(date.year) if year else "", - h=":".join(h) if hour else "", + h=":".join(times) if hour else "", ) else: if lang == "fr" or lang == "fi": - df = "{d} {m} {y} {h}" + formated_date = "{d} {m} {y} {h}" else: - df = "{m} {d}, {y} {h}" - df = df.format( + formated_date = "{m} {d}, {y} {h}" + formated_date = formated_date.format( d=jour, m=month[date.month - 1], y=str(date.year) if year else "", - h=":".join(h) if hour else "", + h=":".join(times) if hour else "", ) - return df.strip() + return formated_date.strip() -async def setup(bot: Gunibot = None, plugin_config: dict = None): +async def setup(bot: Gunibot = None): await bot.add_cog(TimeCog(bot)) diff --git a/plugins/logs/logs.py b/plugins/logs/logs.py index c261ad01..3b910de4 100644 --- a/plugins/logs/logs.py +++ b/plugins/logs/logs.py @@ -46,10 +46,10 @@ async def modlogs_enable(self, ctx: MyContext, options: commands.Greedy[args.mod return logs_flags = self.bot.get_cog('ConfigCog').LogsFlags() flags = self.bot.server_configs[ctx.guild.id]['modlogs_flags'] - flags = logs_flags.intToFlags(flags) + options + flags = logs_flags.int_to_flags(flags) + options flags = list(set(flags)) # remove duplicates await SERVER_CONFIG.edit_config(ctx.guild.id, 'modlogs_flags', - logs_flags.flagsToInt(flags)) + logs_flags.flags_to_int(flags)) await ctx.send( await self.bot._(ctx.guild.id, "sconfig.modlogs-enabled", type=', '.join(options)) ) @@ -62,12 +62,12 @@ async def modlogs_disable(self, ctx: MyContext, options: commands.Greedy[args.mo return logs_flags = self.bot.get_cog('ConfigCog').LogsFlags() flags = self.bot.server_configs[ctx.guild.id]['modlogs_flags'] - flags = logs_flags.intToFlags(flags) + flags = logs_flags.int_to_flags(flags) flags = [x for x in flags if x not in options] await SERVER_CONFIG.edit_config( ctx.guild.id, 'modlogs_flags', - logs_flags.flagsToInt(flags) + logs_flags.flags_to_int(flags) ) await ctx.send( await self.bot._(ctx.guild.id, "sconfig.modlogs-disabled", type=', '.join(options)) @@ -106,7 +106,7 @@ async def send_embed(self, guild, embed: discord.Embed): def get_flags(self, guild_id): """Return the log flags for a the given `guild_id`.""" - flags = self.bot.get_cog('ConfigCog').LogsFlags().intToFlags + flags = self.bot.get_cog('ConfigCog').LogsFlags().int_to_flags() return flags(self.bot.server_configs[guild_id]['modlogs_flags']) @commands.Cog.listener() From 537046e17837993e7f7456f0d98ac10e5b1b723a Mon Sep 17 00:00:00 2001 From: ascpial Date: Thu, 23 Feb 2023 12:08:30 +0000 Subject: [PATCH 031/148] =?UTF-8?q?=F0=9F=9A=A8=20fix:=20various=20linter?= =?UTF-8?q?=20infos=20on=20all=20plugins=20=C2=A9add=20correct=20credits?= =?UTF-8?q?=20to=20ascpial=20=F0=9F=A5=B0=20some=20love=20to=20the=20invit?= =?UTF-8?q?etracker=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/admin/admin.py | 97 ++++++++++---------- plugins/admin/credits.md | 1 + plugins/antikikoo/antikikoo.py | 17 ++-- plugins/ban/ban.py | 25 +++--- plugins/channelArchive/channelArchive.py | 22 +++-- plugins/contact/contact.py | 26 +++--- plugins/contact/credits.md | 2 +- plugins/general/credits.md | 1 + plugins/general/general.py | 70 +++++++-------- plugins/giveaways/credits.md | 2 +- plugins/giveaways/giveaways.py | 110 ++++++++++++----------- plugins/groups/credits.md | 2 +- plugins/groups/groups.py | 103 ++++++++++----------- plugins/help/help.py | 2 +- plugins/hypesquad/credits.md | 2 +- plugins/inviteTracker/inviteTracker.py | 4 +- plugins/misc/credits.md | 2 +- plugins/quizz/credits.md | 1 + plugins/rss/rss.py | 43 ++++----- plugins/rss/setup.py | 55 +++++++----- plugins/thanks/credits.md | 2 +- plugins/thanks/thanks.py | 5 +- plugins/voice/credits.md | 2 +- plugins/voice/voice.py | 5 +- plugins/welcome/credits.md | 2 +- plugins/wormhole/wormhole.py | 7 +- 26 files changed, 317 insertions(+), 293 deletions(-) diff --git a/plugins/admin/admin.py b/plugins/admin/admin.py index ed398fe3..7f0c36e1 100644 --- a/plugins/admin/admin.py +++ b/plugins/admin/admin.py @@ -57,31 +57,31 @@ async def main_msg(self, ctx: commands.Context): @main_msg.command(name="pull") async def gitpull(self, ctx: commands.Context, branch: str = None): """Tire des changements de GitLab""" - m = await ctx.send("Mise à jour depuis gitlab...") + msg = await ctx.send("Mise à jour depuis gitlab...") repo = Repo(os.getcwd()) assert not repo.bare if branch: try: repo.git.checkout(branch) - except GitCommandError as e: - self.bot.log.exception(e) + except GitCommandError as exc: + self.bot.log.exception(exc) if ( "Your local changes to the following files would be overwritten by checkout" - in str(e) + in str(exc) ): - await m.edit( - content=m.content - + "\nCertains fichiers ont été modifiés localement - abandon de la procédure" + await msg.edit( + content=msg.content + \ + "\nCertains fichiers ont été modifiés localement - abandon de la procédure" ) else: - await m.edit( - content=m.content + await msg.edit( + content=msg.content + "\nNom de branche invalide - abandon de la procédure" ) return else: - await m.edit( - content=m.content + f"\nBranche {branch} correctement sélectionnée" + await msg.edit( + content=msg.content + f"\nBranche {branch} correctement sélectionnée" ) origin = repo.remotes.origin origin.pull() @@ -98,20 +98,20 @@ async def git_branches(self, ctx: commands.Context): @main_msg.command(name="shutdown") async def shutdown(self, ctx: commands.Context): """Eteint le bot""" - m = await ctx.send("Nettoyage de l'espace de travail...") + msg = await ctx.send("Nettoyage de l'espace de travail...") await self.cleanup_workspace() - await m.edit(content="Bot en voie d'extinction") + await msg.edit(content="Bot en voie d'extinction") await self.bot.change_presence(status=discord.Status("offline")) self.bot.log.info("Fermeture du bot") await self.bot.close() async def cleanup_workspace(self): - for folderName, _, filenames in os.walk(".."): + for folder_name, _, filenames in os.walk(".."): for filename in filenames: if filename.endswith(".pyc"): - os.unlink(folderName + "/" + filename) - if folderName.endswith("__pycache__"): - os.rmdir(folderName) + os.unlink(folder_name + "/" + filename) + if folder_name.endswith("__pycache__"): + os.rmdir(folder_name) @main_msg.command(name="reboot") async def restart_bot(self, ctx: commands.Context): @@ -135,7 +135,7 @@ async def clean(self, ctx: commands.Context, limit: int): await ctx.message.delete() deleted = await ctx.channel.purge(limit=limit) await ctx.send( - "{} messages supprimés !".format(len(deleted)), delete_after=3.0 + f"{len(deleted)} messages supprimés !", delete_after=3.0 ) @main_msg.command(name="reload") @@ -150,21 +150,19 @@ async def reload_cog(self, ctx: commands.Context, *, cog: str): try: self.bot.reload_extension("plugins." + cog + ".bot.main") except ModuleNotFoundError: - await ctx.send("Cog {} can't be found".format(cog)) + await ctx.send(f"Cog {cog} can't be found") except commands.errors.ExtensionNotLoaded: - await ctx.send("Cog {} was never loaded".format(cog)) - except Exception as e: # pylint: disable=broad-exception-caught - await errors_cog.on_error(e, ctx) - await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}") + await ctx.send(f"Cog {cog} was never loaded") + except Exception as exc: # pylint: disable=broad-exception-caught + await errors_cog.on_error(exc, ctx) + await ctx.send(f"**`ERROR:`** {type(exc).__name__} - {exc}") else: - self.bot.log.info("Module {} rechargé".format(cog)) + self.bot.log.info(f"Module {cog} rechargé") reloaded_cogs.append(cog) if len(reloaded_cogs) > 0: await ctx.bot.get_cog("General").count_lines_code() await ctx.send( - "These cogs has successfully reloaded: {}".format( - ", ".join(reloaded_cogs) - ) + f"These cogs has successfully reloaded: {', '.join(reloaded_cogs)}" ) @main_msg.command(name="add_cog", hidden=True) @@ -172,44 +170,44 @@ async def add_cog(self, ctx: commands.Context, name: str): """Ajouter un cog au bot""" try: self.bot.load_extension("plugins." + name) - await ctx.send("Module '{}' ajouté !".format(name)) - self.bot.log.info("Module {} ajouté".format(name)) - except Exception as e: # pylint: disable=broad-exception-caught - await ctx.send(str(e)) + await ctx.send(f"Module '{name}' ajouté !") + self.bot.log.info(f"Module {name} ajouté") + except Exception as exc: # pylint: disable=broad-exception-caught + await ctx.send(str(exc)) @main_msg.command(name="del_cog", aliases=["remove_cog"], hidden=True) async def rm_cog(self, ctx: commands.Context, name: str): """Enlever un cog au bot""" try: self.bot.unload_extension("plugins." + name) - await ctx.send("Module '{}' désactivé !".format(name)) - self.bot.log.info("Module {} désactivé".format(name)) - except Exception as e: # pylint: disable=broad-exception-caught - await ctx.send(str(e)) + await ctx.send(f"Module '{name}' désactivé !") + self.bot.log.info(f"Module {name} désactivé") + except Exception as exc: # pylint: disable=broad-exception-caught + await ctx.send(str(exc)) @main_msg.command(name="cogs", hidden=True) async def cogs_list(self, ctx: commands.Context): """Voir la liste de tout les cogs""" text = str() - for k, v in self.bot.cogs.items(): - text += "- {} ({}) \n".format(v.file if hasattr(v, "file") else "?", k) + for key, value in self.bot.cogs.items(): + text += f"- {value.file if hasattr(value, 'file') else '?'} ({key}) \n" await ctx.send(text) @main_msg.command(name="activity") - async def change_activity(self, ctx: commands.Context, Type: str, *act: str): + async def change_activity(self, ctx: commands.Context, action_type: str, *act: str): """Change l'activité du bot (play, watch, listen, stream)""" act = " ".join(act) - if Type in ["game", "play", "playing"]: + if action_type in ["game", "play", "playing"]: await self.bot.change_presence(activity=discord.Game(name=act)) - elif Type in ["watch", "see", "watching"]: + elif action_type in ["watch", "see", "watching"]: await self.bot.change_presence( activity=discord.Activity(type=discord.ActivityType.watching, name=act) ) - elif Type in ["listen", "listening"]: + elif action_type in ["listen", "listening"]: await self.bot.change_presence( activity=discord.Activity(type=discord.ActivityType.listening, name=act) ) - elif Type in ["stream"]: + elif action_type in ["stream"]: await self.bot.change_presence( activity=discord.Activity(type=discord.ActivityType.streaming, name=act) ) @@ -239,19 +237,19 @@ async def _eval(self, ctx: commands.Context, *, body: str): stdout = io.StringIO() try: to_compile = f'async def func():\n{textwrap.indent(body, " ")}' - except Exception as e: # pylint: disable=broad-exception-caught - await self.bot.get_cog("Errors").on_error(e, ctx) + except Exception as exc: # pylint: disable=broad-exception-caught + await self.bot.get_cog("Errors").on_error(exc, ctx) return try: exec(to_compile, env) # pylint: disable=exec-used - except Exception as e: # pylint: disable=broad-exception-caught - return await ctx.send(f"```py\n{e.__class__.__name__}: {e}\n```") + except Exception as exc: # pylint: disable=broad-exception-caught + return await ctx.send(f"```py\n{exc.__class__.__name__}: {exc}\n```") func = env["func"] try: with redirect_stdout(stdout): ret = await func() - except Exception as e: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught value = stdout.getvalue() await ctx.send(f"```py\n{value}{traceback.format_exc()}\n```") else: @@ -264,6 +262,5 @@ async def _eval(self, ctx: commands.Context, *, body: str): self._last_result = ret await ctx.send(f"```py\n{value}{ret}\n```") -async def setup(bot:Gunibot=None): - if bot is not None: - await bot.add_cog(Admin(bot), icon="🚨") \ No newline at end of file +async def setup(bot:Gunibot): + await bot.add_cog(Admin(bot), icon="🚨") diff --git a/plugins/admin/credits.md b/plugins/admin/credits.md index 79bf54bb..f933369c 100644 --- a/plugins/admin/credits.md +++ b/plugins/admin/credits.md @@ -1,6 +1,7 @@ Copyright © ZRunner 2020 Copyright © Leirof 2021 - 2022 Copyright © Aeris One 2022 +Copyright © ascpial 2023 Ce programme est régi par la licence CeCILL soumise au droit français et respectant les principes de diffusion des logiciels libres. Vous pouvez diff --git a/plugins/antikikoo/antikikoo.py b/plugins/antikikoo/antikikoo.py index 0093c3f4..d3b7157e 100644 --- a/plugins/antikikoo/antikikoo.py +++ b/plugins/antikikoo/antikikoo.py @@ -11,6 +11,7 @@ from utils import Gunibot, MyContext +# pylint: disable=line-too-long WELCOME_MESSAGE = """(FR) Bienvenue sur {server} {user} ! Vous n'avez accès qu'au salon Lobby pour le moment. Pour débloquer l'accès au reste du Discord, lisez les instructions présentes dans le salon {channel} :wink: @@ -58,7 +59,7 @@ async def on_member_join(self, member: discord.Member): verif_channel: TextChannel = self.bot.get_channel( config["verification_channel"] ) - info_channel = "<#{}>".format(config["info_channel"]) + info_channel = f"<#{config['info_channel']}>" # if config is None, we use the default one welcome_msg: str = config["verification_info_message"] or WELCOME_MESSAGE await verif_channel.send( @@ -76,11 +77,11 @@ async def on_message(self, message: discord.Message): config = self.bot.server_configs[message.guild.id] if message.channel.id != config["verification_channel"]: return - + if config["pass_message"] is None: # not set return - - info_channel = "<#{}>".format(config["info_channel"]) + + info_channel = f"<#{config['info_channel']}>" if message.content.lower() == config["pass_message"].lower(): emb = discord.Embed( description=CONFIRM_MESSAGE.format( @@ -133,7 +134,7 @@ async def ak_msg(self, ctx: MyContext, *, message: str = None): Put nothing to reset it, or "None" for no message""" self.bot.server_configs[ctx.guild.id]["verification_info_message"] = message await ctx.send(await self.bot._(ctx.guild.id, "antikikoo.msg-edited")) - + @commands.command(name='pass_message') @commands.guild_only() @commands.has_permissions(administrator=True) @@ -151,7 +152,7 @@ async def pass_message( context.guild.id, "antikikoo.pass-edited", ) ) - + @commands.command(name='info_channel') @commands.guild_only() @commands.has_permissions(administrator=True) @@ -171,7 +172,7 @@ async def info_channel( channel=channel.mention, ) ) - + @commands.command(name='verification_role') @commands.guild_only() @commands.has_permissions(administrator=True) @@ -193,7 +194,7 @@ async def verification_role( ), allowed_mentions=discord.AllowedMentions.none(), ) - + @commands.command(name='verification_add_role') @commands.guild_only() @commands.has_permissions(administrator=True) diff --git a/plugins/ban/ban.py b/plugins/ban/ban.py index 564acb38..ec73e9c9 100644 --- a/plugins/ban/ban.py +++ b/plugins/ban/ban.py @@ -18,7 +18,7 @@ async def ban_perm_check(ctx: commands.Context) -> bool: """Checks if the user has the permission to ban""" - + self: Ban = ctx.bot.get_cog("Ban") if ctx.guild.id not in self.friendly_ban_guilds: @@ -27,12 +27,12 @@ async def ban_perm_check(ctx: commands.Context) -> bool: for role in ctx.author.roles: if role.id in self.friendly_ban_whitelisted_roles: return True - + return await commands.has_guild_permissions(ban_members=True).predicate(ctx) async def fake_ban_guild_check(ctx: commands.Context) -> bool: """Checks if the guild is configured for the friendly ban command""" - + self: Ban = ctx.bot.get_cog("Ban") return ctx.guild.id in self.friendly_ban_guilds @@ -63,12 +63,13 @@ async def on_member_join(self, member: discord.Member): forbidden: list[discord.Role] = [] for role in self.roles_backup.pop(member.id): - if role.id != role.guild.id and role not in member.roles: # We ignore the @everyone role + # We ignore the @everyone role + if role.id != role.guild.id and role not in member.roles: try: await member.add_roles(role) except discord.Forbidden: forbidden.append(role) - + # send a message to the user if some roles could not be given back if len(forbidden) > 0: await member.send( @@ -130,7 +131,7 @@ async def ban( # indicate that they should be ignored if await event(self, ctx, user, reason): return - + # Pick a random event and execute it if no systematic event has been executed # random events should always run successfully await random.choice(self.random_events)(self, ctx, user, reason) @@ -248,10 +249,10 @@ def load_friendly_ban(self): f"plugins.ban.events.{event['module_name']}" ).execute ) - + # initiate the cache for the banned users roles self.roles_backup: dict[int,list[discord.Role]] = {} - + async def fake_ban( self, ctx: commands.Context, @@ -293,9 +294,9 @@ async def fake_ban( self.roles_backup[user.id] = ctx.guild.get_member( user.id ).roles - + try: - await ctx.guild.kick(user, reason=f"Auto-ban!") + await ctx.guild.kick(user, reason="Auto-ban!") except discord.Forbidden: if show_error: await ctx.send( @@ -308,5 +309,5 @@ async def fake_ban( return True # The end. -async def setup(bot:Gunibot=None): - await bot.add_cog(Ban(bot), icon="🔨") \ No newline at end of file +async def setup(bot:Gunibot): + await bot.add_cog(Ban(bot), icon="🔨") diff --git a/plugins/channelArchive/channelArchive.py b/plugins/channelArchive/channelArchive.py index 2ca0632f..854f6f52 100644 --- a/plugins/channelArchive/channelArchive.py +++ b/plugins/channelArchive/channelArchive.py @@ -36,7 +36,7 @@ async def config_archive_category( @commands.command(name="archive_duration") async def config_archive_duration( - self, ctx: MyContext, duration: commands.Greedy[args.tempdelta] + self, ctx: MyContext, duration: commands.Greedy[args.tempdelta], ): duration = sum(duration) if duration == 0: @@ -46,10 +46,10 @@ async def config_archive_duration( ) return duration = None - x = await self.bot.sconfig.edit_config( + msg = await self.bot.sconfig.edit_config( ctx.guild.id, "archive_duration", duration ) - await ctx.send(x) + await ctx.send(msg) async def cog_unload(self): self.update_loop.cancel() # pylint: disable=no-member @@ -86,11 +86,11 @@ async def update( records = self.bot.db_query(query, ()) # Adding manually archived channels - channelIds = [] + channel_ids = [] added = 0 for channel in self.bot.get_channel(archive_category).channels: listed = False - channelIds.append(channel.id) + channel_ids.append(channel.id) for record in records: if channel.id == record["channel"]: listed = True @@ -107,7 +107,8 @@ async def update( self.bot.get_channel(record["channel"]).category.id != archive_category ): - query = f"DELETE FROM archive WHERE channel = {record['channel']} AND guild = {guild.id}" + query = f"DELETE FROM archive WHERE channel = {record['channel']}"\ + f"AND guild = {guild.id}" unarchived += 1 self.bot.db_query(query, ()) @@ -116,11 +117,13 @@ async def update( for record in records: if self.bot.get_channel(record["channel"]) is None: removed_records += 1 - query = f"DELETE FROM archive WHERE channel = {record['channel']} AND guild = {guild.id}" + query = f"DELETE FROM archive WHERE channel = {record['channel']}"\ + f"AND guild = {guild.id}" self.bot.db_query(query, ()) # Get & delete old channels - query = f"SELECT * FROM archive WHERE timestamp <= datetime('now','-{duration} seconds') AND guild = {guild.id}" + query = f"SELECT * FROM archive WHERE timestamp <= datetime('now','-{duration} seconds')"\ + f"AND guild = {guild.id}" records = self.bot.db_query(query, ()) removed_channels = 0 @@ -133,7 +136,8 @@ async def update( # Remove record removed_records += 1 - query = f"DELETE FROM archive WHERE channel = {record['channel']} AND guild = {guild.id}" + query = f"DELETE FROM archive WHERE channel = {record['channel']}"\ + f"AND guild = {guild.id}" self.bot.db_query(query, ()) # Send confirmation diff --git a/plugins/contact/contact.py b/plugins/contact/contact.py index 328a27df..36997ead 100644 --- a/plugins/contact/contact.py +++ b/plugins/contact/contact.py @@ -81,7 +81,7 @@ async def config_contact_title(self, ctx: MyContext, *, title): await self.bot._(self, ctx.guild.id, "contact.invalid-title") ) - async def urlToByte(self, url: str) -> typing.Optional[bytes]: + async def url_to_byte(self, url: str) -> typing.Optional[bytes]: async with aiohttp.ClientSession( timeout=aiohttp.ClientTimeout(total=10) ) as session: @@ -92,23 +92,23 @@ async def urlToByte(self, url: str) -> typing.Optional[bytes]: res = None return res - def db_get_channels(self, guildID: int): + def db_get_channels(self, guild_id: int): query = "SELECT * FROM contact_channels WHERE guild=?" - res = self.bot.db_query(query, (guildID,)) + res = self.bot.db_query(query, (guild_id,)) return res - def db_add_channel(self, channel: discord.TextChannel, authorID): + def db_add_channel(self, channel: discord.TextChannel, author_id): try: query = ( "INSERT INTO contact_channels (guild,channel, author) VALUES (?, ?, ?)" ) - self.bot.db_query(query, (channel.guild.id, channel.id, authorID)) - except sqlite3.OperationalError as e: - print(e) + self.bot.db_query(query, (channel.guild.id, channel.id, author_id)) + except sqlite3.OperationalError as exc: + print(exc) - def db_delete_channel(self, guildID: int, channelID: int): + def db_delete_channel(self, guild_id: int, channel_id: int): query = "DELETE FROM contact_channels WHERE guild=? AND channel=?" - self.bot.db_query(query, (guildID, channelID)) + self.bot.db_query(query, (guild_id, channel_id)) @commands.Cog.listener() async def on_message(self, message: discord.Message): @@ -162,9 +162,9 @@ async def on_message(self, message: discord.Message): ) self.db_add_channel(channel, message.author.id) - except discord.errors.Forbidden as e: + except discord.errors.Forbidden as exc: await self.bot.get_cog("Errors").on_error( - e, await self.bot.get_context(message) + exc, await self.bot.get_context(message) ) return try: @@ -202,8 +202,8 @@ async def ct_clear(self, ctx: commands.Context, days: int = 15): try: await chan.delete(reason="Channel too old") i += 1 - except discord.DiscordException as e: - errors.append(str(e)) + except discord.DiscordException as exc: + errors.append(str(exc)) else: self.db_delete_channel(ctx.guild.id, data["channel"]) answer = await self.bot._(ctx.guild.id, "contact.deleted", count=i) diff --git a/plugins/contact/credits.md b/plugins/contact/credits.md index dbafbba5..ecc6cd7f 100644 --- a/plugins/contact/credits.md +++ b/plugins/contact/credits.md @@ -1,6 +1,6 @@ Copyright © ZRunner 2021 Copyright © Leirof 2021 - 2022 -Copyright © ascpial 2021 +Copyright © ascpial 2021 - 2023 Copyright © Aeris One 2022 Ce programme est régi par la licence CeCILL soumise au droit français et diff --git a/plugins/general/credits.md b/plugins/general/credits.md index 0c1054ff..48f99531 100644 --- a/plugins/general/credits.md +++ b/plugins/general/credits.md @@ -1,6 +1,7 @@ Copyright © ZRunner 2021 Copyright © Leirof 2021 - 2022 Copyright © Aeris One 2022 +Copyright © ascpial 2023 Ce programme est régi par la licence CeCILL soumise au droit français et respectant les principes de diffusion des logiciels libres. Vous pouvez diff --git a/plugins/general/general.py b/plugins/general/general.py index b0d165e4..9884b495 100644 --- a/plugins/general/general.py +++ b/plugins/general/general.py @@ -16,6 +16,7 @@ from utils import Gunibot, MyContext +CPU_INTERVAL = 3.0 CHANNEL_TYPES = Union[ discord.Thread, discord.abc.GuildChannel, @@ -39,8 +40,8 @@ async def count_lines_code(self): continue for file in files: if file.endswith(".py"): - with open(os.path.join(root, file), "r", encoding="utf8") as f: - for line in f.read().split("\n"): + with open(os.path.join(root, file), "r", encoding="utf8") as file: + for line in file.read().split("\n"): if len(line.strip()) > 2 and line[0] != "#": count += 1 except Exception as exception: # pylint: disable=broad-exception-caught @@ -48,7 +49,7 @@ async def count_lines_code(self): self.codelines = count @commands.command(name="hs") - async def hs(self, ctx: MyContext, channel: CHANNEL_TYPES = None): + async def hs(self, ctx: MyContext, channel: CHANNEL_TYPES = None): # pylint: disable=invalid-name if channel: msg = await self.bot._( ctx.channel, @@ -69,59 +70,58 @@ async def hs(self, ctx: MyContext, channel: CHANNEL_TYPES = None): @commands.command(name="ping") async def rep(self, ctx: MyContext): """Get bot latency""" - m = await ctx.send("Ping...") - t = (m.created_at - ctx.message.created_at).total_seconds() + msg = await ctx.send("Ping...") + time = (msg.created_at - ctx.message.created_at).total_seconds() try: - p = round(self.bot.latency * 1000) + ping = round(self.bot.latency * 1000) except OverflowError: - p = "∞" - await m.edit( - content=":ping_pong: Pong !\nBot ping: {}ms\nDiscord ping: {}ms".format( - round(t * 1000), p - ) + ping = "∞" + await msg.edit( + content=f":ping_pong: Pong !\nBot ping: {round(time * 1000)}ms\nDiscord ping: {ping}ms" ) @commands.command(name="stats") @commands.cooldown(2, 60, commands.BucketType.guild) async def stats(self, ctx: MyContext): """Display some statistics about the bot""" - v = sys.version_info - version = str(v.major) + "." + str(v.minor) + "." + str(v.micro) + v_info = sys.version_info + version = str(v_info.major) + "." + str(v_info.minor) + "." + str(v_info.micro) pid = os.getpid() try: - py = psutil.Process(pid) - ram_usage = round(py.memory_info()[0] / 2.0**30, 3) # , py.cpu_percent()] + process = psutil.Process(pid) + ram_usage = round(process.memory_info()[0] / 2.0**30, 3) # , py.cpu_percent()] except OSError: ram_usage = latency = "?" - py = None + process = None + latency = round(self.bot.latency * 1000, 3) - CPU_INTERVAL = 3.0 + try: async with ctx.channel.typing(): branch = Repo(os.getcwd()).active_branch len_servers = len(ctx.bot.guilds) users = len(ctx.bot.users) bots = len([None for u in ctx.bot.users if u.bot]) - d = await self.bot._(ctx.channel, "general.stats.servs", c=len_servers) - d += "\n" + await self.bot._( + stats = await self.bot._(ctx.channel, "general.stats.servs", c=len_servers) + stats += "\n" + await self.bot._( ctx.channel, "general.stats.members", c=users, bots=bots ) - d += "\n" + await self.bot._( + stats += "\n" + await self.bot._( ctx.channel, "general.stats.codelines", c=self.codelines ) - d += "\n" + await self.bot._( + stats += "\n" + await self.bot._( ctx.channel, "general.stats.pyver", v=version ) - d += "\n" + await self.bot._( + stats += "\n" + await self.bot._( ctx.channel, "general.stats.diver", v=discord.__version__ ) - d += "\n" + await self.bot._(ctx.channel, "general.stats.git", b=branch) - d += "\n" + await self.bot._( + stats += "\n" + await self.bot._(ctx.channel, "general.stats.git", b=branch) + stats += "\n" + await self.bot._( ctx.channel, "general.stats.ram", c=ram_usage ) cpu_txt = await self.bot._(ctx.channel, "general.stats.cpu-loading") - d += "\n" + cpu_txt - d += "\n" + await self.bot._( + stats += "\n" + cpu_txt + stats += "\n" + await self.bot._( ctx.channel, "general.stats.ping", c=latency ) if ctx.can_send_embed: @@ -132,30 +132,30 @@ async def stats(self, ctx: MyContext): title=title, color=8311585, timestamp=ctx.message.created_at, - description=d, + description=stats, ) embed.set_thumbnail(url=self.bot.user.display_avatar) msg: discord.Message = await ctx.send(embed=embed) - if py is None: # PSUtil can't be used + if process is None: # PSUtil can't be used cpu_usage = "?" else: - cpu_usage = py.cpu_percent(CPU_INTERVAL) + cpu_usage = process.cpu_percent(CPU_INTERVAL) cpu_ended = await self.bot._( ctx.channel, "general.stats.cpu-ended", c=cpu_usage ) embed.description = embed.description.replace(cpu_txt, cpu_ended) await msg.edit(embed=embed) else: - msg = await ctx.send(d) - if py is None: # PSUtil can't be used + msg = await ctx.send(stats) + if process is None: # PSUtil can't be used cpu_usage = "?" else: - cpu_usage = py.cpu_percent(CPU_INTERVAL) + cpu_usage = process.cpu_percent(CPU_INTERVAL) cpu_ended = await self.bot._( ctx.channel, "general.stats.cpu-ended", c=cpu_usage ) - d = d.replace(cpu_txt, cpu_ended) - await msg.edit(content=d) + stats = stats.replace(cpu_txt, cpu_ended) + await msg.edit(content=stats) except Exception as exception: # pylint: disable=broad-exception-caught await ctx.bot.get_cog("Errors").on_command_error(ctx, exception) @@ -169,4 +169,4 @@ async def halp(self, ctx): async def setup(bot:Gunibot=None): if bot is not None: - await bot.add_cog(General(bot), icon="🌍") \ No newline at end of file + await bot.add_cog(General(bot), icon="🌍") diff --git a/plugins/giveaways/credits.md b/plugins/giveaways/credits.md index 2a3786d0..c02cbab5 100644 --- a/plugins/giveaways/credits.md +++ b/plugins/giveaways/credits.md @@ -1,6 +1,6 @@ Copyright © ZRunner 2021 Copyright © Leirof 2021-2022 -Copyright © ascpial 2021 +Copyright © ascpial 2021 - 2023 Copyright © Aeris One 2022 Ce programme est régi par la licence CeCILL soumise au droit français et diff --git a/plugins/giveaways/giveaways.py b/plugins/giveaways/giveaways.py index e6f96b39..2cec2760 100644 --- a/plugins/giveaways/giveaways.py +++ b/plugins/giveaways/giveaways.py @@ -77,22 +77,24 @@ def db_add_giveaway( message, dumps(list()), ) - query = "INSERT INTO giveaways (guild, channel, name, max_entries, ends_at, message, users) VALUES (?, ?, ?, ?, ?, ?, ?)" + query = "INSERT INTO giveaways"\ + "(guild, channel, name, max_entries, ends_at, message, users)"\ + "VALUES (?, ?, ?, ?, ?, ?, ?)" rowid: int = self.bot.db_query(query, data) return rowid - def db_get_giveaways(self, guildID: int) -> List[dict]: + def db_get_giveaways(self, guild_id: int) -> List[dict]: """ Get giveaways attached to a server guildID: the guild (server) ID Returns: a list of dicts containing the giveaways info """ query = "SELECT rowid, * FROM giveaways WHERE guild=?" - liste = self.bot.db_query(query, (guildID,)) + liste = self.bot.db_query(query, (guild_id,)) res = list(map(dict, liste)) - for r in res: - r["users"] = loads(r["users"]) - r["ends_at"] = datetime.datetime.strptime(r["ends_at"], "%Y-%m-%d %H:%M:%S") + for data in res: + data["users"] = loads(data["users"]) + data["ends_at"] = datetime.datetime.strptime(data["ends_at"], "%Y-%m-%d %H:%M:%S") return res def db_get_expired_giveaways(self) -> List[dict]: @@ -103,24 +105,24 @@ def db_get_expired_giveaways(self) -> List[dict]: query = "SELECT rowid, * FROM giveaways WHERE ends_at <= ? AND running = 1" liste = self.bot.db_query(query, (datetime.datetime.now(),)) res = list(map(dict, liste)) - for r in res: - r["users"] = loads(r["users"]) - r["ends_at"] = datetime.datetime.strptime(r["ends_at"], "%Y-%m-%d %H:%M:%S") + for data in res: + data["users"] = loads(data["users"]) + data["ends_at"] = datetime.datetime.strptime(data["ends_at"], "%Y-%m-%d %H:%M:%S") return res - def db_get_users(self, rowID: int) -> List[int]: + def db_get_users(self, row_id: int) -> List[int]: """ Get the users participating into a giveaway via command rowID: the ID of the giveaway to edit Returns: list of users IDs """ query = "SELECT users FROM giveaways WHERE rowid=?" - res = self.bot.db_query(query, (rowID,)) + res = self.bot.db_query(query, (row_id,)) if len(res) == 0: return None return loads(res[0]["users"]) - def db_edit_participant(self, rowID: int, userID: int, add: bool = True) -> bool: + def db_edit_participant(self, row_id: int, user_id: int, add: bool = True) -> bool: """ Add a participant to a giveaway rowID: the ID of the giveaway to edit @@ -128,49 +130,49 @@ def db_edit_participant(self, rowID: int, userID: int, add: bool = True) -> bool add: if we should add or remove the user Returns: if the operation succeed """ - current_participants = self.db_get_users(rowID) + current_participants = self.db_get_users(row_id) if current_participants is None: # means that the giveaway doesn't exist return False if add: - if userID in current_participants: + if user_id in current_participants: # user was already participating return - current_participants = dumps(current_participants + [userID]) + current_participants = dumps(current_participants + [user_id]) else: try: - current_participants.remove(userID) + current_participants.remove(user_id) except ValueError: # user was not participating return current_participants = dumps(current_participants) query = "UPDATE giveaways SET users=? WHERE rowid=?" - rowcount = self.bot.db_query(query, (rowID, userID), returnrowcount=True) + rowcount = self.bot.db_query(query, (row_id, user_id), returnrowcount=True) return rowcount != 0 - def db_stop_giveaway(self, rowID: int) -> bool: + def db_stop_giveaway(self, row_id: int) -> bool: """ Stop a giveaway rowID: the ID of the giveaway to stop Returns: if the giveaway has successfully been stopped """ query = "UPDATE giveaways SET running=0 WHERE rowid=?" - rowcount = self.bot.db_query(query, (rowID,), returnrowcount=True) + rowcount = self.bot.db_query(query, (row_id,), returnrowcount=True) return rowcount == 1 - def db_delete_giveaway(self, rowID: int) -> bool: + def db_delete_giveaway(self, row_id: int) -> bool: """ Delete a giveaway from the database rowID: the ID of the giveaway to delete Returns: if the giveaway has successfully been deleted """ query = "DELETE FROM giveaways WHERE rowid=?" - rowcount = self.bot.db_query(query, (rowID,), returnrowcount=True) + rowcount = self.bot.db_query(query, (row_id,), returnrowcount=True) return rowcount == 1 - async def get_allowed_emojis(self, guildID: int) -> List[Union[discord.Emoji, str]]: + async def get_allowed_emojis(self, guild_id: int) -> List[Union[discord.Emoji, str]]: """Get a list of allowed emojis for a specific guild""" - value = self.bot.server_configs[guildID]["giveaways_emojis"] + value = self.bot.server_configs[guild_id]["giveaways_emojis"] if value is None: return None @@ -200,7 +202,10 @@ async def giveaway(self, ctx: MyContext): async def start(self, ctx: MyContext, *, settings: str): """Start a giveaway Usage" - [p]giveaway start name: ; duration: