diff --git a/bot.py b/bot.py index d612074..3b3f386 100644 --- a/bot.py +++ b/bot.py @@ -1,48 +1,65 @@ +""" +Module of a python bot named Conjurer - used to work on BDSM discord servers. +""" # This Python file uses the following encoding: utf-8 -#pylint: disable=too-many-lines -#pylint: disable=line-too-long -''' -Docstring -''' +# trunk-ignore-all(flake8/E501) +# trunk-ignore-all(bandit/B311) +# trunk-ignore-all(mypy/index) +# pylint: disable=line-too-long +# pylint: disable=too-many-lines +import asyncio +import io +import json +import logging +import netrc + # *=========================================== Standard Library Imports import os -import json -import re -import netrc -import logging -from logging import handlers -from datetime import datetime -import sys -from sys import platform -from platform import uname -import io import random -import asyncio -from pathlib import PurePath -from pathlib import Path +import re import shutil +import sys +from datetime import datetime +from logging import handlers +from pathlib import Path, PurePath +from platform import uname +from sys import platform +from typing import List, TypedDict, Union # *==============Imported libraries -import pdf2image -import numpy as np -import PyPDF2 -import openai -import requests -import eyed3 -import spotipy -from spotipy.oauth2 import SpotifyClientCredentials import discord +import eyed3 +import numpy as np +import openai +import pdf2image +import PyPDF2 +import requests +import spotipy from discord.ext import commands +from spotipy.oauth2 import SpotifyClientCredentials + +import yt_dlp from spotify_dl import spotify from spotify_dl import youtube as youtube_download -import yt_dlp +Movie = TypedDict("Movie", {"name": str, "year": int}) +movie: Movie = {"name": "Blade Runner", "year": 1982} + +Music_Config = TypedDict( + "Music_Config", + { + "playing": bool, + "ctx": Union[str, None], + "queue": List[str], + "requestor": List[str], + }, +) # *=========================================== Predefines formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") master_timeout = datetime.now() INITIAL_TIME_WAIT = 5 -MUZYKA = {"playing": False, "ctx": None, "queue": [], "requestor": []} +MUZYKA: Music_Config = {"playing": False, "ctx": None, "queue": [], "requestor": []} LOGFILE = "" NETRC_FILE = "" @@ -53,14 +70,14 @@ SETTINGS_FILE = "" ENCODING = "" GRAPHICS_PATH = "" # *=========================================== Platform Specific Predefines -if platform in("linux","linux2"): +if platform in ("linux", "linux2"): if "microsoft-standard" in uname().release: LOGFILE = "/home/pi/Conjurer/discord.log" MEMORY_FIVE_SIARA = "/home/pi/Conjurer/pamiec.json" MEMORY_FIVE_MUZYKA = "/home/pi/Conjurer/pamiec_muzyki.json" MUSIC_FOLDER = "/home/pi/RetroPie/mp3/" SETTINGS_FILE = "/home/pi/Conjurer/settings.json" - NETRC_FILE = "/home/pi/.netrc" + NETRC_FILE = "/home/pi/netrc" LOGSTORE = "/home/pi/RetroPie/logs/" ACCIDENT_LOG = "/home/pi/Conjurer/accident_log.json" ENCODING = "utf-8" @@ -88,19 +105,18 @@ elif platform == "win32": ACCIDENT_LOG = "accident_log.json" ENCODING = "cp1250" - # *=========================================== Keys -netrc = netrc.netrc(NETRC_FILE) +netrc_mod = netrc.netrc(NETRC_FILE) REMOTE_HOST_NAME = "discord" -authTokens = netrc.authenticators(REMOTE_HOST_NAME) +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) TOKEN = authTokens[2] REMOTE_HOST_NAME = "openai" -authTokens = netrc.authenticators(REMOTE_HOST_NAME) +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) openai.api_key = authTokens[2] REMOTE_HOST_NAME = "spotipy" -authTokens = netrc.authenticators(REMOTE_HOST_NAME) +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) spotify_ctrl = spotipy.Spotify( client_credentials_manager=SpotifyClientCredentials( client_id=authTokens[0], @@ -131,7 +147,6 @@ random.seed() logger.debug(platform) logger.info("Playlist generation") - # *=========================================== Load Data files music_file_list = [] for mp3_item in Path.glob(Path(MUSIC_FOLDER), "**/*.mp3"): @@ -145,7 +160,7 @@ with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as temp_memory_file: with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as temp_music_memory_file: # First we load existing data into a dict. message_table_muzyka = json.load(temp_music_memory_file) -with open(SETTINGS_FILE,"r",encoding=ENCODING) as f_settings_file: +with open(SETTINGS_FILE, "r", encoding=ENCODING) as f_settings_file: data = json.load(f_settings_file) word_reactions = data["word_reactions"] historia_fabryczki = data["fabryczka"] @@ -160,21 +175,28 @@ client = commands.Bot(intents=intents, command_prefix="$") @client.event async def on_ready(): - """_summary_ - """ + """Metoda wywoływana przy połączeniu do serwera.""" logger.debug("%s has connected to Discord!", client.user) await client.change_presence(activity=discord.Game(name="Axe Throwing Darts")) await client.tree.sync() - #await tree.sync(guild=discord.Object(id=Your guild id)) + # await tree.sync(guild=discord.Object(id=Your guild id)) await check() @client.event +# trunk-ignore(pylint/R0915) +# trunk-ignore(pylint/R0914) +# trunk-ignore(pylint/R0912) async def on_message(message): - """_summary_ - Args: - message (_type_): _description_ + """ + Handle incoming messages in a Discord server, perform various + checks and actions based on the content and context of the message, and respond accordingly. + + :param message: The message object that is received when a user sends a message in a Discord server + or DM. The code is checking various conditions and performing actions based on the content of the + message and the context in which it was sent. It also includes TODOs for future improvements + :return: The function `on_message` is being returned. """ vykidailo = False channel = None @@ -255,7 +277,7 @@ async def on_message(message): vykidailo = True if role.name == "Bartender": bartender = True - global MESSAGE_TABLE # pylint: disable=global-statement + global MESSAGE_TABLE # pylint: disable=global-statement result, MESSAGE_TABLE = await handle_response( prompt, vykidailo, bartender, MESSAGE_TABLE, username, False ) @@ -269,18 +291,18 @@ async def on_message(message): prompt=message.content, n=1, size="1024x1024" ) image_url = response["data"][0]["url"] - logger.debug("Wynikowy obrazek pod url: %s",image_url) + logger.debug("Wynikowy obrazek pod url: %s", image_url) response = requests.get(image_url, timeout=360) temp_file_name = message.content + ".png" - temp_file_name = ( - GRAPHICS_PATH+ message.content + ".png" - ) + temp_file_name = GRAPHICS_PATH + message.content + ".png" with open(temp_file_name, "wb") as dalle_file: dalle_file.write(response.content) logger.info("Koniec procedury obrazkowej.") - fnord = discord.File(temp_file_name, spoiler=False, description=message.content) + fnord = discord.File( + temp_file_name, spoiler=False, description=message.content + ) await message.reply(file=fnord) @@ -288,11 +310,19 @@ async def on_message(message): async def connect(ctx, arg=None): - """_summary_ + """ + Connect the bot to a voice channel if it is not already connected. - Args: - ctx (_type_): _description_ - arg (_type_, optional): _description_. Defaults to None. + :param ctx: ctx is short for context and refers to the context in which the command was invoked. It + contains information about the message, the channel, the server, and the user who invoked the + command + :param arg: The `arg` parameter is an optional argument that can be passed to the `connect` + function. It is not used in the code snippet provided, but it could potentially be used to specify a + specific voice channel to connect to + :return: If the voice client is already connected, the function will return without doing anything. + If the function successfully connects to the voice channel, it will return a voice channel + connection object. If it is not possible to connect to the voice channel, the function will log an + error message and return nothing. """ if ctx and arg: logger.info("Ctx and arg defined for connect") @@ -307,11 +337,16 @@ async def connect(ctx, arg=None): logger.error("Not possible to connect to voice") logger.info("Connecting to voice") -async def disconnect(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +async def disconnect(ctx): + """ + Asynchronous Python function that disconnects the voice client if it is connected and + logs a message if the context is defined. + + :param ctx: ctx is short for context and refers to the context in which a command is being executed. + It contains information about the message, the user who sent the message, the channel the message + was sent in, and more. In this case, the `disconnect` function is likely being called as a command + in """ if ctx: logger.info("Ctx defined for disconnect") @@ -321,16 +356,25 @@ async def disconnect(ctx): await voice_client.disconnect() +# trunk-ignore(pylint/R0915) +# trunk-ignore(pylint/R0914) +# trunk-ignore(pylint/R0912) async def play(ctx, zamawial=None, arg=None): - """_summary_ + """ + Play a music file, retrieve metadata about the file, and send a + message to a Discord channel with information about the song being played. - Args: - ctx (_type_): _description_ - zamawial (_type_, optional): _description_. Defaults to None. - arg (_type_, optional): _description_. Defaults to None. + :param ctx: The "ctx" parameter is a context object that contains information about the current + Discord command invocation, such as the message, the channel, and the user who invoked the command. + It is passed to the function automatically by the Discord.py library + :param zamawial: The parameter `zamawial` is a variable that stores the user who requested the song + to be played. It is an optional parameter and can be None if no user requested the song + :param arg: The `arg` parameter is an optional argument that can be passed to the `play` function. + It is not used in the code provided, so its purpose is unclear without additional context """ # TODO: Uzupełnianie metadanych. Jeśli ich nie ma sprawdzamy nazwę (w sposób inteligentny - czyli jeśli składa się z samych cyfr i/lub słowa track to sprawdzić album w sensie folder) - # TODO: Jeśli w metadanych jest tylko "audiotrack" i "album" też po folderze. Jeśli są niekompletne uzupełnić dalej. + # TODO: Jeśli w metadanych jest tylko "audiotrack" i "album" też po + # folderze. Jeśli są niekompletne uzupełnić dalej. logger.info("Play procedure") if arg: logger.info("Arg defined for play") @@ -351,7 +395,7 @@ async def play(ctx, zamawial=None, arg=None): metadata = eyed3.load(file_to_play) query = None separator = None - if platform == "linux" or platform == "linux2": + if platform in ("linux", "linux2"): separator = "/" elif platform == "win32": separator = "\\" @@ -392,20 +436,20 @@ async def play(ctx, zamawial=None, arg=None): username = ctx.message.author.nick else: username = ctx.message.author.name - global MESSAGE_TABLE # pylint: disable=global-statement + global MESSAGE_TABLE # pylint: disable=global-statement result, MESSAGE_TABLE = await handle_response( query, vykidailo, bartender, message_table_muzyka, username, True ) - logger.debug("Obecna historia czatu: %s",message_table_muzyka) + logger.debug("Obecna historia czatu: %s", message_table_muzyka) await ctx.send(result) async def check(): - """_summary_ - """ + """Funkcja sprawdzająca czy grać następny kawałek.""" while True: # TODO: Tu dołożyć skanowanie rozmiaru pliku logowania oraz plików pamięci i przenoszenie ich na większy dysk. # TODO: Dodać typing i dać znać że wykonuje operacje porządkowania Baru (zamiata, układa szklanki itp.) + # trunk-ignore(codespell/misspelled) # TODO: dlaczego sie tu wypierdala if client.voice_clients: voice_client = client.voice_clients[0] @@ -429,8 +473,12 @@ async def check(): "*Conjurer porządkuje bar, wypala szklanki do czysta miotaczem płomieni ze swojej zbroi i ogólnie wygląda na zajętego....*" ) with channel.typing(): - path_newfile = f"{LOGSTORE}{MEMORY_FIVE_MUZYKA[:-4]}{datetime.now()}.json" - with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as file_music_memory: + path_newfile = ( + f"{LOGSTORE}{MEMORY_FIVE_MUZYKA[:-4]}{datetime.now()}.json" + ) + with open( + MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING + ) as file_music_memory: with open(path_newfile, "x", encoding=ENCODING) as new_file: # First we load existing data into a dict. file_data = json.load(file_music_memory) @@ -468,19 +516,25 @@ async def check(): await asyncio.sleep(60) -async def handle_response(prompt, vykidailo, bartender, history, username, music): - """_summary_ +# trunk-ignore(pylint/R0913) +async def handle_response( + prompt, vykidailo, bartender, history, username, music +): + """ + Handle responses by appending them to a history, use OpenAI to + generate a response, and then append the generated response to the history. - Args: - prompt (_type_): _description_ - vykidailo (_type_): _description_ - bartender (_type_): _description_ - history (_type_): _description_ - username (_type_): _description_ - music (_type_): _description_ - - Returns: - _type_: _description_ + :param prompt: The prompt for the OpenAI chatbot to generate a response to + :param vykidailo: It is a boolean variable that indicates whether the user invoking the function is + an administrator or not + :param bartender: The bartender parameter is a boolean value indicating whether the user making the + request is a bartender or not + :param history: A list containing the conversation history between the user and the assistant + :param username: The username of the user who initiated the conversation + :param music: The "music" parameter is a boolean value that indicates whether the conversation is + related to music or not. If it is True, the conversation history will be stored in a different file + and the response will be generated using a different model + :return: The function `handle_response` returns a tuple containing the `result` and `MESSAGE_TABLE`. """ # TODO: Wykrywać "Pracuję nad odpowiedzią i tego typu rzeczy" logger.info("Wywolanie procedury openai z promptem: %s", prompt) @@ -541,7 +595,7 @@ async def handle_response(prompt, vykidailo, bartender, history, username, music # convert back to json. json.dump(file_data, file, indent=4) else: - with open(MEMORY_FIVE_SIARA, "r+",encoding=ENCODING) as file: + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file: # First we load existing data into a dict. file_data = json.load(file) # Join new_data with file_data inside emp_details @@ -553,16 +607,22 @@ async def handle_response(prompt, vykidailo, bartender, history, username, music return result, MESSAGE_TABLE +# trunk-ignore(pylint/R0914) +# trunk-ignore(pylint/R0915) async def get_file(ctx, source, link): - """_summary_ + """ + Take in three parameters: "ctx" (context), + "source" (a string representing the source of the file), and "link" (a string representing the link + to the file). Execute it in asynchronouse way. And get file from spotify or youtube. - Args: - ctx (_type_): _description_ - source (_type_): _description_ - link (_type_): _description_ - - Returns: - _type_: _description_ + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which a command is being executed, including information such as the + message, the channel, the server, and the user who invoked the command + :param source: The source parameter is likely a string that represents the source of the file that + the user wants to retrieve. This could be a website, a cloud storage service, or any other location + where the file is stored + :param link: The "link" parameter is likely a string that represents a URL or file path to the + location of the file that the function is trying to retrieve """ item_id = None item_type = None @@ -599,11 +659,11 @@ async def get_file(ctx, source, link): if platform == "win32": dir_path = dir_path.replace("/", "\\") return dir_path, file_list - else: - await ctx.send( - "No se jaja chyba robisz, Ty byś spotifaja nie poznawszy jakby z krzaków wyskoczył i w dupę kopnął. Plejlista, kawałek albo album prosze." - ) - logger.error("Wrong link provided spotify: %s",link) + + await ctx.send( + "No se jaja chyba robisz, Ty byś spotifaja nie poznawszy jakby z krzaków wyskoczył i w dupę kopnął. Plejlista, kawałek albo album prosze." + ) + logger.error("Wrong link provided spotify: %s", link) elif source == "Youtube": link_ok = False pat = r"^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$" @@ -647,7 +707,7 @@ async def get_file(ctx, source, link): with yt_dlp.YoutubeDL(ydl_opts) as ydl: try: ydl.download([query]) - except Exception as exce: #pylint: disable=broad-exception-caught + except Exception as exce: # pylint: disable=broad-exception-caught logger.error(exce) logger.info( "Failed to download %s, make sure yt_dlp is up to date", link @@ -661,29 +721,32 @@ async def get_file(ctx, source, link): if re.match(".*" + ident + ".*", file): file_list.append(file) return dir_path, file_list - else: - await ctx.send( - "No se jaja chyba robisz, Ty byś jutuba nie poznawszy jakby z krzaków wyskoczył i w dupę kopnął. Jutuba to nie tuba którą sobie można gdzieś wsadzić." - ) - logger.error("Wrong link provided youtube: %s",link) - return "", None + await ctx.send( + "No se jaja chyba robisz, Ty byś jutuba nie poznawszy jakby z krzaków wyskoczył i w dupę kopnął. Jutuba to nie tuba którą sobie można gdzieś wsadzić." + ) + logger.error("Wrong link provided youtube: %s", link) + return "", None + # define an asynchronous generator async def async_iterator_generator(range_of_iterable): - # pylint: disable=pointless-string-statement - """_summary_ + """ + Generate asynchronouse iterator. + This is an incomplete function definition for an asynchronous iterator generator that takes a range + of iterable as input. - Args: - range (_type_): _description_ - - Yields: - _type_: _description_ + :param range_of_iterable: The parameter `range_of_iterable` is likely a range or iterable object + that the async iterator generator will iterate over asynchronously. It could be a list, tuple, set, + or any other iterable object. The generator will yield each item in the iterable object + asynchronously, allowing other code to run in between """ # normal loop for i in range_of_iterable: + # pylint: disable=pointless-string-statement # yield the result yield i - ''' Alternatywna wersja przerobienia fora na asynchroniczny + ''' + Alternatywna wersja przerobienia fora na asynchroniczny. # traverse the iterable of awaitables for item in coros: # await and get the result from the awaitable @@ -692,17 +755,24 @@ async def async_iterator_generator(range_of_iterable): print(result) ''' + +# trunk-ignore(pylint/R0914) +# trunk-ignore(pylint/R0915) +# trunk-ignore(pylint/R0912) async def wyszukaj(ctx, how_many=0): - """_summary_ - - Args: - ctx (_type_): _description_ - how_many (int, optional): _description_. Defaults to 0. - - Returns: - _type_: _description_ """ - # TODO: Potem dorobić wyszukiwania po kawałkach słów i sieć neuronową z możliwością wyrażenia opinii o dopasowaniu. + Take in a context object and an optional integer parameter "how_many" and perform search + operation on music library. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command + :param how_many: how_many is a parameter that specifies the number of search results to be returned. + It is an optional parameter with a default value of 0, which means that if no value is provided for + how_many, the function will return all search results, defaults to 0 (optional) + """ + # TODO: Potem dorobić wyszukiwania po kawałkach słów + # i sieć neuronową z możliwością wyrażenia opinii o dopasowaniu. word_list = ctx.message.content.split() logger.info("Wyszukuje") search_weight = [] @@ -716,7 +786,7 @@ async def wyszukaj(ctx, how_many=0): skip_start = 1 for word in word_list[skip_start:]: token_weight = len(word) - logger.info("Słowo kluczowe: %s",word) + logger.info("Słowo kluczowe: %s", word) itr = 0 for file in music_file_list: if platform == "win32": @@ -769,8 +839,7 @@ async def wyszukaj(ctx, how_many=0): matched_times += 1 itr += 1 - logger.info( - "Stworzylem tablice wag zajęło mi to %s",datetime.now() - time_start) + logger.info("Stworzylem tablice wag zajęło mi to %s", datetime.now() - time_start) time_start = datetime.now() item_to_search = await max_weight(search_weight) itr = 0 @@ -793,13 +862,13 @@ async def wyszukaj(ctx, how_many=0): async def max_weight(lista): - """_summary_ + """ + Take a list of weights and return the maximum weight. - Args: - list (_type_): _description_ - - Returns: - _type_: _description_ + :param lista: It seems like the parameter `lista` is a list of items, possibly representing weights. + The function name `max_weight` suggests that the function is intended to find the maximum weight + from the list. However, without more context or information about the problem, it's difficult to say + for sure what the """ maximum_weight = 0 for iterator in lista: @@ -810,12 +879,21 @@ async def max_weight(lista): # *=========================================== Define Commands -@client.hybrid_command(name = "dej_co_ze_spotifaja", description = "Podaj link do spotify - sciagnie i doda muzyke", guild=discord.Object(id=664789470779932693)) -async def dej_co_ze_spotifaja(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="dej_co_ze_spotifaja", + description="Podaj link do spotify - sciagnie i doda muzyke", + guild=discord.Object(id=664789470779932693), +) +async def dej_co_ze_spotifaja(ctx): + """ + Get a playlist or a music number from Spotify. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in all + Discord.py commands """ logger.info("Spotifaj") await ctx.send("Dej mnie chwilkę") @@ -830,7 +908,7 @@ async def dej_co_ze_spotifaja(ctx): else: separator = "/" for file in files: - global MUZYKA #pylint: disable=global-variable-not-assigned + global MUZYKA # pylint: disable=global-variable-not-assigned MUZYKA["queue"].insert( 0, dir_path @@ -844,15 +922,24 @@ async def dej_co_ze_spotifaja(ctx): music_file_list.append(file) await ctx.send( f"Dodałem do listy {dir_path}{file['artist']} - + {file['name']}.mp3" - ) + ) logger.info("Spotifaj udany") -@client.hybrid_command(name = "dej_co_z_jutuba", description = "Podaj link do youtube - sciagnie i doda muzyke",guild=discord.Object(id=664789470779932693)) -async def dej_co_z_jutuba(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="dej_co_z_jutuba", + description="Podaj link do youtube - sciagnie i doda muzyke", + guild=discord.Object(id=664789470779932693), +) +async def dej_co_z_jutuba(ctx): + """ + Download a music number from youtube. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. It allows the command to interact + with the Discord API and """ await ctx.send("Dej mnie chwilkę") logger.info("Jutub") @@ -864,19 +951,30 @@ async def dej_co_z_jutuba(ctx): _, files = await get_file(ctx, "Youtube", item_yt) if files: for file_iter in files: - global MUZYKA #pylint: disable = global-variable-not-assigned + global MUZYKA # pylint: disable=global-variable-not-assigned MUZYKA["queue"].insert(0, file_iter) music_file_list.append(file_iter) MUZYKA["requestor"].insert(0, ctx.author) await ctx.send(f"Dodałem do listy {file_iter}") logger.info("Jutub udany") -@client.hybrid_command(name = "update_banlist", description = "Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj",guild=discord.Object(id=664789470779932693)) -async def update_banlist(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="update_banlist", + description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj", + guild=discord.Object(id=664789470779932693), +) +# trunk-ignore(pylint/R0914) +# trunk-ignore(pylint/R0912) +async def update_banlist(ctx): + """ + Update a banlist in a Discord guild from preprovided list in channel. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + contains information about the context in which the command was invoked, such as the message, the + channel, the server, and the user who invoked the command. This information can be used to perform + various actions, such """ async with ctx.channel.typing(): allowed = False @@ -885,6 +983,7 @@ async def update_banlist(ctx): allowed = True if ctx.channel.id != 1102190666827702342: + # trunk-ignore(codespell/misspelled) await ctx.send("Nie wydaje mnie sie") elif not allowed: await ctx.send("Idź bo Cię zdziele") @@ -898,24 +997,31 @@ async def update_banlist(ctx): for message in messages: lines = message.content.split("\n") for line in lines: - match = re.search("\\d{18}", line) #poprawilem backslash na podwojny bo linter sie czepial + match = re.search( + "\\d{18}", + line + # trunk-ignore(codespell/misspelled) + ) # poprawilem backslash na podwojny bo linter sie czepial if match: ids_to_ban.append(match.group()) tobancunter += 1 - # 2. get list of already banned users for all servers Conjurer has rights to + # 2. get list of already banned users for all servers Conjurer + # has rights to for guild in client.guilds: bancunter = 0 - logger.info("Serwer: %s",guild) + # trunk-ignore(codespell/misspelled) + logger.info("Serwer: %s", guild) current_banlist = [] permission = True try: async for entry in guild.bans(limit=None): current_banlist.append(entry.user.id) bancunter += 1 - except: #pylint: disable=bare-except + except BaseException: # pylint: disable=broad-exception-caught permission = False if permission: - # 3. compare lists ban only users on list 2 and not on list 1 + # 3. compare lists ban only users on list 2 and not on + # list 1 cunter_counter = 0 bad_id_cunter_counter = 0 ban_list = np.setdiff1d(ids_to_ban, current_banlist) @@ -943,19 +1049,29 @@ async def update_banlist(ctx): ) await ctx.send("Zrobione tak czy inaczej") -@client.hybrid_command(nsfw=True, name = "get_image_sadox", description = "Wyświetla losową stronę z losowego komiksu FanSadox. Bardzo NSFW.",guild=discord.Object(id=664789470779932693)) -async def get_image_sadox(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + nsfw=True, + name="get_image_sadox", + description="Wyświetla losową stronę z losowego komiksu FanSadox. Bardzo NSFW.", + guild=discord.Object(id=664789470779932693), +) +async def get_image_sadox(ctx): + """ + Take in a context parameter and retrieve an image related from fansadox collection. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. In this case, it is likely being used + to determine """ logger.info("Get sadox") channel = ctx.message.channel async with channel.typing(): dir_path_sadox = "" # select random file - if platform == "linux" or platform == "linux2": + if platform in ("linux", "linux2"): dir_path_sadox = "/home/pi/RetroPie/Fansadox/" elif platform == "win32": dir_path_sadox = "C:\\Users\\mtusz\\OneDrive\\Dokumenty\\Fansadox\\" @@ -983,31 +1099,50 @@ async def get_image_sadox(ctx): await ctx.send(file=file) logger.info("Get sadox completed") -@client.hybrid_command(name = "graj_muzyko", description = "Włącza muzykę na kanale #nocna-zmiana.",guild=discord.Object(id=664789470779932693)) -async def graj_muzyko(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="graj_muzyko", + description="Włącza muzykę na kanale #nocna-zmiana.", + guild=discord.Object(id=664789470779932693), +) +async def graj_muzyko(ctx): """ + Async function named "graj_muzyko" starts music playback on channel "Nocna Zmiana". + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in all + Discord.py commands + """ + logger.info("Press play on tape") async with ctx.typing(): - global MUZYKA #pylint: disable = global-variable-not-assigned + global MUZYKA # pylint: disable=global-variable-not-assigned MUZYKA["playing"] = True MUZYKA["ctx"] = ctx await connect(ctx=ctx) await play(ctx=ctx) logger.info("Press play on tape completed") -@client.hybrid_command(name = "cisza", description = "Wyłącza muzykę i czyści plejliste.",guild=discord.Object(id=664789470779932693)) -async def cisza(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="cisza", + description="Wyłącza muzykę i czyści plejliste.", + guild=discord.Object(id=664789470779932693), +) +async def cisza(ctx): + """ + Stop playback of the music and clear the queue. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in all + Discord.py commands """ logger.info("Stop") - global MUZYKA #pylint: disable = global-variable-not-assigned + global MUZYKA # pylint: disable=global-variable-not-assigned MUZYKA["playing"] = False MUZYKA["ctx"] = None voice_client = client.voice_clients[0] @@ -1018,12 +1153,21 @@ async def cisza(ctx): MUZYKA["requestor"].pop() logger.info("Stop completed") -@client.hybrid_command(name = "dalej", description = "Przerzuca na następny kawałek",guild=discord.Object(id=664789470779932693)) -async def dalej(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="dalej", + description="Przerzuca na następny kawałek", + guild=discord.Object(id=664789470779932693), +) +async def dalej(ctx): + """ + Play next track in queue. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which a command is being executed, including information such as the + message, the channel, the server, and the user who invoked the command. The context object is used + to access and manipulate this """ if ctx: logger.info("Ctx defined for dalej") @@ -1034,12 +1178,21 @@ async def dalej(ctx): await play(ctx=ctx) logger.info("Next completed") -@client.hybrid_command(name = "daj_mi_chwile", description = "Pauzuje",guild=discord.Object(id=664789470779932693)) -async def daj_mi_chwile(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="daj_mi_chwile", + description="Pauzuje", + guild=discord.Object(id=664789470779932693), +) +async def daj_mi_chwile(ctx): + """ + Pause music playback. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in most + Discord.py commands and is """ if ctx: logger.info("Ctx defined for pause") @@ -1050,12 +1203,21 @@ async def daj_mi_chwile(ctx): voice_client.pause() logger.info("Pause completed") -@client.hybrid_command(name = "graj_dalej", description = "Odpauzowuje",guild=discord.Object(id=664789470779932693)) -async def graj_dalej(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="graj_dalej", + description="Odpauzowuje", + guild=discord.Object(id=664789470779932693), +) +async def graj_dalej(ctx): + """ + Unpause music and continue playback. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in all + Discord.py commands """ if ctx: logger.info("Ctx defined for graj_dalej") @@ -1066,25 +1228,35 @@ async def graj_dalej(ctx): logger.info("Unpause completed") -@client.hybrid_command(name = "zagraj_mi_kawalek", description = "Wyszukuje w bibliotece muzycznej Hammera kawałek który ma zostać zagrany",guild=discord.Object(id=664789470779932693)) +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="zagraj_mi_kawalek", + description="Wyszukuje w bibliotece muzycznej Hammera kawałek który ma zostać zagrany", + guild=discord.Object(id=664789470779932693), +) async def zagraj_mi_kawalek(ctx): - """_summary_ + """ + Play a song or music piece in response to a command + triggered by a user in a Discord chat context. - Args: - ctx (_type_): _description_ + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + contains information about the context in which the command was invoked, such as the message, the + channel, the server, and the user who invoked the command. This information can be used to perform + various actions, such """ async with ctx.typing(): search_time_glob = datetime.now() - logger.info("Zaczynam szukać timestamp %s",datetime.now()) + logger.info("Zaczynam szukać timestamp %s", datetime.now()) file = await wyszukaj(ctx=ctx) logger.info( "Koniec szukania(timestamp %s zajęło %s", - datetime.now(), datetime.now() - search_time_glob - ) + datetime.now(), + datetime.now() - search_time_glob, + ) if file: for plik in file: - global MUZYKA #pylint: disable=global-variable-not-assigned + global MUZYKA # pylint: disable=global-variable-not-assigned MUZYKA["queue"].insert(0, plik[1]) MUZYKA["requestor"].insert(0, ctx.author) metadata = eyed3.load(plik[1]) @@ -1102,28 +1274,39 @@ async def zagraj_mi_kawalek(ctx): "Obawiam się że nie mogę nic znaleźć u siebie. Spróbuj komendy '$dej_co_ze_spotifaja' jeśli masz link" ) -@client.hybrid_command(name = "zrob_mi_plejliste", description = "Generuje playliste - param1 - dlugosc, potem slowa wyszukiwania",guild=discord.Object(id=664789470779932693)) -async def zrob_mi_plejliste(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="zrob_mi_plejliste", + description="Generuje playliste - param1 - dlugosc, potem slowa wyszukiwania", + guild=discord.Object(id=664789470779932693), +) +async def zrob_mi_plejliste(ctx): + """ + Generate a playlist in queue. First word in this command shall be int defining length of the playlist. + Rest of the line are search terms. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in all + Discord.py commands """ reply = "Wygenerowana plejlista:\n" async with ctx.typing(): - logger.info("Zaczynam szukać timestamp %s",datetime.now()) + logger.info("Zaczynam szukać timestamp %s", datetime.now()) search_time_glob = datetime.now() dlugosc_playlisty = ctx.message.content.split()[1] file = await wyszukaj(ctx=ctx, how_many=dlugosc_playlisty) logger.info( "Koniec szukania(timestamp %s zajęło %s", - datetime.now(), datetime.now() - search_time_glob - ) + datetime.now(), + datetime.now() - search_time_glob, + ) index = 1 if file: for plik in file: - global MUZYKA #pylint: disable=global-variable-not-assigned + global MUZYKA # pylint: disable=global-variable-not-assigned MUZYKA["queue"].insert(0, plik[1]) MUZYKA["requestor"].insert(0, ctx.author) metadata = eyed3.load(plik[1]) @@ -1137,8 +1320,10 @@ async def zrob_mi_plejliste(ctx): if metadata.tag.album: reply += f" z albumu {metadata.tag.album}" if metadata.tag.title: - reply += " z prywatnej kolekcji Hammera i na Twoją specjalną rpośbę.\n" - index+=1 + reply += ( + " z prywatnej kolekcji Hammera i na Twoją specjalną rpośbę.\n" + ) + index += 1 if len(reply) > 1800: await ctx.send(reply) reply = "" @@ -1149,13 +1334,22 @@ async def zrob_mi_plejliste(ctx): await ctx.send(reply) logger.info("Plejlista zrobiona") -@client.hybrid_command(name = "przytul", description = "Przytul kogoś - daj mention po komendzie :)") -async def przytul(ctx, arg:discord.Member=None): - """_summary_ - Args: - ctx (_type_): _description_ - arg (discord.Member, optional): _description_. Defaults to None. +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="przytul", description="Przytul kogoś - daj mention po komendzie :)" +) +async def przytul(ctx, arg: Union[discord.Member, None] = None): + """ + Generate a text about hugging mentioned user. + + :param ctx: ctx stands for "context" and is a required parameter in Discord.py commands. It + represents the context in which the command is being executed, including information such as the + message, the channel, the server, and the user who invoked the command + :param arg: The parameter "arg" is a variable that can accept either a discord.Member object or None + as its value. It is an optional parameter, which means that if no value is provided for it when the + function is called, it will default to None + :type arg: Union[discord.Member, None] """ async with ctx.typing(): nieprzytulac = False @@ -1170,29 +1364,42 @@ async def przytul(ctx, arg:discord.Member=None): ) elif arg: await ctx.send( + # trunk-ignore(codespell/misspelled) f"Już dobrze.... Już dobrze... Ojej.. Biedactwo... :( *W ułamku sekundy {arg.mention} znajduje sie w duszącym uścisku. Żebra trzeszczą - kilka pęka. Pacnięcia po plecach grożą odbiciem nerek, a głaskanie po głowie powoduje wstrząs mózgu*" - ) + ) else: await ctx.send( "Kogo mam przytulić? *Wyłamuje kostki i przeciąga się - jego 200 kilowa sylwetka złożona z samych mięśni świadczy o tym że jest gotowy*" ) -@client.hybrid_command(name = "fabryczka", description = "Historia fabryczki") +# trunk-ignore(mypy/arg-type) +@client.hybrid_command(name="fabryczka", description="Historia fabryczki") async def fabryczka(ctx): - """_summary_ + """ + Send a general description of "fabryczkagate" to channel. - Args: - ctx (_type_): _description_ + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. The context object provides a way to + interact with the Discord """ await ctx.send(historia_fabryczki) -@client.hybrid_command(name = "chata_hammera", description = "Czas od ostatniego incydentu w AbsinthHammerTimeSpaceContinuum") -async def chata_hammera(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="chata_hammera", + description="Czas od ostatniego incydentu w AbsinthHammerTimeSpaceContinuum", +) +async def chata_hammera(ctx): + """ + Send measured time from last incident in Hammer Fortress to the chat. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + contains information about the context in which the command was invoked, such as the message, the + channel, the server, and the user who invoked the command. This information can be used to perform + various actions, such """ with open(ACCIDENT_LOG, "r+", encoding=ENCODING) as new_file: # First we load existing data into a dict. @@ -1201,15 +1408,22 @@ async def chata_hammera(ctx): czas = datetime.strptime(file_data[-1][1], "%Y-%m-%d %H:%M:%S.%f") await ctx.send( f"Komenda mierząca incydenty w Eldritch AbsinthHammerTimeSpaceContinuum.\n Od ostatniego incydentu w chacie Hammera minęło {datetime.now() - czas}. Incydent to: {opis}" - ) + ) -@client.hybrid_command(name = "historia_incydentow_u_hammera", description = "Wyswietla liste incydentów które miały miejsce w AbsinthHammerTimeSpaceContinuum.") +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="historia_incydentow_u_hammera", + description="Wyswietla liste incydentów które miały miejsce w AbsinthHammerTimeSpaceContinuum.", +) async def historia_incydentow_u_hammera(ctx): - """_summary_ + """ + Send a list of incidents in Hammer Fortress to the chat. - Args: - ctx (_type_): _description_ + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + contains information about the context in which the command was invoked, such as the message, the + channel, the server, and the user who invoked the command. This information can be used to perform + various actions, such """ with open(ACCIDENT_LOG, "r+", encoding=ENCODING) as new_file_accidents: # First we load existing data into a dict. @@ -1224,12 +1438,21 @@ async def historia_incydentow_u_hammera(ctx): index += 1 await ctx.send(return_data) -@client.hybrid_command(name = "reset_the_clock", description = "Resetowanie zegara - dostępne wyłącznie dla Hammera", guild=discord.Object(id=664789470779932693)) -async def reset_the_clock(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# trunk-ignore(mypy/arg-type) +@client.hybrid_command( + name="reset_the_clock", + description="Resetowanie zegara - dostępne wyłącznie dla Hammera", + guild=discord.Object(id=664789470779932693), +) +async def reset_the_clock(ctx): + """ + Reset the clock on "accidents" in Hammer Fortress adding a new one. + + :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It + represents the context in which the command was invoked, including information such as the message, + the channel, the server, and the user who invoked the command. This parameter is required in all + Discord.py commands """ hammer = False for role in ctx.message.author.roles: @@ -1248,31 +1471,42 @@ async def reset_the_clock(ctx): json.dump(file_data, new_file_accidents, indent=4) await ctx.send( f"Komenda mierząca incydenty w Eldritch AbsinthHammerTimeSpaceContinuum.\n RESET THE CLOCK. Od ostatniego incydentu w chacie Hammera minęło {czas}. No ale teraz się odpierdoliło: {opis}" - ) + ) else: await ctx.send( f"Komenda mierząca incydenty w Eldritch AbsinthHammerTimeSpaceContinuum.\n Gdzie z łapami do zegara? {client.get_user(346956223645614080).mention} coś albo się komuś stało albo zaraz się stanie jak będzie zegar tykał...." - ) -#@client.hybrid_command() -async def get_image_stable_diffusion(ctx): - """_summary_ + ) - Args: - ctx (_type_): _description_ + +# @client.hybrid_command() +async def get_image_stable_diffusion(ctx): + """ + Connect to remote stable diffusion and generate an image. Retrieve the image. + + :param ctx: ctx is short for context and is typically used in Discord.py to represent the context in + which a command is being executed. It contains information such as the message, the channel, the + author, and other relevant data that can be used to perform actions or respond to the user. In this + case, it """ if ctx: pass -#@client.hybrid_command() -async def zagraj_muzyke_mego_ludu(ctx): - """_summary_ - Args: - ctx (_type_): _description_ +# @client.hybrid_command() +async def zagraj_muzyke_mego_ludu(ctx): """ + Play completeley random playlist based on messaging history. + + :param ctx: ctx stands for "context" and is typically used in Discord.py commands to represent the + context in which the command was invoked. This includes information such as the message, the + channel, the author, and other relevant details. In this case, it is being passed as a parameter to + the `get_image + """ + if ctx: pass # *================================== Run +# trunk-ignore(mypy/arg-type) client.run(TOKEN)