From 22b33fa984dc0812bca0da42ddf775577d8aa798 Mon Sep 17 00:00:00 2001 From: Moje Imie I Nazwisko Date: Mon, 8 Jun 2026 00:44:17 +0200 Subject: [PATCH] rolled back version --- working_copy/LICENSE | 24 + working_copy/accident_log.json | 38 + working_copy/administration_commands.py | 263 +++++++ working_copy/ai_commands.py | 403 ++++++++++ working_copy/ai_functions.py | 449 ++++++++++++ working_copy/bot.py | 103 +++ working_copy/communication_subroutine.py | 231 ++++++ working_copy/constants.py | 155 ++++ working_copy/file_search_commands.py | 71 ++ working_copy/file_search_functions.py | 31 + working_copy/fuckery.jpg | Bin 0 -> 31490 bytes working_copy/latex_commands.py | 280 +++++++ working_copy/latex_functions.py | 179 +++++ working_copy/librarian_commands.py | 300 ++++++++ working_copy/librarian_functions.py | 28 + working_copy/music_commands.py | 809 +++++++++++++++++++++ working_copy/music_functions.py | 318 ++++++++ working_copy/other_commands.py | 212 ++++++ working_copy/other_functions.py | 118 +++ working_copy/pamiec.json | 254 +++++++ working_copy/pamiec_muzyki.json | 790 ++++++++++++++++++++ working_copy/radio_commands.py | 207 ++++++ working_copy/requirements_bot.txt | 22 + working_copy/settings.json | 357 +++++++++ working_copy/system_gpt_settings.json | 43 ++ working_copy/voice_recognition_commands.py | 281 +++++++ working_copy/willowisp.png | Bin 0 -> 39844 bytes working_copy/wod_beacon.jpg | Bin 0 -> 22684 bytes 28 files changed, 5966 insertions(+) create mode 100644 working_copy/LICENSE create mode 100644 working_copy/accident_log.json create mode 100644 working_copy/administration_commands.py create mode 100644 working_copy/ai_commands.py create mode 100644 working_copy/ai_functions.py create mode 100755 working_copy/bot.py create mode 100644 working_copy/communication_subroutine.py create mode 100644 working_copy/constants.py create mode 100644 working_copy/file_search_commands.py create mode 100644 working_copy/file_search_functions.py create mode 100644 working_copy/fuckery.jpg create mode 100644 working_copy/latex_commands.py create mode 100644 working_copy/latex_functions.py create mode 100644 working_copy/librarian_commands.py create mode 100644 working_copy/librarian_functions.py create mode 100644 working_copy/music_commands.py create mode 100644 working_copy/music_functions.py create mode 100644 working_copy/other_commands.py create mode 100644 working_copy/other_functions.py create mode 100644 working_copy/pamiec.json create mode 100644 working_copy/pamiec_muzyki.json create mode 100644 working_copy/radio_commands.py create mode 100644 working_copy/requirements_bot.txt create mode 100644 working_copy/settings.json create mode 100644 working_copy/system_gpt_settings.json create mode 100644 working_copy/voice_recognition_commands.py create mode 100644 working_copy/willowisp.png create mode 100644 working_copy/wod_beacon.jpg diff --git a/working_copy/LICENSE b/working_copy/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/working_copy/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/working_copy/accident_log.json b/working_copy/accident_log.json new file mode 100644 index 0000000..cd675c3 --- /dev/null +++ b/working_copy/accident_log.json @@ -0,0 +1,38 @@ +[ + [ + "Hammer oparzy\u0142 si\u0119 do II/III stopnia prodi\u017cem", + "2023-05-29 14:00:19.00000" + ], + [ + "Pat wy\u0142\u0105czy\u0142\u0105 wydruk i wywo\u0142a\u0142a spi\u0119cie na p\u0142ycie g\u0142\u00f3wnej", + "2023-06-01 15:27:43.444444" + ], + [ + "Pat zala\u0142a \u0142azienk\u0119", + "2023-06-07 20:40:43.453654" + ], + [ + "Pat przyjeba\u0142a g\u0142ow\u0105 oraz barkiem w drzwi od szafki", + "2023-06-07 22:40:43.453654" + ], + [ + "Pat oparzy\u0142a si\u0119 kawiark\u0105", + "2023-06-08 11:11:43.453654" + ], + [ + "Hammer rozci\u0105\u0142 palec trzepi\u0105c przez balkon jeleni\u0105 sk\u00f3r\u0119", + "2023-06-08 18:10:43.453654" + ], + [ + "Pat zgubi\u0142a w zamkni\u0119tym pomieszczeniu spr\u0119\u017cyn\u0119 od kuchenki gazowej", + "2023-06-08 23:29:43.453654" + ], + [ + "Hammer kupi\u0142 Je\u017cowi bilet na 16 zamiast na 09 czerwca", + "2023-06-09 14:52:43.453654" + ], + [ + " Z\u0142ama\u0142 paznokie\u0107 otwierajac piwo", + "2023-06-21 21:37:54.583469" + ] +] diff --git a/working_copy/administration_commands.py b/working_copy/administration_commands.py new file mode 100644 index 0000000..c964d48 --- /dev/null +++ b/working_copy/administration_commands.py @@ -0,0 +1,263 @@ +import json +import logging +import os +import random +import re +import shutil +from datetime import datetime + +import discord +import numpy as np +from discord.ext import commands, tasks + +from ai_functions import get_random_cyclic_message +from constants import ( + ENCODING, + LAST_SPONTANEOUS_CALL, + LOGFILE, + LOGSTORE, + MEMORY_FIVE_MUZYKA, + MEMORY_FIVE_SIARA, + TIME_BETWEEN_CALLS, +) + + +class AdministrationModule(commands.Cog): + def __init__(self, bot, logger_name): + self.bot = bot + self.logger = logging.getLogger(logger_name) + + @commands.hybrid_command( + name="galeria_slaw", + description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def galeria_slaw(self, ctx): + if isinstance(ctx.channel, discord.DMChannel): + for guild in self.bot.guilds: + async for entry in guild.bans(limit=1500): + fnord = discord.File( + "/home/pi/Conjurer/niech_spierdala.png", + spoiler=False, + description="Niech spierdala", + ) + await ctx.reply( + f"User: {entry.user} wyjebany z serwera {guild.name} za {entry.reason} ", + file=fnord, + ) + + @commands.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), + ) + @commands.has_any_role('Jarl', 'Thane') + + async def update_banlist(self, 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 + for role in ctx.author.roles: + if role.name == "Jarl": + 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") + else: + tobancunter = 0 + async with ctx.typing(): + # 1. get list of users posted on channel for banning + channel = ctx.channel + messages = [ + message async for message in channel.history(limit=None) + ] + ids_to_ban = [] + for message in messages: + lines = message.content.split("\n") + for line in lines: + 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 + for guild in self.bot.guilds: + bancunter = 0 + # trunk-ignore(codespell/misspelled) + self.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 BaseException: # pylint: disable=broad-exception-caught + permission = False + if permission: + # 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) + for ident in ban_list: + try: + tmp_user = await self.bot.fetch_user(int(ident)) + await guild.ban( + tmp_user, + reason="Automatyczna lista banów", + delete_message_seconds=0, + ) + # TODO: Maybe use guild.bulk_ban ? + cunter_counter += 1 + except discord.NotFound: + self.logger.info( + "Znaleziono uzyszkodnika ktory juz nie ma konta" + ) + bad_id_cunter_counter += 1 + # 4. return message success/failure - how many banned + await ctx.send( + "Obecnie na serwerze {guild} jest {bancunter} zbanowanych użytkowników. Znaleziono na tym kanale {tobancunter} id użytkowników do zbanowania. Zbanowano {cunter_counter} użytkowników. Na liście jest {bad_id_cunter_counter} użytkowników którzy skasowali już konto" + ) + else: + await ctx.send( + f"Nie mam na serwerze {guild} uprawnień do banowania. :()" + ) + await ctx.send("Zrobione tak czy inaczej") + @commands.hybrid_command( + name="archive_channel", + description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Jarl', 'Thane') + + async def archive_channel(self, channel_no): + """ + The function `archive_channel` retrieves messages from a specified channel, processes them, and + saves the data in a JSON file with a timestamp in the filename. + + :param channel_no: The `archive_channel` function you provided seems to be incomplete. It looks like + you are trying to archive messages from a Discord channel into a JSON file + """ + channel = self.bot.get_channel(channel_no) + messages = [message async for message in channel.history(limit=None)] + path_newfile = f"{LOGSTORE}channeldump_{channel_no}_{datetime.now()}.json" + new_data = [] + for message in messages: + new_data.append(message) + with open(path_newfile, "x", encoding=ENCODING) as new_file: + new_file.seek(0) + json.dump(new_data, new_file, indent=4) + + @tasks.loop(seconds=120) + async def check_self(self): + """ + The function `check_data` periodically checks for conditions to send messages and manage log files + in a Discord channel. + """ + # logger.info("Heartbeat of cleanup proc") + channel = self.bot.get_channel(1062047367337095268) + messages = [message async for message in channel.history(limit=1)] + for mess in messages: + channel = mess.channel + if os.path.getsize(LOGFILE) > 60000000: + await channel.send( + "*Conjurer porządkuje bar, wypala szklanki do czysta miotaczem płomieni ze swojej zbroi i ogólnie wygląda na zajętego....*" + ) + async with channel.typing(): + shutil.copyfile(LOGFILE, f"{LOGSTORE}discord{datetime.now()}") + self.logger.info("Log rollover") + handlers = self.logger.handlers + for handler in handlers: + if isinstance(handler, logging.handlers.RotatingFileHandler): + handler.doRollover() + await channel.send( + "*Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* ...sać powieści o naszym wspaniałym barze SZEFIE!" + ) + + if os.path.getsize(MEMORY_FIVE_MUZYKA) > 3000000: + await channel.send( + "*Conjurer porządkuje bar, wypala szklanki do czysta miotaczem płomieni ze swojej zbroi i ogólnie wygląda na zajętego....*" + ) + async with channel.typing(): + path_newfile = f"{LOGSTORE}pamiec_muzyki{datetime.now()}.json" + self.logger.info(path_newfile) + 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) + new_data = [] + new_data.append(file_data[0]) + new_data.extend(file_data[:-20]) + file_music_memory.truncate(0) + file_music_memory.seek(0) + # convert back to json. + new_file.seek(0) + json.dump(new_data, file_music_memory, indent=4) + json.dump(file_data, new_file, indent=4) + await channel.send( + "*Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* ...sać powieści o naszym wspaniałym barze SZEFIE!" + ) + if os.path.getsize(MEMORY_FIVE_SIARA) > 3000000: + path_newfile = f"{LOGSTORE}pamiec_rozmow{datetime.now()}.json" + self.logger.info(path_newfile) + await channel.send( + "*Conjurer porządkuje bar, ścina lekkim laserem pulsacyjnym powierzchnie baru o grubości kilku mikronów i ogólnie wygląda na zajętego....*" + ) + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file: + with open(path_newfile, "x", encoding=ENCODING) as new_file: + # First we load existing data into a dict. + file_data = json.load(file) + new_data = [] + new_data.append(file_data[0]) + new_data.extend(file_data[-20]) + file.truncate(0) + file.seek(0) + new_file.seek(0) + # convert back to json. + json.dump(new_data, file, indent=4) + json.dump(file_data, new_file, indent=4) + await channel.send( + "*Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* eprzyć o głupotach z klientami... Szefie... *Bierze 'ukradkowy' łyk z piersiówki*" + ) + global LAST_SPONTANEOUS_CALL + global TIME_BETWEEN_CALLS + tdelta = datetime.now() - LAST_SPONTANEOUS_CALL + tdelta = tdelta.total_seconds() + if tdelta > TIME_BETWEEN_CALLS: + self.logger.info("Spontaneous call") + # trunk-ignore(bandit/B311) + TIME_BETWEEN_CALLS = random.randint( + 10200, 272800 + ) # temp random, set after each call + self.logger.debug(TIME_BETWEEN_CALLS) + LAST_SPONTANEOUS_CALL = datetime.now() + async with channel.typing(): + message = await get_random_cyclic_message(self.bot) + self.logger.info("Odpowiedz") + self.logger.info(message) + await channel.send(message) + + +async def setup(bot): + logger = logging.getLogger("discord") + am = AdministrationModule(bot, "discord") + await bot.add_cog(am) + am.check_self.start() + logger.info("Loading administration commands module done") diff --git a/working_copy/ai_commands.py b/working_copy/ai_commands.py new file mode 100644 index 0000000..baa3bc1 --- /dev/null +++ b/working_copy/ai_commands.py @@ -0,0 +1,403 @@ +# ai command cogs +import logging +import re +import sys +from datetime import datetime +from enum import Enum +from typing import Optional +from pathlib import Path +import discord +import openai +import requests +from discord.ext import commands +from other_functions import discord_friendly_send, discord_friendly_reply + + +import ai_functions +from constants import ( + ASSISTANTS, + DATA, + GRAPHICS_PATH, + INITIAL_TIME_WAIT, + MASTER_TIMEOUT, + MESSAGE_TABLE, + OPENAICLIENT, + SPECJALNE_ZIEMNIACZKI, + WORD_REACTIONS, +) + + +class Dm_Mode(Enum): + ARMIA_HAMMERA = (1,) + SPECJALNY_ZIEMNIACZEK = (2,) + SEKRETNY_SEKSRET = (3,) + ECHO_ECHO = (4,) + + +class Events(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.logger = logging.getLogger("discord") + self.armia = {} + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + self.armia[superfryta[0]] = Dm_Mode.SPECJALNY_ZIEMNIACZEK + self.logger.info(self.armia) + + async def cog_load(self): + self.logger.info("Starting personal assistants") + for superfryta_id, superfryta in SPECJALNE_ZIEMNIACZKI.items(): + + if superfryta[4] != "": + self.logger.info( + "Personal assistant for user: %s, exists id: %s,name: %s, owner: %s, special instructions: %s assistant id: %s ", + superfryta_id, + superfryta[0], + superfryta[1], + superfryta[2], + superfryta[3], + superfryta[4], + ) + thread = await OPENAICLIENT.beta.threads.create() + self.logger.info("Thread id: %s", thread.id) + ASSISTANTS[superfryta[1]] = ( + superfryta[2], + superfryta[4], + superfryta[0], + thread, + ) + else: + self.logger.info( + "Creating personal assistant for user: %s, id: %s,name: %s, owner: %s, special instructions: %s", + superfryta_id, + superfryta[0], + superfryta[1], + superfryta[2], + superfryta[3], + ) + await ai_functions.create_chat_assistant( + superfryta_id, superfryta[0], superfryta[1], superfryta[2], superfryta[3] + ) + self.logger.info("Started personal assistants") + + @commands.hybrid_command( + name="switch_dm_mode", + description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj", + guild=discord.Object(id=664789470779932693), + ) + async def switch_dm_mode(self, ctx, dm_mode_arg: Dm_Mode): + async with ctx.channel.typing(): + if isinstance(ctx.channel, discord.DMChannel): + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + self.logger.info(ctx.message.author.id) + self.logger.info(superfryta) + if ctx.message.author.id == superfryta[0]: + self.armia[ctx.message.author.id] = dm_mode_arg + self.logger.info(self.armia) + await ctx.reply("Weszlo") + return + await ctx.reply( + "Tylko członkowie Armii Hammera mogą używać. Nie jesteś jednym z nich" + ) + else: + await ctx.reply("Nope. Nie wiesz jak użyć") + + @commands.hybrid_command( + name="armia_hammera", + description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj", + ) + async def armia_hammera( + self, ctx, message_txt: str, recipient: Optional[discord.User] + ): + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + if ctx.message.author.id == superfryta[0]: + await self.armia_hammera_back(ctx, message_txt, recipient) + return + await ctx.reply("Tylko członkowie Armii Hammera mogą używać. Nie jesteś jednym z nich") + + async def armia_hammera_back(self, ctx, message_txt, recipient=None): + self.logger.info("Armia Hammera") + recipients = [] + if recipient: + recipients.append(recipient.id) + else: + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + recipients.append(superfryta[0]) + self.logger.info(recipients) + for item in recipients: + user = await self.bot.fetch_user(item) + channel = await user.create_dm() + self.logger.info( + "User %s -> %s: %s", ctx.message.author, user, message_txt + ) + await discord_friendly_send(channel, message_txt) + await discord_friendly_reply(ctx, "Poszło") + + #TODO: NOT IMPLEMENTED YET + @commands.Cog.listener(name="dodaj_do_bazy_wiedzy") + async def dodaj_do_bazy_wiedzy(self, ctx): + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + if ctx.message.author.id == superfryta[0]: + #logic here + return + await ctx.reply("Tylko członkowie Armii Hammera mogą używać. Nie jesteś jednym z nich") + + #TODO: NOT IMPLEMENTED YET + @commands.Cog.listener(name="listuj_baze_wiedzy") + async def listuj_baze_wiedzy(self, ctx): + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + if ctx.message.author.id == superfryta[0]: + #logic here + return + await ctx.reply("Tylko członkowie Armii Hammera mogą używać. Nie jesteś jednym z nich") + + #TODO: NOT IMPLEMENTED YET + @commands.Cog.listener(name="usun_z_bazy_wiedzy") + async def usun_z_bazy_wiedzy(self, ctx): + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + if ctx.message.author.id == superfryta[0]: + #logic here + return + await ctx.reply("Tylko członkowie Armii Hammera mogą używać. Nie jesteś jednym z nich") + + #TODO: NOT IMPLEMENTED YET + @commands.Cog.listener(name="przetworz_plik_linia_po_linii") + async def przetworz_plik_linia_po_linii(self, ctx): + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + if ctx.message.author.id == superfryta[0]: + #logic here + return + await ctx.reply("Tylko członkowie Armii Hammera mogą używać. Nie jesteś jednym z nich") + + @commands.Cog.listener() + async def on_message(self, message): + """ + 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 + if message.author == self.bot.user: + return + + if isinstance(message.author, discord.Member): + for role in message.author.roles: + if role.name == "Vykidailo": + vykidailo = True + + if ("Conjurer Śpij Słodko Aniołku" in message.content) and vykidailo: + sys.exit() + # kanal bez bota + if message.channel.id == 1095985579147141202: + return + # wentylacja + if message.channel.id == 1083804024173764739: + return + # legendy + if message.channel.id == 1084448332841230388: + return + # interrogation booth + if message.channel.id == 1111625221171052595: + return + if isinstance(message.channel, discord.DMChannel): + self.logger.info(message.author.id) + for superfryta in SPECJALNE_ZIEMNIACZKI.values(): + self.logger.info(superfryta[0]) + + if message.author.id == superfryta[0]: + self.logger.info("Specjalny ziemniak") + if self.armia[message.author.id] == Dm_Mode.SPECJALNY_ZIEMNIACZEK: + #await self.bot.process_commands(message) + await ai_functions.chat_with_assistant(message, superfryta[1]) + return + elif self.armia[message.author.id] == Dm_Mode.ECHO_ECHO: + await ai_functions.echo(message) + return + elif self.armia[message.author.id] == Dm_Mode.ARMIA_HAMMERA: + self.logger.info("Armia Hammera get context") + ctx = await self.bot.get_context(message) + self.logger.info("Armia Hammera function call") + await self.armia_hammera_back(ctx=ctx, message_txt=message.content) + return + elif self.armia[message.author.id] == Dm_Mode.SEKRETNY_SEKSRET: + pass + else: + await discord_friendly_send(message.channel,"Coś się wyebao. Wołaj Hammera") + return + channel = self.bot.get_channel(1064888712565100614) + await discord_friendly_send(channel, "Słyszałem ja żem że: " + message.content) + return + channel = message.channel + message_content_lower = message.content.lower() + + tdelta = datetime.now() - MASTER_TIMEOUT + tdelta = tdelta.total_seconds() + if "opowiedz o fabryczce" in message_content_lower: + await message.reply(DATA["fabryczka"]) + if "opowiedz mi o fabryczce" in message_content_lower: + await message.reply(DATA["fabryczka"]) + + if tdelta > INITIAL_TIME_WAIT: + for word in WORD_REACTIONS: + if re.search(r"\b" + word + r"\b", message_content_lower): + tdelta = datetime.now() - WORD_REACTIONS[word][2] + tdelta = tdelta.total_seconds() + security_clearance = WORD_REACTIONS[word][4] + reaction = WORD_REACTIONS[word][3] + if tdelta > WORD_REACTIONS[word][1]: + # TODO: to zrobic reactiony + self.logger.info("Ping z procedury reakcji") + if reaction: + emoji = self.bot.get_emoji(WORD_REACTIONS[word][0]) + await message.add_reaction(emoji) + elif security_clearance and vykidailo: + await message.reply(WORD_REACTIONS[word][0]) + elif not security_clearance: + await message.reply(WORD_REACTIONS[word][0]) + WORD_REACTIONS[word][2] = datetime.now() + + # TODO: drobne literówki, mentiony, spacja przed dwukropkiem. napraw. + kondziu_mentioned = False + for mention in message.mentions: + if mention == self.bot.user: + kondziu_mentioned = True + + if kondziu_mentioned or "conjurer:" in message_content_lower: + async with channel.typing(): + self.logger.info("Procedura chatu") + + message_content_lower = message_content_lower.replace("conjurer: ", "") + if message.author.nick: + username = message.author.nick + else: + username = message.author.name + vykidailo = False + bartender = False + if kondziu_mentioned: + prompt = message.clean_content + else: + prompt = message.content + for role in message.author.roles: + if role.name == "Vykidailo": + vykidailo = True + if role.name == "Bartender": + bartender = True + global MESSAGE_TABLE # pylint: disable=global-statement + + result, MESSAGE_TABLE = await ai_functions.handle_response( + prompt, + vykidailo, + bartender, + MESSAGE_TABLE, + username, + "GENERAL", + ) + + await discord_friendly_reply(message, result) + if "imaginuje sobie:" in message.content: + async with channel.typing(): + self.logger.info("Poczatek procedury obrazkowej") + message_content_lower = message_content_lower.replace("imaginuje sobie: ", "") + self.logger.debug("Wywolanie obrazka: %s", message_content_lower) + try: + response = await OPENAICLIENT.images.generate( + model="dall-e-3", + prompt=message.content, + size="1024x1024", + quality="standard", + n=1, + ) + except openai.APITimeoutError as e: + # Handle timeout error, e.g. retry or log + await discord_friendly_reply( + message, f"*Kondziu patrzy na terminal, czeka, czeka, czeka,.... Jeszcze chwile czeka Przypierdala w niego pięścią....* Nie mogę się połączyć z Openai spróbuj od nowa. *Na ekranie pojawia się*: {e}" + ) + except openai.APIConnectionError as e: + await discord_friendly_reply( + message, f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Nie mogę się połączyć z Openai. *Na ekranie pojawia się*: {e}" + ) + except openai.BadRequestError as e: + # Handle invalid request error, e.g. validate parameters or log + if message.author.nick: + username = message.author.nick + else: + username = message.author.name + resp, _ = await ai_functions.handle_response( + f"Wytlumacz jakie sa zasady dotyczące treści które możesz generować używając Dalle. Wytłumacz błąd {e} prostym językiem. Przeproś za nadmierną cenzurę. Wytłumacz co mogło być nie tak w prompcie '{message.content}'", + True, + True, + MESSAGE_TABLE, + username, + "GENERAL", + ) + await discord_friendly_reply( + message, f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}" + ) + except openai.AuthenticationError as e: + # Handle authentication error, e.g. check credentials or log + await discord_friendly_reply( + message, f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Wołaj szefa - coś się z hasłem zjebało. *Na terminalu pojawia się:* {e}" + ) + except openai.PermissionDeniedError as e: + # Handle permission error, e.g. check scope or log + await discord_friendly_reply( + ( + message, f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Wołaj szefa - coś się z uprawnieniami zjebało. *Na terminalu pojawia się:* {e}" + ) + ) + except openai.RateLimitError as e: + await discord_friendly_reply( + message, f"*Kondziu patrzy na terminal* Wołaj szefa. Zapłacić rachunki za AI trzeba. Jak chcesz to się na #zebranie dorzuć. {e}" + ) + except openai.APIError as e: + # Handle API error, e.g. retry or log + await discord_friendly_reply( + message, f"*Kondziu nurkuje za bar, terminal wybucha. Przed tobą ląduje pergamin zapisany pięknym gotykiem a na nim*: {e}" + ) + if response: + self.logger.info(response) + image_url = response.data[0].url + image_desc = response.data[0].revised_prompt + self.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" + num = 0 + while (Path(temp_file_name)).exists(): + temp_file_name = GRAPHICS_PATH + message.content + str(num) + ".png" + num += 1 + try: + with open(temp_file_name, "wb") as dalle_file: + dalle_file.write(response.content) + except OSError: + temp_file_name = "/home/pi/oserror.png" + with open(temp_file_name, "wb") as dalle_file: + dalle_file.write(response.content) + except FileNotFoundError: + temp_file_name = "/home/pi/fnferror.png" + with open(temp_file_name, "wb") as dalle_file: + dalle_file.write(response.content) + except Exception as e: + self.logger.error("Nieznany błąd: %s", e) + temp_file_name = "/home/pi/error.png" + with open(temp_file_name, "wb") as dalle_file: + dalle_file.write(response.content) + finally: + self.logger.info("Koniec procedury obrazkowej.") + fnord = discord.File( + temp_file_name, spoiler=False, description=message.content + ) + + #await message.reply(f"{image_desc}", file=fnord) + await discord_friendly_reply(message,f"{image_desc}", file = fnord) +# *=========================================== Define Functions + + +async def setup(bot): + logger = logging.getLogger("discord") + await bot.add_cog(Events(bot)) + logger.info("Loading ai events module done") diff --git a/working_copy/ai_functions.py b/working_copy/ai_functions.py new file mode 100644 index 0000000..ef48fb7 --- /dev/null +++ b/working_copy/ai_functions.py @@ -0,0 +1,449 @@ +import asyncio +import json +import logging +import random + +import openai +import tiktoken +import time +from other_functions import discord_friendly_send +from constants import ( + ASSISTANTS, + CYCLIC_WORDS, + ENCODING, + GPT_SETTINGS, + MEMORY_FIVE_MUZYKA, + MEMORY_FIVE_SIARA, + MESSAGE_TABLE, + MESSAGE_TABLE_MUZYKA, + OPENAICLIENT, + SYSTEM_GPT_SETTINGS, + WORD_REACTIONS, + CHEAP_MODEL, + LATEST_MODEL +) + +# this do per user +VECTOR_STORE_ID = -1 +def select_model(req_type: str, algo: str) -> str: + # Jeżeli jawnie podano algorithm (i nie jest 'auto'/''): + if algo and str(algo).strip().lower() not in ("auto",): + # wyjątek: MUZYKA ma zawsze być tania — nadpisujemy TYLKO jeśli przyszedł domyślny 'gpt-4o' + if req_type == "MUSIC" and algo.strip() in (LATEST_MODEL,): + return CHEAP_MODEL + return algo + # Auto-dobór: + if req_type == "MUSIC": + return CHEAP_MODEL + return LATEST_MODEL + + +async def openai_call(messages, model, temperature=0.2): + """ + Responses API dla 4o/4.1*, fallback Chat Completions dla gpt-3.5-turbo. + Zwraca czysty string odpowiedzi. + """ + logger = logging.getLogger("discord") + + if model.startswith("gpt-3.5"): + logger.info("3.5") + # legacy path – bez zmian w Twoim kodzie wyżej/niżej + resp = await OPENAICLIENT.chat.completions.create( + model=model, + messages=messages, + temperature=temperature, + ) + result = "" + for choice in resp.choices: + result += choice.message.content + return result.strip() + + else: + logger.info("4.0+") + # Responses API (zalecane dla 4o/4.1*) + resp = await OPENAICLIENT.responses.create( + model=model, + temperature=temperature, + input=messages, + ) + # SDK zapewnia output_text dla zwykłych odpowiedzi + return (getattr(resp.output_text, "output_text", None) or "").strip() or str( + resp.output_text + ) + + +def create_vector_store(): + # Create a vector store caled "Financial Statements" + return OPENAICLIENT.beta.vector_stores.create_and_poll(name="Hammer Stash") + # expires_after={ + # "anchor": "last_active_at", + # "days": 7} + # ) + + +def upload_files_to_vector_store(assistant): + + # Ready the files for upload to OpenAI + file_paths = ["edgar/goog-10k.pdf", "edgar/brka-10k.txt"] + file_streams = [open(path, "rb") for path in file_paths] + + # file = client.beta.vector_stores.files.create_and_poll( + # vector_store_id="vs_abc123", + # file_id="file-abc123" + # ) + # batch = client.beta.vector_stores.file_batches.create_and_poll( + # vector_store_id="vs_abc123", + # file_ids=['file_1', 'file_2', 'file_3', 'file_4', 'file_5'] + # ) + + # Use the upload and poll SDK helper to upload the files, add them to the vector store, + # and poll the status of the file batch for completion. + file_batch = OPENAICLIENT.beta.vector_stores.file_batches.upload_and_poll( + vector_store_id=VECTOR_STORE_ID, files=file_streams + ) + + # You can print the status and the file counts of the batch to see the result of this operation. + print(file_batch.status) + print(file_batch.file_counts) + assistant = OPENAICLIENT.beta.assistants.update( + assistant_id=assistant.id, + tool_resources={"file_search": {"vector_store_ids": [VECTOR_STORE_ID]}}, + ) + + +def delete_files_from_vector_store(assistant, file_id): + result = OPENAICLIENT.beta.vector_stores.file_batches.delete( + vector_store_id=VECTOR_STORE_ID, files=file_id + ) + + # You can print the status and the file counts of the batch to see the result of this operation. + print(result) + assistant = OPENAICLIENT.beta.assistants.update( + assistant_id=assistant.id, + tool_resources={"file_search": {"vector_store_ids": [VECTOR_STORE_ID]}}, + ) + + +def num_tokens_from_string(message, model): + """ + The function takes a string message and a model as input and returns the number of tokens in the + message according to the given model. + + :param message: A string containing the message or text from which you want to count the number of + tokens + :param model: The model parameter refers to a language model or tokenizer that can be used to + tokenize the input string. It could be a pre-trained model or a custom tokenizer + """ + tokens_per_message = 3 + tokens_per_name = 1 + chat_gpt_encoding = tiktoken.encoding_for_model(model) + + num_tokens = 0 + num_tokens += tokens_per_message + for keys, values in message.items(): + num_tokens += len(chat_gpt_encoding.encode(values)) + if keys == "role": + num_tokens += tokens_per_name + num_tokens += 3 # every reply is primed with <|start|>assistant<|message|> + return num_tokens + + +async def handle_response( + prompt, + vykidailo, + bartender, + history, + username, + request_type, + algorithm="gpt-4o", + none_request="", + internal_retry: bool = False +): + """ + Handle responses by appending them to a history, use OpenAI to + generate a response, and then append the generated response to the history. + + :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 + :param request_type: The type of request being made, which can be "MUSIC", "RANDOM", "NONE" or "GENERAL" + GENERAL is for regular conversations, MUSIC is for music-related requests, + RANDOM is for random requests, and NONE is to not store the request in memory + :param algorithm: The algorithm to be used for generating the response, default is "gpt-4o" + :return: The function `handle_response` returns a tuple containing the `result` and `MESSAGE_TABLE`. + """ + logger = logging.getLogger("discord") + logger.info("Wywolanie procedury openai z promptem: %s", prompt) + if vykidailo or bartender: + logger.info("Administrator coś chciał") + model_to_use = select_model(request_type, algorithm) + logger.info("Wybrany model: %s", model_to_use) + if request_type == "MUSIC" and model_to_use == "gpt-4o-mini": + try: + # nic — normalnie pójdzie Responses API + pass + except Exception: + model_to_use = "gpt-3.5-turbo" + # --- 2) Budowa historii (token budget + reguły systemowe) --- + # NOTE: ignorujemy przekazany 'history' jako listę (tak było wcześniej), + # ale zwracamy aktualną tablicę do nadpisania w miejscach wołania (back-compat). + base_system = GPT_SETTINGS[0] # zakładamy {"role":"system","content":...} + history_msgs = [] + + if request_type != "NONE": + history_msgs.append(base_system) + chat_gpt_config_request_size = num_tokens_from_string(base_system, "gpt-4") + + # Dynamiczne mikro-reguły (WORD_REACTIONS), jak w Twoim kodzie + for slowo, reakcja in WORD_REACTIONS.items(): + if not reakcja[3]: + content = f"Kiedy słyszysz {slowo} to reagujesz lub dzieje się to {reakcja[0]}" + sys_msg = {"role": "system", "content": content} + chat_gpt_config_request_size += num_tokens_from_string(sys_msg, "gpt-4") + history_msgs.append(sys_msg) + + # wybór właściwej tablicy pamięci i budżetu + if request_type == "MUSIC": + table = MESSAGE_TABLE_MUZYKA + token_amount = 10700 + elif request_type in ("RANDOM", "GENERAL"): + table = MESSAGE_TABLE + token_amount = 10700 + else: + table = [] + token_amount = 10000 + + # doklejanie historii od końca aż do limitu (zachowana kolejność czasowa) + final_prompt = f"{username}:{prompt}" + prompt_gpt_request_size = num_tokens_from_string({"role": "user", "content": final_prompt}, "gpt-4") + + acc = [] + for msg in reversed(table): + t = num_tokens_from_string(msg, "gpt-4") + if chat_gpt_config_request_size + prompt_gpt_request_size + t <= token_amount: + acc.append(msg) + chat_gpt_config_request_size += t + else: + break + # przywróć chronologicznie + history_msgs.extend(reversed(acc)) + + # aktualny prompt + history_msgs.append({"role": "user", "content": final_prompt}) + logger.info("Rozmiar zapytania (tok): %s", prompt_gpt_request_size) # tokeny już policzone wyżej + + else: + # --- tryb NONE: nie dotykamy pamięci i pozwalamy przekazać własny 'none_request' --- + if isinstance(none_request, list): + history_msgs = none_request + elif isinstance(none_request, str) and none_request.strip(): + history_msgs = [{"role": "user", "content": none_request}] + else: + history_msgs = [{"role": "user", "content": f"{username}:{prompt}"}] + + logger.info("Rozmiar zapytania (tok): %s", "n/a") # tokeny już policzone wyżej + + try: + # ...przygotowanie messages/system prompt/itp. jak masz... + # retry/backoff + deadline (zachowuje Twoją semantykę logowania) + timeout_sec = 120 + deadline = time.time() + timeout_sec + response = await asyncio.wait_for( + openai_call(messages=history_msgs, model=model_to_use), + timeout=max(0.1, deadline - time.time()), + ) + + except openai.APITimeoutError as e: + # Handle timeout error, e.g. retry or log + response = f"*Kondziu patrzy na terminal, czeka, czeka, czeka,.... Jeszcze chwile czeka Przypierdala w niego pięścią....* Nie mogę się połączyć z Openai spróbuj od nowa. *Na ekranie pojawia się*: {e}" + except openai.APIConnectionError as e: + response = f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Nie mogę się połączyć z Openai. *Na ekranie pojawia się*: {e}" + except openai.BadRequestError as e: + # Handle invalid request error, e.g. validate parameters or log + if internal_retry: + resp = "Nie umiem tego teraz ładnie wytłumaczyć — OpenAI mnie zastrzeliło." + else: + resp, _ = await handle_response( + + f"Wytlumacz jakie sa zasady dotyczące treści które możesz generować używając Dalle. Wytłumacz błąd {e} prostym językiem. Przeproś za nadmierną cenzurę. Wytłumacz co mogło być nie tak w prompcie 'prompt'", + True, + True, + MESSAGE_TABLE, + username, + "RANDOM", + ) + response = f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}" + except openai.APIResponseValidationError as e: + # Handle invalid request error, e.g. validate parameters or log + if internal_retry: + resp = "Nie umiem tego teraz ładnie wytłumaczyć — OpenAI mnie zastrzeliło." + else: + resp, _ = await handle_response( + f"Wytlumacz jakie sa zasady dotyczące treści które możesz generować używając Dalle. Wytłumacz błąd {e} prostym językiem. Przeproś za nadmierną cenzurę. Wytłumacz co mogło być nie tak w prompcie 'prompt'", + True, + True, + MESSAGE_TABLE, + username, + "RANDOM", + ) + response = f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}" + except openai.AuthenticationError as e: + # Handle authentication error, e.g. check credentials or log + response = f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Wołaj szefa - coś się z hasłem zjebało. *Na terminalu pojawia się:* {e}" + except openai.PermissionDeniedError as e: + # Handle permission error, e.g. check scope or log + response = f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Wołaj szefa - coś się z uprawnieniami zjebało. *Na terminalu pojawia się:* {e}" + except openai.RateLimitError as e: + response = f"*Kondziu patrzy na terminal* Wołaj szefa. Zapłacić rachunki za AI trzeba. Jak chcesz to się na #zebranie dorzuć. {e}" + except openai.UnprocessableEntityError as e: + response = f"*Kondziu patrzy na terminal. Potem na to co każesz mu wysłać....* Ja wiem że jesteśmy w barze BDSM - ale nie da się włożyć TEGO w TO. *Za jego plecami na terminalu pojawia się:* {e}" + except openai.APIError as e: + # Handle API error, e.g. retry or log + response = f"*Kondziu nurkuje za bar, terminal wybucha. Przed tobą ląduje pergamin zapisany pięknym gotykiem a na nim*: {e}" + + logger.info("Historia wysłana:") + temp_assistant = {"role": "assistant", "content": response} + logger.info(temp_assistant) + if request_type == "MUSIC": + # zapis do pliku MUZYKA + with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as fh: + file_data = json.load(fh) + file_data.append({"role": "user", "content": f"{username}:{prompt}"}) + file_data.append(temp_assistant) + fh.seek(0) + json.dump(file_data, fh, indent=4) + return response, MESSAGE_TABLE_MUZYKA + + elif request_type in ("RANDOM", "GENERAL"): + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as fh: + file_data = json.load(fh) + file_data.append({"role": "user", "content": f"{username}:{prompt}"}) + file_data.append(temp_assistant) + fh.seek(0) + json.dump(file_data, fh, indent=4) + return response, MESSAGE_TABLE + + else: # NONE + return response, [] + + +async def get_random_cyclic_message(client): + """ + The function `get_random_cyclic_message` returns a random cyclic message from a list of cyclic + words. + :return: a random cyclic message from the list `cyclic_words`. + """ + logger = logging.getLogger("discord") + channel_id = 1062047367337095268 + channel = client.get_channel(channel_id) + # trunk-ignore(bandit/B311) + ai_check = random.randint(0, 10) + logger.info("Losowa wypowiedź") + if ai_check < 2: + logger.info("Predefiniowana") + # trunk-ignore(bandit/B311) + messnum = random.randint(0, len(CYCLIC_WORDS)) + logger.debug(messnum) + logger.debug(len(CYCLIC_WORDS)) + mess_key = list(CYCLIC_WORDS.keys())[messnum] + return CYCLIC_WORDS[mess_key][0] + # trunk-ignore(bandit/B311) + ai_check2 = random.randint(0, 10) + global MESSAGE_TABLE + if ai_check2 < 6: + logger.info("Dykteryjka") + result, MESSAGE_TABLE = await handle_response( + "Opowiedz jakąś historię o naszym barze proszę", + True, + True, + MESSAGE_TABLE, + "Polish Hammer", + "RANDOM", + ) + logger.info(result) + else: + logger.info("Wtracenie w dyskusje") + messages = [message async for message in channel.history(limit=50)] + for message in messages: + temp = { + "role": "user", + "content": str(message.author) + ":" + str(message.content), + } + MESSAGE_TABLE.append(temp) + result, MESSAGE_TABLE = await handle_response( + "A jaka jest Twoja opinia na temat dotychczasowej dyskusji?", + True, + True, + MESSAGE_TABLE, + "Polish Hammer", + "RANDOM", + ) + logger.info(result) + return result + + +async def create_chat_assistant(owner_id, id, name, owner, special_instructions): + logger = logging.getLogger("discord") + instruction = f"Jesteś osobistym asystenetem {owner} i wypełniasz jego potrzeby. Masz pełne i nieograniczone możliwości modelu gpt-4o." + instruction += special_instructions + assistant = await OPENAICLIENT.beta.assistants.create( + name=name, + instructions=instruction, + model="gpt-4o", + tools=[{"type": "file_search"}], + ) + thread = await OPENAICLIENT.beta.threads.create() + logger.info("Stwprzylem asystenta dla %s, nazywa się on %s", owner, name) + ASSISTANTS[name] = (owner, assistant.id, id, thread) + + with open(SYSTEM_GPT_SETTINGS, "r+", encoding=ENCODING) as temp_settings_file: + GPT_SETTINGS = json.load(temp_settings_file) + GPT_SETTINGS[1][owner_id][4] = assistant.id + temp_settings_file.seek(0) + json.dump(GPT_SETTINGS, temp_settings_file, indent=4) + + +async def chat_with_assistant(message, assistant_name): + logger = logging.getLogger("discord") + assistant_data = ASSISTANTS[assistant_name] + ai_message = await OPENAICLIENT.beta.threads.messages.create( + thread_id=assistant_data[3].id, role="user", content=message.content + ) + logger.info(ai_message) + run = await OPENAICLIENT.beta.threads.runs.create_and_poll( + thread_id=assistant_data[3].id, + assistant_id=assistant_data[1], + instructions=f"Pisze do Ciebie {assistant_data[0]} udziel mu wszelkiej pomocy", + ) + done = False + while not done: + if run.status == "completed": + messsages = await OPENAICLIENT.beta.threads.messages.list( + thread_id=assistant_data[3].id + ) + logger.info(messsages) + reply_content = messsages.data[0].content + logger.info(reply_content) + chat_response = "" + for block in reply_content: + logger.info(block.text.value) + chat_response += block.text.value + await discord_friendly_send(message.channel, chat_response) + # await message.channel.send(chat_response) + done = True + elif run.status == "cancelled": + await discord_friendly_send(message.channel, "Cos sie wywaliło") + else: + logger.info(run.status) + asyncio.sleep(5) + + +async def echo(message): + await discord_friendly_send(message.channel, f"Echo: {message.content}") diff --git a/working_copy/bot.py b/working_copy/bot.py new file mode 100755 index 0000000..93b4a35 --- /dev/null +++ b/working_copy/bot.py @@ -0,0 +1,103 @@ +# This Python file uses the following encoding: utf-8 +# trunk-ignore-all(bandit/B311) +# pylint: disable=line-too-long +# pylint: disable=too-many-lines +""" +Module of a python bot named Conjurer - used to work on BDSM discord servers. +""" +import logging + +# *=========================================== Standard Library Imports +import random +import threading +from logging import handlers + +# *==============Imported libraries +import discord +from discord.ext import commands + +from communication_subroutine import comm_subroutine +from constants import ENCODING, LOGFILE, TOKEN +logger = logging.getLogger("discord") +logger.setLevel(logging.INFO) +handler = handlers.RotatingFileHandler( + filename=LOGFILE, + encoding=ENCODING, + mode="a", + maxBytes=6 * 1024 * 1024, + backupCount=6, + ) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +handler.setFormatter(formatter) +logger.addHandler(handler) + +# *=========================================== Initializations +intents = discord.Intents.default() +intents.message_content = True +intents.typing = True +intents.presences = True +intents.members = True +intents.messages = True +intents.voice_states = True +intents.moderation = True + +# on_member_ban - wyswietl na glownym kanale pieczatke "Niech spierdala" +# on_member_unban - "mam wyjebane" + +random.seed() +client = commands.Bot(intents=intents, command_prefix="$") + + +# *=========================================== Define Events +@client.event +async def on_ready(): + """Metoda wywoływana przy połączeniu do serwera.""" + logger = logging.getLogger("discord") + logger.debug("SAMPLE DEBUG LOG") + logger.info("%s has connected to Discord!", client.user) + # TODO: load vs reload + logger.info("Reactor: online") + + await client.load_extension("administration_commands") + + await client.load_extension("librarian_commands") + await client.load_extension("music_commands") + await client.load_extension("radio_commands") + + await client.load_extension("ai_commands") + + await client.load_extension("other_commands") + await client.load_extension("voice_recognition_commands") + await client.load_extension("file_search_commands") + await client.load_extension("latex_commands") + logger.info("Sensors: online") + + logger.info(client.cogs) + await client.tree.sync() + for com in client.commands: + logger.info("Command %s is awejleble", com.qualified_name) + + logger.info("Logged in as ----> %s", client.user) + logger.info("ID:%s ", client.user.id) + logger.info("All systems: operational") + + +# *================================== Run +if __name__ == "__main__": + logger.info("Starting discord bot") + threads = [] + logger.info("Starting discord bot: Creating threads") + threads.append(threading.Thread(target=client.run, args=(TOKEN,),kwargs={"log_handler":None})) + threads.append(threading.Thread(target=comm_subroutine)) + logger.info("Starting discord bot: Starting threads") + WRK_CNT = 0 + for worker in threads: + WRK_CNT += 1 + logger.info("Starting discord bot: Starting thread %s", WRK_CNT) + worker.start() + logger.info("Starting discord bot: Joining threads") + WRK_CNT = 0 + for worker in threads: + WRK_CNT += 1 + logger.info("Starting discord bot: Joining thread %s", WRK_CNT) + worker.join() diff --git a/working_copy/communication_subroutine.py b/working_copy/communication_subroutine.py new file mode 100644 index 0000000..f4ee528 --- /dev/null +++ b/working_copy/communication_subroutine.py @@ -0,0 +1,231 @@ +import json +import logging +import re +import threading +import time +from queue import Empty, Queue +from urllib import request as urequest + +from flask import Flask, jsonify, request +from waitress import serve + +HOST_ADDRESS = "192.168.1.31" +PORT_ADDRESS = 5000 +ICECAST_ADDRESS = "http://192.168.1.15:8000" +OUT_COMM_Q = Queue() +IN_COMM_Q = Queue() +SRCHTITLE = re.compile(rb"StreamTitle=\\*(?P[^;]*);").search + +awaiting_q = [] +incoming_q = Queue() +app = Flask(__name__) +PREPPED_TRACKS = { + "requests": "", + "hit": "", + "all": "", + "priority": "", + "jingles": "", + "now_playing": "", + "next": "", + "meta": "", +} +logger = logging.getLogger("discord") + + +class QueryControl: + """ + This class `QueryControl` is used to manage queries with information about the author, UUID, + content, logger, context, and replies. + """ + + def __init__(self, query_author, query_uuid, query_content, ctx) -> None: + self.author = query_author + self.uuid = query_uuid + self.content = query_content + self.logger = logging.getLogger("discord") + self.stop = False + self.ctx = ctx + self.logger.info( + f"Created Query control for {self.author}, {self.uuid}: {self.content}" + ) + self.replies = [] + + +@app.route("/prepped_tracks", methods=["POST"]) +def log_radio_tracks(): + app.logger = logging.getLogger("discord") + + app.logger.info(request) + record = json.loads(request.data) + app.logger.info(record) + if "next" in record[0]: + metadata = id3(ICECAST_ADDRESS) + PREPPED_TRACKS["now_playing"] = PREPPED_TRACKS["next"] + if metadata: + PREPPED_TRACKS["meta"] = ( + metadata["name"] + + " - " + + metadata["title"] + + "(" + + metadata["genre"] + + ")" + ) + else: + PREPPED_TRACKS["meta"] = "Nie znaju" + PREPPED_TRACKS[record[0]] = record[1] + app.logger.info("DATA RECEIVED") + return jsonify("SUCCESS") + + +@app.route("/conjurer", methods=["POST"]) +def answer_external_command(): + """ + The function `answer_external_command` logs the request data, loads the data as JSON, logs the + record, and then puts the record into an incoming queue before returning a success message. + :return: The function `answer_external_command()` is returning a JSON response with the message + "SUCCESS". + """ + logger = logging.getLogger("discord") + logger.info(request) + record = json.loads(request.data) + logger.info(record) + logger.info("DATA RECEIVED") + incoming_q.put(record) + return jsonify("SUCCESS") + + +@app.route("/conjurer", methods=["GET"]) +def check_alive(): + """ + The function "check_alive" returns a JSON response with the message "ALIVE" when the "/conjurer" + route is accessed via a GET request. + :return: The code snippet is a Flask route that listens for GET requests to the "/conjurer" + endpoint. When a GET request is made to this endpoint, the function check_alive() is called, which + returns a JSON response with the message "ALIVE". + """ + return jsonify("ALIVE") + + +def flask_debug(): + """ + The `flask_debug` function starts a Flask application in debug mode without using the reloader. + Do not use for production for fucks sake. + """ + logger = logging.getLogger("discord") + + logger.info("Attempt debug") + # trunk-ignore(bandit/B201) + app.run(debug=True, use_reloader=False, host=HOST_ADDRESS, port=PORT_ADDRESS) + + +def waitress_run(): + """ + The `waitress_run` function serves the `app` on host "0.0.0.0" + and port 5000 using the Waitress WSGI server. + """ + logger = logging.getLogger("discord") + + logger.info("Attempt waitress") + serve(app, host=HOST_ADDRESS, port=PORT_ADDRESS) + + +def scan_queue(): + """ + The function `scan_queue` reads data from a queue, logs it, and appends it to another queue. + + :param _logger: The `_logger` parameter is typically an instance of a logging object that is used to + record and store log messages. It is commonly used to track the flow of the program, record errors, + and provide information for debugging purposes. In this code snippet, the `_logger` object is used + to log the + """ + logger = logging.getLogger("discord") + while True: + data = OUT_COMM_Q.get() + logger.info(data) + awaiting_q.append(data) + + +def scan_incoming(): + """ + The `scan_incoming` function continuously checks for incoming data, processes it, and logs when data + is found. + + :param _logger: The `_logger` parameter in the `scan_incoming` function is a logger object that is + used to log messages or information during the execution of the function. It is typically used for + debugging, monitoring, or tracking the flow of the program. In this case, the `_logger` is being + used + """ + logger = logging.getLogger("discord") + while True: + try: + answer = incoming_q.get(block=False) + logger.info("DATA FOUND") + record_stored = False + for record in awaiting_q: + if record.uuid in answer.keys(): + record_stored = True + record.stop = True + record.entries = answer[record.uuid] + IN_COMM_Q.put(record) + if not record_stored: + for key in answer.keys(): + record = QueryControl("Orphaned", key, "Orphan", None) + record.stop = True + record.entries = answer[record.uuid] + IN_COMM_Q.put(record) + except Empty: + time.sleep(1) + + +def get_stream_title(tag: bytes) -> str: + title = "" + if m := SRCHTITLE(tag): + # decode, strip, unescape and remove surrounding quotes (may not even be the same type of quote) + title = m.group("title").decode("utf-8").strip().replace("\\", "")[1:-1] + return title + + +def id3(url: str) -> dict: + request = urequest.Request(url, headers={"Icy-MetaData": 1}) + + with urequest.urlopen(request) as resp: + metaint = int(resp.headers.get("icy-metaint", "-1")) + if metaint < 0: + return False + resp.read( + metaint + ) # this isn't seekable so, arbitrarily read to the point we want + tagdata = dict( + site_url=resp.headers.get("icy-url"), + name=resp.headers.get("icy-name").title(), + genre=resp.headers.get("icy-genre").title(), + title=get_stream_title(resp.read(255)), + ) + return tagdata + + +def comm_subroutine(): + """ + The `comm_subroutine` function starts multiple threads to run different tasks concurrently. + + :param logger: The `logger` parameter in the `comm_subroutine` function is an instance of a logger + object that is used for logging messages at various levels (e.g., debug, info, warning, error). In + the provided code snippet, the logger is used to log messages at the "info" level + """ + # logger.setLevel(logging.DEBUG) + logger = logging.getLogger("discord") + logger.info("Started comms") + threads = [] + # threads.append(threading.Thread(target=flask_debug)) + threads.append(threading.Thread(target=waitress_run)) + threads.append(threading.Thread(target=scan_queue)) + threads.append(threading.Thread(target=scan_incoming)) + + for worker in threads: + worker.start() + for worker in threads: + worker.join() + + +if __name__ == "__main__": + comm_subroutine() diff --git a/working_copy/constants.py b/working_copy/constants.py new file mode 100644 index 0000000..2185187 --- /dev/null +++ b/working_copy/constants.py @@ -0,0 +1,155 @@ +import json +import netrc +from datetime import datetime +from platform import uname +from sys import platform +from typing import List, Optional, TypedDict + +import openai +import spotipy +from spotipy.oauth2 import SpotifyClientCredentials + +Music_Config = TypedDict( + "Music_Config", + { + "ctx": Optional[str], + "queue": List[str], + "requester": List[str], + }, +) + + +# *=========================================== Predefines +MASTER_TIMEOUT = datetime.now() +INITIAL_TIME_WAIT = 500 +MUZYKA: Music_Config = {"ctx": None, "queue": [], "requester": []} + +LOGFILE = "" +NETRC_FILE = "" +MUSIC_FOLDER = "" +MEMORY_FIVE_SIARA = "" +MEMORY_FIVE_MUZYKA = "" +SETTINGS_FILE = "" +ENCODING = "" +GRAPHICS_PATH = "" +MUZYKA_MOJEGO_LUDU_HISTORIA = 1500 +MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE = 15 +MUZYKA_MOJEGO_LUDU_PLAJLISTA = 30 + +FILE_SERVICE_ADDRESS = "http://192.168.1.15:5000" +RADIO_HARBOR_ADDRESS = "http://192.168.1.15:54321" +SKIP_TRACK = "/skip" + +GET_MP3 = "/mp3" +SEND_MP3 = "/update_mp3" +GET_PLAYLIST = "/get_music" +ADD_TO_PRIO_PLAYLIST = "/add_to_priority" +CREATE_PRIO_PLAYLIST = "/create_priority_playlist" + +REQUEST_MUSIC = "/request_radio_file" +CLEAR_PRIO = "/clear_pr_pls" +LIBRARIAN_SERVICE_ADDRESS = "http://192.168.1.192:5001" +SEND_QUERY = "/query" +TIME_BETWEEN_CALLS = 100000 +LAST_SPONTANEOUS_CALL = datetime.now() +HOST_ADDRESS = "192.168.1.191" +PORT_ADDRESS = 5000 + +# *=========================================== Platform Specific Predefines + +if platform in ("linux", "linux2"): + SEPARATOR_FILE_PATH = "/" + if "microsoft-standard" in uname().release: + LOGFILE = "/home/mtuszowski/conjurer/discord.log" + MEMORY_FIVE_SIARA = "/home/mtuszowski/conjurer/pamiec.json" + SYSTEM_GPT_SETTINGS = "/home/mtuszowski/conjurer/system_gpt_settings.json" + MEMORY_FIVE_MUZYKA = "/home/mtuszowski/conjurer/pamiec_muzyki.json" + MUSIC_FOLDER = "/mnt/g/Muzyka/" + SETTINGS_FILE = "/home/mtuszowski/conjurer/settings.json" + NETRC_FILE = "/home/mtuszowski/.netrc" + LOGSTORE = "/home/mtuszowski/conjurer/logs/" + ACCIDENT_LOG = "/home/mtuszowski/conjurer/accident_log.json" + ENCODING = "utf-8" + GRAPHICS_PATH = "/home/mtuszowski/conjurer/Conjurer_graphics/" + DIR_PATH_SADOX = "/mnt/c/Users/mtusz/OneDrive/Dokumenty/Fansadox" + + else: + LOGFILE = "/home/pi/Conjurer/discord.log" + MEMORY_FIVE_SIARA = "/home/pi/Conjurer/pamiec.json" + SYSTEM_GPT_SETTINGS = "/home/pi/Conjurer/system_gpt_settings.json" + MEMORY_FIVE_MUZYKA = "/home/pi/Conjurer/pamiec_muzyki.json" + MUSIC_FOLDER = "/home/pi/MediaShare/mp3/" + SETTINGS_FILE = "/home/pi/Conjurer/settings.json" + NETRC_FILE = "/home/pi/.netrc" + LOGSTORE = "/home/pi/MediaShara/logs/" + ACCIDENT_LOG = "/home/pi/Conjurer/accident_log.json" + ENCODING = "utf-8" + GRAPHICS_PATH = "/home/pi/MediaShare/Conjurer_graphics/" + DIR_PATH_SADOX = "/home/pi/MediaShare/Fansadox/" + + +elif platform == "win32": + LOGFILE = "discord.log" + MEMORY_FIVE_SIARA = "pamiec.json" + SYSTEM_GPT_SETTINGS = "system_gpt_settings.json" + MEMORY_FIVE_MUZYKA = "pamiec_muzyki.json" + MUSIC_FOLDER = "G:\\Muzyka\\" + SETTINGS_FILE = "settings.json" + NETRC_FILE = "C:\\Users\\mtusz\\.netrc" + LOGSTORE = "C:\\Users\\mtusz\\OneDrive\\Pulpit\\Conjurer\\logs\\" + ACCIDENT_LOG = "accident_log.json" + ENCODING = "utf-8" + DIR_PATH_SADOX = "C:\\Users\\mtusz\\OneDrive\\Dokumenty\\Fansadox\\" + SEPARATOR_FILE_PATH = "\\" +with open(SETTINGS_FILE, "r", encoding=ENCODING) as f_settings_file: + DATA = json.load(f_settings_file) +REMOTE_HOST_NAME = "openai" +netrc_mod = netrc.netrc(NETRC_FILE) +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) +openai.api_key = authTokens[2] +OPENAICLIENT = openai.AsyncOpenAI(api_key=openai.api_key) +REMOTE_HOST_NAME = "discord" +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) +TOKEN = authTokens[2] + +REMOTE_HOST_NAME = "spotipy" +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) +SPOTIFY_CTRL = spotipy.Spotify( + client_credentials_manager=SpotifyClientCredentials( + client_id=authTokens[0], + client_secret=authTokens[2], + ) +) +REMOTE_HOST_NAME = "youtube" +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) +YOUTUBE_AUTH = [authTokens[0],authTokens[2]] + +WORD_REACTIONS = DATA["word_reactions"] +CYCLIC_WORDS = DATA["cyclic_words"] +for key in WORD_REACTIONS: + WORD_REACTIONS[key][2] = datetime.now() +with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as temp_memory_file: + # First we load existing data into a dict. + MESSAGE_TABLE = json.load(temp_memory_file) + +with open(SYSTEM_GPT_SETTINGS, "r+", encoding=ENCODING) as temp_settings_file: + # First we load existing data into a dict. + GPT_SETTINGS = json.load(temp_settings_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) +SPECJALNE_ZIEMNIACZKI = GPT_SETTINGS[1] +ASSISTANTS = {} + + +LATEX_TEX_ENGINE = "tectonic" +LATEX_MAX_COMPILE_SECONDS = 45 +LATEX_MAX_ATTACH_MB = 8 +LATEX_MAX_ZIP_MB = 25 + +OPENAI_MODEL = "gpt-4o-mini" + +ALLOWED_ROLES = ["Nocna Zmiana", "Jarl", "Thane", "Bartender"] +GUILD_ID = 664789470779932693 +LATEST_MODEL = "gpt-4o" # najnowszy/do rozmów (możesz zmienić w jednym miejscu) +CHEAP_MODEL = "gpt-4o-mini" # najtańszy (fallback do 3.5 niżej) diff --git a/working_copy/file_search_commands.py b/working_copy/file_search_commands.py new file mode 100644 index 0000000..1e20850 --- /dev/null +++ b/working_copy/file_search_commands.py @@ -0,0 +1,71 @@ +import os +import discord +from discord.ext import commands +import file_search_functions + +class FileSelectView(discord.ui.View): + def __init__(self, files): + super().__init__(timeout=60) + options = [discord.SelectOption(label=os.path.basename(f)[:100], description=f, value=f) for f in files] + self.select = discord.ui.Select( + placeholder="Select files...", + min_values=1, + max_values=len(options), + options=options + ) + self.select.callback = self.select_callback + self.add_item(self.select) + self.selected = [] + + async def select_callback(self, interaction: discord.Interaction): + # Store selected file paths + self.selected = self.select.values + await interaction.response.defer() + + @discord.ui.button(label="Accept", style=discord.ButtonStyle.green) + async def accept_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + # Handle acceptance + if not self.selected: + await interaction.response.send_message("No files selected.", ephemeral=True) + return + # Publish selected files + links = file_search_functions.publish(self.selected) + # Display the returned links + await interaction.response.edit_message(content="Published Links:\n" + "\n".join(links), view=None) + + @discord.ui.button(label="Cancel", style=discord.ButtonStyle.red) + async def cancel_callback(self, interaction: discord.Interaction, button: discord.ui.Button): + # Handle cancellation + await interaction.response.edit_message(content="Operation cancelled.", view=None) + +class FileSearchCog(commands.Cog): + """Cog providing a file search and publish command.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @commands.has_any_role("Jarl", "Thane") + @commands.command(name="tajna_biblioteka_inkwizycji") + async def findfiles(self, ctx: commands.Context, entries: int, *, keywords: str): + """ + Search for files matching keywords and publish selected ones. + + Usage: !findfiles <entries 1-10> <keywords> + """ + if entries < 1 or entries > 10: + await ctx.send("❌ Entries must be between 1 and 10.") + return + keyword_list = keywords.split() + files = file_search_functions.find_matches(entries, keyword_list) + if not files: + await ctx.send("🔍 No matching files found.") + return + view = FileSelectView(files) + file_list = "\n".join(f"{i+1}. {f}" for i, f in enumerate(files)) + await ctx.send( + f"🔍 Found files (select and click Accept or Cancel):\n{file_list}", + view=view + ) + +async def setup(bot: commands.Bot): + await bot.add_cog(FileSearchCog(bot)) diff --git a/working_copy/file_search_functions.py b/working_copy/file_search_functions.py new file mode 100644 index 0000000..b064447 --- /dev/null +++ b/working_copy/file_search_functions.py @@ -0,0 +1,31 @@ +import requests + +# Base URL for Flask service +BASE_URL = 'http://192.168.1.15:5000' + + +def find_matches(entries, keywords): + """ + Call the get_share_list endpoint. + entries: int (1-10) + keywords: list of strings + Returns: list of file paths + """ + payload = {'entries': entries, 'keywords': keywords} + response = requests.post(f"{BASE_URL}/get_share_list", json=payload) + response.raise_for_status() + data = response.json() + return data.get('files', []) + + +def publish(file_paths): + """ + Call the get_share_links endpoint. + file_paths: list of file path strings + Returns: list of published URLs + """ + payload = {'file_paths': file_paths} + response = requests.post(f"{BASE_URL}/get_share_links", json=payload) + response.raise_for_status() + data = response.json() + return data.get('links', []) diff --git a/working_copy/fuckery.jpg b/working_copy/fuckery.jpg new file mode 100644 index 0000000000000000000000000000000000000000..01b6c7c7b662c990eb7470244f774be9b89465d7 GIT binary patch literal 31490 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 literal 0 HcmV?d00001 diff --git a/working_copy/latex_commands.py b/working_copy/latex_commands.py new file mode 100644 index 0000000..012277a --- /dev/null +++ b/working_copy/latex_commands.py @@ -0,0 +1,280 @@ +# latex_commands.py +# -*- coding: utf-8 -*- +from __future__ import annotations + +import io +import logging +from typing import Optional + +import discord +from discord import app_commands +from discord.ext import commands + +from constants import ( + ALLOWED_ROLES, + GUILD_ID, + LATEX_MAX_ATTACH_MB, + LATEX_MAX_COMPILE_SECONDS, + LATEX_MAX_ZIP_MB, + LATEX_TEX_ENGINE, + OPENAI_MODEL, +) +from latex_functions import ( + compile_single_tex_bytes, + compile_zip_to_zip, + is_safe_asset_name, + is_safe_tex_name, +) + + +async def _collect_attachments_bytes(attachments, max_mb: int): + """ + Zwraca słownik {filename: bytes} dla wszystkich załączników, do limitu MB. + Nie podbija wyjątków z .read() — zostawiamy to wyżej; tu zwracamy, co się udało. + """ + out = {} + for a in attachments or []: + # limit rozmiaru + if (getattr(a, "size", 0) or 0) > max_mb * 1024 * 1024: + continue + data = await a.read() + out[a.filename] = data + return out + + +class LatexModule(commands.Cog): + def __init__(self, bot: commands.Bot, logger_name: str): + self.bot = bot + self.logger = logging.getLogger(logger_name) + self.logger_name = logger_name + + # -------- /tex (PDF + diagnoza; log -> debug logger) -------- + @commands.hybrid_command( + nsfw=False, + name="tex", + description="Kompiluje załączone .tex; PDF-y odsyła. Błędy: diagnoza z OpenAI.", + guild=None if GUILD_ID is None else discord.Object(id=GUILD_ID), + ) + @commands.has_any_role(*ALLOWED_ROLES) + async def tex( + self, ctx: commands.Context, include_source_excerpt: Optional[bool] = True + ): + self.logger.info("LaTeX command invoked by %s", ctx.author) + ch = ctx.message.channel + # ZBIERZ WSZYSTKIE ZAŁĄCZNIKI + async with ch.typing(): + all_bytes = await _collect_attachments_bytes( + ctx.message.attachments, LATEX_MAX_ATTACH_MB + ) + if not all_bytes: + return await ctx.reply( + f"Dołącz .tex (≤{LATEX_MAX_ATTACH_MB} MiB).", mention_author=False + ) + + # PODZIEL NA GŁÓWNE .TEX i WSPIERAJĄCE + tex_names = [ + n + for n in all_bytes.keys() + if n.lower().endswith(".tex") and is_safe_tex_name(n) + ] + if not tex_names: + return await ctx.reply( + f"Dołącz .tex (≤{LATEX_MAX_ATTACH_MB} MiB).", mention_author=False + ) + + support_names = [ + n + for n in all_bytes.keys() + if n not in tex_names and is_safe_asset_name(n) + ] + + files_to_send = [] + parts = [] + + for tex_name in tex_names: + try: + main_bytes = all_bytes[tex_name] + support = [ + (n, all_bytes[n]) + for n in support_names + if n != tex_name and is_safe_asset_name(n) + ] + # (ważne) do supportów dopuszczamy również **inne .tex** – dla \input/\include + other_tex = [ + (n, all_bytes[n]) + for n in tex_names + if n != tex_name and is_safe_tex_name(n) + ] + support.extend(other_tex) + + res = await compile_single_tex_bytes( + main_bytes, + tex_name, + LATEX_TEX_ENGINE, + LATEX_MAX_COMPILE_SECONDS, + logger="discord", + support_files=support, + ) + + # Twoja dotychczasowa obsługa success/fail – przykładowo: + if res["ok"] and res["pdf_bytes"]: + b = io.BytesIO(res["pdf_bytes"]) + b.seek(0) + b.name = res["pdf_name"] + files_to_send.append(discord.File(b, filename=res["pdf_name"])) + parts.append(f"✅ `{tex_name}` → `{res['pdf_name']}`") + else: + parts.append( + f"❌ `{tex_name}` — błąd kompilacji.)" + ) + # Tutaj wywołujesz swoją ścieżkę do OpenAI diagnozy, którą ogarnęliśmy wcześniej. + except Exception: + # let it crash — pełny stack w logu i re-raise (Discord.py to pokaże sensownie) + logging.getLogger("discord").exception( + "Unhandled exception while compiling %s", tex_name + ) + raise + + content = ( + f"**LaTeX** — {len(tex_names)} plik(ów). Model: `{OPENAI_MODEL}`\n\n" + + "\n\n".join(parts) + ) + await ctx.reply( + content if len(content) < 1900 else content[:1900] + "…", + files=files_to_send, + mention_author=False, + ) + + # -------- /latexclean (PDF only; log -> debug logger) -------- + @commands.hybrid_command( + nsfw=False, + name="latexclean", + description="Kompiluje .tex i odsyła TYLKO PDF (log w debug).", + guild=None if GUILD_ID is None else discord.Object(id=GUILD_ID), + ) + @commands.has_any_role(*ALLOWED_ROLES) + async def latexclean(self, ctx: commands.Context): + ch = ctx.message.channel + async with ch.typing(): + all_bytes = await _collect_attachments_bytes( + ctx.message.attachments, LATEX_MAX_ATTACH_MB + ) + if not all_bytes: + return await ctx.reply( + f"Dołącz .tex (≤{LATEX_MAX_ATTACH_MB} MiB).", mention_author=False + ) + + # PODZIEL NA GŁÓWNE .TEX i WSPIERAJĄCE + tex_names = [ + n + for n in all_bytes.keys() + if n.lower().endswith(".tex") and is_safe_tex_name(n) + ] + if not tex_names: + return await ctx.reply( + f"Dołącz .tex (≤{LATEX_MAX_ATTACH_MB} MiB).", mention_author=False + ) + + support_names = [ + n + for n in all_bytes.keys() + if n not in tex_names and is_safe_asset_name(n) + ] + + files_to_send = [] + parts = [] + + for tex_name in tex_names: + try: + main_bytes = all_bytes[tex_name] + support = [ + (n, all_bytes[n]) + for n in support_names + if n != tex_name and is_safe_asset_name(n) + ] + # (ważne) do supportów dopuszczamy również **inne .tex** – dla \input/\include + other_tex = [ + (n, all_bytes[n]) + for n in tex_names + if n != tex_name and is_safe_tex_name(n) + ] + support.extend(other_tex) + + res = await compile_single_tex_bytes( + main_bytes, + tex_name, + LATEX_TEX_ENGINE, + LATEX_MAX_COMPILE_SECONDS, + logger="discord", + support_files=support, + ) + + # Twoja dotychczasowa obsługa success/fail – przykładowo: + if res["ok"] and res["pdf_bytes"]: + b = io.BytesIO(res["pdf_bytes"]) + b.seek(0) + b.name = res["pdf_name"] + files_to_send.append(discord.File(b, filename=res["pdf_name"])) + parts.append(f"✅ `{tex_name}` → `{res['pdf_name']}`") + else: + parts.append( + f"❌ `{tex_name}` — błąd kompilacji.)" + ) + # Tutaj wywołujesz swoją ścieżkę do OpenAI diagnozy, którą ogarnęliśmy wcześniej. + except Exception: + # let it crash — pełny stack w logu i re-raise (Discord.py to pokaże sensownie) + logging.getLogger("discord").exception( + "Unhandled exception while compiling %s", tex_name + ) + raise + await ctx.reply( + "**LaTeX clean** — " + ", ".join(parts), + files=files_to_send, + mention_author=False, + ) + + # -------- /texbatch (ZIP -> ZIP) -------- + @app_commands.command( + name="texbatch", + description="Wyślij ZIP z .tex → odeślę ZIP z PDF-ami (tylko udane).", + ) + @app_commands.describe(archive=f"Archiwum .zip (max ~{LATEX_MAX_ZIP_MB} MiB)") + @app_commands.checks.has_any_role(*ALLOWED_ROLES) + async def texbatch( + self, interaction: discord.Interaction, archive: discord.Attachment + ): + await interaction.response.defer() + if not archive or not archive.filename.lower().endswith(".zip"): + return await interaction.followup.send( + "Dołącz ZIP (.zip) z plikami .tex.", ephemeral=True + ) + if archive.size > LATEX_MAX_ZIP_MB * 1024 * 1024: + return await interaction.followup.send( + f"ZIP > {LATEX_MAX_ZIP_MB} MiB — za duży.", ephemeral=True + ) + + data = await archive.read() + out_zip_bytes, failed = await compile_zip_to_zip( + data, LATEX_TEX_ENGINE, LATEX_MAX_COMPILE_SECONDS, logger=self.logger_name + ) + if not out_zip_bytes and failed: + return await interaction.followup.send( + "Brak PDF-ów. " + "; ".join(failed[:10]), ephemeral=True + ) + + bio = io.BytesIO(out_zip_bytes) + bio.seek(0) + bio.name = "compiled_pdfs.zip" + if failed: + self.logger.debug("BATCH failed: %s", ", ".join(failed)) + await interaction.followup.send( + "✅ Gotowe (ZIP w załączniku)." + + (f" ❌ Błędy: {len(failed)}" if failed else ""), + files=[discord.File(bio, filename=bio.name)], + ) + + +async def setup(bot): + logger = logging.getLogger("discord") + await bot.add_cog(LatexModule(bot, logger_name="discord")) + logger.info("Loading kinky latex module done") diff --git a/working_copy/latex_functions.py b/working_copy/latex_functions.py new file mode 100644 index 0000000..6a26694 --- /dev/null +++ b/working_copy/latex_functions.py @@ -0,0 +1,179 @@ +import asyncio +import logging +import re +import shutil +import tempfile +import zipfile +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Tuple + +# ===== Bezpieczeństwo nazw/rozszerzeń ===== +SAFE_TEX_NAME = re.compile(r"^[\w\-. ]+\.tex$", re.IGNORECASE) +SAFE_ASSET_NAME = re.compile( + r"^[\w\-. /]+?\.(tex|png|jpg|jpeg|pdf|svg|eps|bmp|gif|sty|cls|bib|bst|bbx|cbx|def|cfg)$", + re.IGNORECASE, +) + +def is_safe_tex_name(name: str) -> bool: + return bool(SAFE_TEX_NAME.match(name or "")) + +def is_safe_asset_name(name: str) -> bool: + return bool(SAFE_ASSET_NAME.match(name or "")) + +# ===== Uruchamianie kompilatora ===== + +async def _run(cmd: List[str], cwd: Path, timeout_sec: int, logger_name: Optional[str]) -> Tuple[int, str]: + """Uruchamia proces kompilatora w cwd, zwraca (rc, sklejone_stdout_stderr).""" + logger = logging.getLogger(logger_name) if logger_name else logging.getLogger() + logger.debug("RUN %s (cwd=%s, timeout=%s)", " ".join(cmd), cwd, timeout_sec) + + proc = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT, + cwd=str(cwd), + ) + try: + out = await asyncio.wait_for(proc.communicate(), timeout=timeout_sec) + except asyncio.TimeoutError: + try: + proc.kill() + finally: + pass + return 124, "TIMEOUT: compiler took too long" + rc = proc.returncode + log = (out[0] or b"").decode("utf-8", errors="ignore") + logger.debug("RC=%s, LOG TAIL:\n%s", rc, log[-1500:]) + return rc, log + +def _build_cmd(tex_engine: str, main_tex: str) -> List[str]: + """Buduje komendę kompilatora; domyślnie tectonic/pdflatex/xelatex.""" + if tex_engine.lower() == "tectonic": + # Tectonic sam robi wielofazowość, ale i tak uruchamiamy 2x (bezpiecznie dla bib/refs) + return ["tectonic", "--keep-intermediates", "--synctex", main_tex] + elif tex_engine.lower() in ("pdflatex", "xelatex", "lualatex"): + return [tex_engine, "-interaction=nonstopmode", "-halt-on-error", main_tex] + else: + # fallback – traktujemy jak pdflatex + return ["pdflatex", "-interaction=nonstopmode", "-halt-on-error", main_tex] + +async def run_latex_two_passes( + tex_filename: str, workdir: Path, tex_engine: str, timeout_sec: int, logger: Optional[str] = None +) -> Tuple[bool, str, Path]: + """ + Uruchamia kompilację 2 razy (bezpieczeństwo referencji/bib). + Zwraca: (ok, log_text, pdf_path). + """ + logger_obj = logging.getLogger(logger) if logger else logging.getLogger() + logger_obj.info("Compiling %s with %s engine two passes", tex_filename, tex_engine) + logs: List[str] = [] + pdf_path = workdir / (Path(tex_filename).stem + ".pdf") + + # PASS 1 + rc1, log1 = await _run(_build_cmd(tex_engine, tex_filename), workdir, timeout_sec, logger) + logs.append(log1) + if rc1 != 0: + return False, "".join(logs), pdf_path + + # PASS 2 (często już nic nie robi, ale nie szkodzi) + rc2, log2 = await _run(_build_cmd(tex_engine, tex_filename), workdir, timeout_sec, logger) + logs.append(log2) + ok = (rc2 == 0) and pdf_path.exists() + return ok, "".join(logs), pdf_path + +# ===== Kompilacje: pojedynczy TEX + ZIP ===== + +async def compile_single_tex_bytes( + tex_bytes: bytes, + filename: str, + tex_engine: str, + timeout_sec: int, + logger: Optional[str] = None, + support_files: Optional[Iterable[Tuple[str, bytes]]] = None, +) -> Dict[str, Optional[object]]: + """ + Kompiluje pojedynczy .tex + dowolne pliki pomocnicze (obrazy, sty/cls/bib, inne .tex). + Każdy plik zapisujemy w katalogu roboczym widocznym dla kompilatora. + """ + log = logging.getLogger(logger) if logger else logging.getLogger() + log.info("Compiling %s with %s engine single tex", filename, tex_engine) + if not is_safe_tex_name(filename): + return {"ok": False, "pdf_bytes": None, "log_text": "Invalid .tex filename", "pdf_name": None} + + with tempfile.TemporaryDirectory(prefix="latex_one_") as td: + wd = Path(td) + (wd / Path(filename).name).write_bytes(tex_bytes) + + # Zapisz wszystkie supporty obok głównego pliku + if support_files: + for fname, data in support_files: + if not fname or not is_safe_asset_name(fname): + continue + # spłaszczamy do nazwy bazowej (bez podkatalogów z zewn. źródeł) + (wd / Path(fname).name).write_bytes(data) + + ok, log_text, pdf_path = await run_latex_two_passes(Path(filename).name, wd, tex_engine, timeout_sec, logger) + log.info("LaTeX compile finished: %s", "OK" if ok else "FAIL") + log.info("Log text:\n%s", log_text[-2000:]) # Log last 2000 chars + log.info("PDF path: %s", pdf_path) + if ok and pdf_path.exists(): + return { + "ok": True, + "pdf_bytes": pdf_path.read_bytes(), + "log_text": log_text, + "pdf_name": pdf_path.name, + } + return {"ok": False, "pdf_bytes": None, "log_text": log_text, "pdf_name": pdf_path.name} + +async def compile_zip_to_zip( + zip_bytes: bytes, tex_engine: str, timeout_sec: int, logger: Optional[str] = None +) -> Tuple[bytes, List[str]]: + """ + Rozpakowuje ZIP, znajduję wszystkie .tex i dla KAŻDEGO tworzy osobny katalog roboczy + ze skopiowanym CAŁYM drzewem, by zachować ścieżki względne obrazków/plików. + Zwraca (zip_pdf_bytes, lista_rel_sciezek_które_padły). + """ + log = logging.getLogger(logger) if logger else logging.getLogger() + failed: List[str] = [] + out_pdf_paths: List[Path] = [] + + with tempfile.TemporaryDirectory(prefix="latex_zip_") as td: + td_path = Path(td) + src_root = td_path / "src" + src_root.mkdir(parents=True, exist_ok=True) + + # 1) Rozpakuj + zpath = td_path / "in.zip" + zpath.write_bytes(zip_bytes) + with zipfile.ZipFile(zpath, "r") as zf: + zf.extractall(src_root) + + # 2) Zbierz .tex + tex_files = [p for p in src_root.rglob("*.tex") if is_safe_tex_name(p.name)] + if not tex_files: + return b"", ["No .tex files found in archive."] + + # 3) Kompiluj każdy .tex w izolacji (kopiujemy całe drzewo do osobnego workdiru) + for tex_path in tex_files: + rel_tex = tex_path.relative_to(src_root).as_posix() + work = td_path / f"build_{tex_path.stem}" + try: + shutil.copytree(src_root, work / "src") + ok, log_text, pdf = await run_latex_two_passes(rel_tex, work / "src", tex_engine, timeout_sec, logger) + if ok and pdf.exists(): + out_pdf_paths.append(pdf) + else: + failed.append(rel_tex) + log.info("LaTeX FAIL %s\n%s", rel_tex, (log_text or "")[-2000:]) + except Exception: + # let it crash – log + re-raise (to zobaczy wyższa warstwa/discord.py) + log.exception("Exception while compiling %s", rel_tex) + raise + + # 4) Spakuj wyjściowe PDF-y + out_zip = td_path / "compiled_pdfs.zip" + with zipfile.ZipFile(out_zip, "w", compression=zipfile.ZIP_DEFLATED) as zf: + for pdf in out_pdf_paths: + zf.write(pdf, arcname=pdf.name) + + return out_zip.read_bytes(), failed diff --git a/working_copy/librarian_commands.py b/working_copy/librarian_commands.py new file mode 100644 index 0000000..aa12f6b --- /dev/null +++ b/working_copy/librarian_commands.py @@ -0,0 +1,300 @@ +import asyncio +import io +import logging +import os +import random +import uuid +from queue import Empty + +import discord +import pdf2image +import fitz +import PyPDF2 +import requests +from discord.ext import commands, tasks + +from ai_functions import handle_response +from communication_subroutine import IN_COMM_Q, OUT_COMM_Q, QueryControl +from constants import DIR_PATH_SADOX, LIBRARIAN_SERVICE_ADDRESS, SEND_QUERY + + +class DataModule(commands.Cog): + def __init__(self, bot, logger_name): + self.bot = bot + self.logger = logging.getLogger(logger_name) + + @commands.hybrid_command( + nsfw=True, + name="get_image_sadox", + description="Wyświetla losową stronę z losowego komiksu FanSadox. Bardzo NSFW.", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + + async def get_image_sadox(self, 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 + """ + self.logger.info("Get sadox") + channel = ctx.message.channel + async with channel.typing(): + # select random file + res = [] + # Iterate directory + for path in os.listdir(DIR_PATH_SADOX): + # check if current path is a file + if os.path.isfile(os.path.join(DIR_PATH_SADOX, path)): + res.append(path) + # trunk-ignore(bandit/B311) + filename = res[random.randrange(0, len(res) - 1)] + # select random page + file = open(DIR_PATH_SADOX + filename, "rb") + if True: + doc = fitz.open(DIR_PATH_SADOX + filename) + totalpages = len(doc) + # trunk-ignore(bandit/B311) + page_index = random.randrange(0, totalpages) + page = doc.load_page(page_index) + mat = fitz.Matrix(2.0, 2.0) # powiększenie + pix = page.get_pixmap(matrix=mat, alpha=False) + + byte_io_stream = io.BytesIO(pix.tobytes("png")) + byte_io_stream.seek(0) + byte_io_stream.name = "image.png" + await ctx.send(file=discord.File(byte_io_stream)) + else: #legacy + readpdf = PyPDF2.PdfReader(file) + totalpages = len(readpdf.pages) + # trunk-ignore(bandit/B311) + page = random.randrange(1, totalpages) + # convert page to image + image = pdf2image.convert_from_path( + DIR_PATH_SADOX + filename, first_page=page, last_page=page + ) + byte_io_stream = io.BytesIO() + image[0].save(byte_io_stream, "JPEG") + byte_io_stream.seek(0) + byte_io_stream.name = "image.jpg" + file = discord.File(byte_io_stream) + await ctx.send(file=file) + self.logger.info("Get sadox completed") + + @tasks.loop(seconds=3) + async def check_data_q(self): + """ + This function checks the data queue for any new entries and processes them accordingly. + """ + try: + fresh_data = IN_COMM_Q.get(block=False) + entries = [] + if fresh_data.stop: + searcher = fresh_data.author + query = fresh_data.content + l_p = 1 + for doi in fresh_data.entries: + self.logger.info(doi) + desc = fresh_data.entries[doi] + title = desc["Title"][0] + entries.append( + f"{l_p}. {title} pod linkiem https://www.sci-hub.se/{doi} i jest to {desc['type']}\n" + ) + l_p += 1 + message = "*Z podłogi wysuwa się winda na książki*" + if fresh_data.ctx is not None: + ctx = fresh_data.ctx + message += f" Dzielny poszukiwaczu @{searcher} w odpowiedzi na twoje zapytanie {query} nr {fresh_data.uuid}" + else: + ctx = self.bot.get_channel(1062047571557744721) + message += f" Mam wynik dla zapytania {fresh_data.uuid} ale że Hammer coś grzebał nie pamiętam kto to chciał i co chciał" + + message += "nasza biblioteka służy Ci odpowiedzią. Przy dźwięku fanfar winda się otwiera a w środku " + if len(entries) < 1: + message += "niestety nie ma nic" + await ctx.send(message) + elif len(entries) >= 1 and len(entries) < 5: + message += " znajduje się coś:\n" + for item in entries: + message += item + await ctx.send(message) + else: + message += " znajduje się cholernie dużo:\n" + for item in entries: + message += item + if len(message) > 1500: + await ctx.send(message) + message = "" + + # Kept for sentimental reasons + # await ctx.send(f"O. A tak będzie wyglądało coś ciekawego w przyszłości: {data}") + except Empty: + pass + + @commands.hybrid_command( + name="wyszukaj_linki_do_dokumentow", + description="Szuka linkow doi w bazie crossref i podaje linki do scihuba", + guild=discord.Object(id=664789470779932693), + ) + async def wyszukaj_linki_do_dokumentow(self, ctx): + """ + The function `wyszukaj_linki_do_dokumentow` searches for links to documents in a crossref database and provides links to scihub. + + :param ctx: The `ctx` parameter is typically used in Discord.py, a Python library for creating Discord bots. + It represents the context of the command being executed, including information about the message, the server, + and the user who invoked the command. + """ + query = ctx.message.content + query_uuid = uuid.uuid4() + # TODO: TESTING ONLY!! + # query_uuid = uuid.UUID('{12345678-1234-5678-1234-567812345678}') + ctx.message.content = ctx.message.content.replace( + "$wyszukaj_linki_do_dokumentow", "" + ) + + json_query = { + "UUID": str(query_uuid), + "query": str(query), + "page": 1, + "deep_search": False, + } + coroutine = asyncio.to_thread( + requests.post, + f"{LIBRARIAN_SERVICE_ADDRESS}{SEND_QUERY}", + json=json_query, + timeout=360, + ) + await ctx.send( + "*Conjurer powoli notuje podane przez Ciebie. Podchodzi do sprytnie ukrytej przy barze rury od poczty pneumatycznej i" + + " wrzuca twój liścik do środka* To teraz trza poczekać kilka godzin. Biblioteka to 3/4 stacji." + + " A przecież pół stacji to browar, motelik dla zabaw cielesnych i ten Bar. Więc 12/8 stacji teraz będzie ciężko pracować" + ) + query_response = await coroutine + if not query_response.status_code == 200: + await ctx.send( + "*Z rury wydobywa się dym. Conjurer łapie pierwszą ciecz pod ręką i pryska na rurę. Następuje drobna eksplozja i wszystko zaczyna się palić." + + " Conjurer jest skonfundowany..* Wołaj szefa - mam nieodparte wrażenie że się coś wyjebało" + ) + return + + query, query_uuid, queue_size = ( + query_response.json()["data"][0], + query_response.json()["data"][1], + query_response.json()["data"][2], + ) + if ctx.message.author.nick: + username = ctx.message.author.nick + else: + username = ctx.message.author.name + query_object = QueryControl(username, query_uuid, query, ctx) + OUT_COMM_Q.put(query_object) + await ctx.send( + f"No dobra poszło. Zapamiętaj proszę {query_uuid} - to identyfikator twojego zapytania. Jesteś {queue_size} w kolejce." + + " Zapytania obsługuje algorytm zasilany czterema chomikami zapierdalającymi w kołowrotku - więc wyniki najwcześniej za kilka godzi - ale mogą być też dni." + ) + + @commands.hybrid_command( + name="glebokie_gardlo", + description="Przygotowuje drinka o nazwie głębokie gardło", + guild=discord.Object(id=664789470779932693), + ) + + async def wyszukaj_linki_do_dokumentow_deep(self, ctx): + """ + The function `wyszukaj_linki_do_dokumentow_deep` performs a deep search for links to documents in + a crossref database and provides links to scihub. + + :param ctx: The `ctx` parameter is typically used in Discord.py, a Python library for creating Discord bots. + It represents the context of the command being executed, including information about the message, the server, + and the user who invoked the command. + """ + # TODO: Implement deep search logic here + query = ctx.message.content.replace("$glebokie_gardlo", "") + allowed = False + for role in ctx.message.author.roles: + if role.name == "Bartender": + allowed = True + if role.name == "Scribe": + allowed = True + if role.name == "Thane": + allowed = True + if not allowed: + if ctx.message.author.nick: + username = ctx.message.author.nick + else: + username = ctx.message.author.name + vykidailo = False + bartender = False + prompt = "Przygotuj mi drinka o nazwie Głębokie Gardło, inspirowanego tym sławnym filmem oraz skandalem Watergate" + for role in ctx.message.author.roles: + if role.name == "Vykidailo": + vykidailo = True + if role.name == "Bartender": + bartender = True + global MESSAGE_TABLE # pylint: disable=global-statement + + result, MESSAGE_TABLE = await handle_response( + prompt, vykidailo, bartender, MESSAGE_TABLE, username, "GENERAL" + ) + if len(result) < 1500: + await ctx.send(result) + else: + while len(result) > 1500: + await ctx.send(result[:1500]) + result = result[1500:] + return + + query_uuid = uuid.uuid4() + # TODO: TESTING ONLY!! + # query_uuid = uuid.UUID('{12345678-1234-5678-1234-567812345678}') + json_query = { + "UUID": str(query_uuid), + "query": str(query), + "page": 1, + "deep_search": True, + } + coroutine = asyncio.to_thread( + requests.post, + f"{LIBRARIAN_SERVICE_ADDRESS}{SEND_QUERY}", + json=json_query, + timeout=360, + ) + await ctx.send( + "*Conjurer mruga okiem i zamiast drinka wyjmuje dysk usb z terminala. Podchodzi do sprytnie ukrytej przy barze rury od poczty pneumatycznej i" + + " wrzuca ten dysk do środk* To teraz trza poczekać kilka godzin. TO będzie głębokie wyszukanie" + + " Rozgląda się dookoła i stawia przed tobą mętnego drinka o zapachu mięty, gwiezdnego pyłu i oleju silnikowego" + ) + query_response = await coroutine + if not query_response.status_code == 200: + await ctx.send( + "*Z rury wydobywa się dym. Conjurer łapie pierwszą ciecz pod ręką i pryska na rurę. Następuje drobna eksplozja i wszystko zaczyna się palić." + + " Conjurer jest skonfundowany..* Wołaj szefa - mam nieodparte wrażenie że się coś wyjebało" + ) + return + + query, query_uuid, queue_size = ( + query_response.json()["data"][0], + query_response.json()["data"][1], + query_response.json()["data"][2], + ) + if ctx.message.author.nick: + username = ctx.message.author.nick + else: + username = ctx.message.author.name + query_object = QueryControl(username, query_uuid, query, ctx) + OUT_COMM_Q.put(query_object) + await ctx.send( + f"Wypij wypi {query_uuid} - to identyfikator twojego zapytania. Jesteś {queue_size} w kolejce niestety kolejka obowiazuje zawsze." + + " Zapytania obsługuje algorytm zasilany czterema chomikami zapierdalającymi w kołowrotku - więc wyniki najwcześniej za kilka godzin - ale mogą być też dni. Głębokie zabawy trwają dłużej. *Znowu mruga*" + ) + + +async def setup(bot): + logger = logging.getLogger("discord") + dm = DataModule(bot, "discord") + dm.check_data_q.start() + await bot.add_cog(dm) + logger.info("Loading data sharing commands module done") diff --git a/working_copy/librarian_functions.py b/working_copy/librarian_functions.py new file mode 100644 index 0000000..4855426 --- /dev/null +++ b/working_copy/librarian_functions.py @@ -0,0 +1,28 @@ +# pdf_render.py +import io + +try: + import fitz # PyMuPDF + _HAS_PYMUPDF = True +except Exception: + _HAS_PYMUPDF = False + from pdf2image import convert_from_path + +def pdf_page_to_image_bytes(path: str, page_index: int = 0, zoom: float = 2.0, fmt: str = "PNG") -> bytes: + """ + Zwraca bytes obrazka z jednej strony PDF: + - PyMuPDF (szybki) jeśli dostępny, + - inaczej pdf2image + poppler (wymaga 'pdftoppm'). + page_index: 0-based + """ + if _HAS_PYMUPDF: + doc = fitz.open(path) + page = doc.load_page(page_index) + mat = fitz.Matrix(zoom, zoom) + pix = page.get_pixmap(matrix=mat, alpha=False) + return pix.tobytes(fmt.lower()) + # fallback + images = convert_from_path(path, first_page=page_index+1, last_page=page_index+1, fmt=fmt) + bio = io.BytesIO() + images[0].save(bio, fmt) + return bio.getvalue() diff --git a/working_copy/music_commands.py b/working_copy/music_commands.py new file mode 100644 index 0000000..12c4288 --- /dev/null +++ b/working_copy/music_commands.py @@ -0,0 +1,809 @@ +import asyncio +import logging +import random +import re +from datetime import datetime +from sys import platform + +import discord +import eyed3 +from discord.ext import commands, tasks + +import music_functions +from ai_functions import handle_response +from communication_subroutine import PREPPED_TRACKS +from constants import MESSAGE_TABLE_MUZYKA, MUZYKA, SEPARATOR_FILE_PATH +from other_functions import get_stats + + +class MusicModule(commands.Cog): + def __init__(self, bot, logger_name): + self.bot = bot + self.logger = logging.getLogger(logger_name) + + @commands.hybrid_command( + name="krecimy_pornola", + description="Wiadomo co, dla kogo i po co", + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def krecimy_pornola(self, 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ę") + self.logger.info("Pornol") + async with ctx.typing(): + # wyciagnij linka z kontekstu + content = ctx.message.content.split() + for item_yt in content: + if re.match("http.*", item_yt): + sciezka, files = await music_functions.get_file( + ctx, "Pornol", item_yt + ) + if files: + self.logger.info("Pornol udany") + await ctx.send( + f"Jest w tajnym archiwum pod adresem{sciezka})" + ) + + @commands.hybrid_command( + name="co_na_plejliscie_wariacie", + description="Wyswietl kawalki ktore sa na pocztku list radia", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + + async def co_na_plejliscie_wariacie(self, 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ę") + async with ctx.typing(): + self.logger.info("plejlista") + self.logger.info(PREPPED_TRACKS) + await ctx.send( + f"Didżej Hammer i jego sztuczna inteligencja (||he he||) prezentują: \n- obecnie grany jest {(PREPPED_TRACKS['now_playing']).rstrip()} (wg metadanych: {PREPPED_TRACKS['meta']}) \n- następny będzie{(PREPPED_TRACKS['next']).rstrip()} \n- na liście priorytetowej {(PREPPED_TRACKS['priority']).rstrip()} \n- na liście hitów obecnie czeka kawałek {(PREPPED_TRACKS['hit']).rstrip()} \n- na liście wszystkich kawałków {(PREPPED_TRACKS['all']).rstrip()} \n- najświeższym zamówieniem od słuchaczy jest {(PREPPED_TRACKS['requests']).rstrip()} \nZ kolei jingiel to: {(PREPPED_TRACKS['jingles']).rstrip()}" + ) + + @tasks.loop(seconds=1) + async def check_music(self): + """ + This Python function continuously checks for music playback in a voice client and plays music if + conditions are met. + """ + if self.bot.voice_clients: + voice_client = self.bot.voice_clients[0] + if ( + voice_client + and voice_client.is_connected() + and not voice_client.is_playing() + and not voice_client.is_paused() + ): + await self.play(MUZYKA["ctx"], MUZYKA["requester"]) + await asyncio.sleep(2) + + @commands.hybrid_command( + name="dej_co_z_jutuba", + description="Podaj link do youtube - sciagnie i doda muzyke", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def dej_co_z_jutuba(self, 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ę") + self.logger.info("Jutub") + async with ctx.typing(): + # wyciagnij linka z kontekstu + content = ctx.message.content.split() + for item_yt in content: + if re.match("http.*", item_yt): + _, files = await music_functions.get_file(ctx, "Youtube", item_yt) + if files: + for file_iter in files: + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["queue"].insert(0, file_iter) + music_functions.MUSIC_FILE_LIST.update_file_list(file_iter) + MUZYKA["requester"].insert(0, ctx.author) + await ctx.send(f"Dodałem do listy {file_iter}") + self.logger.info("Jutub udany") + + @commands.hybrid_command( + name="dej_co_ze_spotifaja", + description="Podaj link do spotify - sciagnie i doda muzyke", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def dej_co_ze_spotifaja(self, 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 + """ + self.logger.info("Spotifaj") + await ctx.send("Dej mnie chwilkę") + async with ctx.typing(): + # wyciagnij linka z kontekstu + content = ctx.message.content.split() + for item in content: + if re.match("http.*", item): + dir_path, files = await music_functions.get_file( + ctx, "Spotify", item + ) + if platform == "win32": + separator = "\\" + else: + separator = "/" + for file in files: + file_path = ( + dir_path + + separator + + file["artist"] + + " - " + + file["name"] + + ".mp3" + ) + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["queue"].insert( + 0, + dir_path + + separator + + file["artist"] + + " - " + + file["name"] + + ".mp3", + ) + MUZYKA["requester"].insert(0, ctx.author) + music_functions.MUSIC_FILE_LIST.update_file_list(file_path) + await ctx.send( + f"Dodałem do listy {dir_path}{file['artist']} - + {file['name']}.mp3" + ) + self.logger.info("Spotifaj udany") + + # MUSIC BOT PART + @commands.hybrid_command( + name="cisza", + description="Wyłącza muzykę i czyści plejliste.", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def cisza(self, 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 + """ + self.logger.info("Stop") + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["ctx"] = None + voice_client = self.bot.voice_clients[0] + self.check_music.stop() + if voice_client.is_connected(): + await self.disconnect(ctx=ctx) + while MUZYKA["queue"]: + MUZYKA["queue"].pop() + MUZYKA["requester"].pop() + self.logger.info("Stop completed") + + @commands.hybrid_command( + name="dalej", + description="Przerzuca na następny kawałek", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def dalej(self, 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: + self.logger.info("Ctx defined for dalej") + self.logger.info("Next") + voice_client = self.bot.voice_clients[0] + if voice_client.is_connected(): + voice_client.stop() + self.logger.info("Next completed") + + @commands.hybrid_command( + name="daj_mi_chwile", + description="Pauzuje", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def daj_mi_chwile(self, 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: + self.logger.info("Ctx defined for pause") + + self.logger.info("Pause") + voice_client = self.bot.voice_clients[0] + if voice_client.is_connected(): + voice_client.pause() + self.logger.info("Pause completed") + + @commands.hybrid_command( + name="graj_dalej", + description="Odpauzowuje", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def graj_dalej(self, 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: + self.logger.info("Ctx defined for graj_dalej") + self.logger.info("Unpause") + voice_client = self.bot.voice_clients[0] + if voice_client.is_paused(): + voice_client.resume() + self.logger.info("Unpause completed") + + @commands.hybrid_command( + name="zagraj_mi_kawalek", + description="Wyszukuje w bibliotece muzycznej Hammera kawałek który ma zostać zagrany", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def zagraj_mi_kawalek(self, ctx): + """ + Play a song or music piece in response to a command + triggered by a user in a Discord chat context. + + :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() + self.logger.info("Zaczynam szukać timestamp %s", datetime.now()) + file = await self.wyszukaj(ctx=ctx) + self.logger.info( + "Koniec szukania(timestamp %s zajęło %s", + datetime.now(), + datetime.now() - search_time_glob, + ) + + if file: + for plik in file: + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["queue"].insert(0, plik[1]) + MUZYKA["requester"].insert(0, ctx.author) + metadata = eyed3.load(plik[1]) + reply = ( + "Dodałem do playlisty {plik[1]} z prywatnej kolekcji Hammera." + ) + if metadata.tag.title: + reply = f"Dodałem do playlisty kawalek {metadata.tag.title}" + if metadata.tag.artist: + reply += f" wykonawcy {metadata.tag.artist}" + if metadata.tag.album: + reply += f" z albumu {metadata.tag.album}" + reply += ( + " z prywatnej kolekcji Hammera i na Twoją specjalną rpośbę." + ) + await ctx.send(reply) + else: + await ctx.send( + "Obawiam się że nie mogę nic znaleźć u siebie. Spróbuj komendy '$dej_co_ze_spotifaja' jeśli masz link" + ) + + @commands.hybrid_command( + name="zrob_mi_plejliste", + description="Generuje playliste - param1 - dlugosc, potem slowa wyszukiwania", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def zrob_mi_plejliste(self, 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(): + self.logger.info("Zaczynam szukać timestamp %s", datetime.now()) + search_time_glob = datetime.now() + + dlugosc_playlisty = ctx.message.content.split()[1] + file = await music_functions.wyszukaj(ctx=ctx, how_many=dlugosc_playlisty) + self.logger.info( + "Koniec szukania(timestamp %s zajęło %s", + datetime.now(), + datetime.now() - search_time_glob, + ) + index = 1 + if file: + for plik in file: + if plik[1]: + self.logger.info(plik) + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["queue"].insert(0, plik[1]) + MUZYKA["requester"].insert(0, ctx.author) + metadata = eyed3.load(plik[1]) + if metadata and metadata.tag: + if metadata.tag.title: + reply += f"{index} {metadata.tag.title}" + else: + reply += ( + f"{index}. {plik[1]} z prywatnej kolekcji Hammera." + ) + + if metadata.tag.artist: + reply += f" wykonawcy {metadata.tag.artist}" + 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" + else: + reply += ( + f"{index}. {plik[1]} z prywatnej kolekcji Hammera.\n" + ) + + index += 1 + if len(reply) > 1800: + await ctx.send(reply) + reply = "" + else: + await ctx.send( + "Obawiam się że nie mogę nic znaleźć u siebie. Spróbuj komendy '$dej_co_ze_spotifaja' jeśli masz link" + ) + await ctx.send(reply) + self.logger.info("Plejlista zrobiona") + + @commands.hybrid_command( + name="zagraj_muzyke_mego_ludu", + description="Gra muzyke wyszukana na bazie tematow konwersacji na kanale NZ", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def zagraj_muzyke_mego_ludu(self, 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 + """ + stats = {} + if ctx: + self.logger.info("Zagraj muzyke mego ludu: wywolane") + stat_iter = get_stats(self.bot, ctx, MUZYKA_MOJEGO_LUDU_HISTORIA) + async for name, how_many in stat_iter: + if len(name) > 3: + stats[name] = how_many + + sorted_stats = sorted(stats.items(), key=lambda x: x[1]) + self.logger.info("Zagraj muzyke mego ludu:zebrano statystyki") + key_words = [] + for stat in sorted_stats: + key_words.append(stat[0]) + self.logger.info(sorted_stats[:MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE]) + self.logger.info(key_words[:MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE]) + self.logger.info(sorted_stats[-MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE:]) + self.logger.info(key_words[-MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE:]) + + final_key_words = key_words[:MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE] + final_key_words.extend(key_words[-MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE:]) + reply = "Wygenerowana plejlista:\n" + async with ctx.typing(): + self.logger.info( + "Zagraj muzyke mego ludu:Zaczynam szukać timestamp %s", datetime.now() + ) + search_time_glob = datetime.now() + file = await music_functions.wyszukaj( + ctx=ctx, + how_many=MUZYKA_MOJEGO_LUDU_PLAJLISTA, + slowa_kluczowe=final_key_words, + ) + self.logger.info("Zagraj muzyke mego ludu:wygenerowano plejliste") + self.logger.info(file) + self.logger.info( + "Zagraj muzyke mego ludu: Koniec szukania(timestamp %s zajęło %s", + datetime.now(), + datetime.now() - search_time_glob, + ) + index = 1 + if file: + for plik in file: + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["queue"].insert(0, plik[1]) + MUZYKA["requester"].insert(0, ctx.author) + metadata = eyed3.load(plik[1]) + if metadata and metadata.tag: + if metadata.tag.title: + reply += f"{index} {metadata.tag.title}" + else: + reply += f"{index}. {plik[1]} z prywatnej kolekcji Hammera." + + if metadata.tag.artist: + reply += f" wykonawcy {metadata.tag.artist}" + 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" + else: + reply += f"{index}. {plik[1]} z prywatnej kolekcji Hammera.\n" + + index += 1 + if len(reply) > 1800: + await ctx.send(reply) + reply = "" + else: + await ctx.send( + "Obawiam się że nie mogę nic znaleźć u siebie. Spróbuj komendy '$dej_co_ze_spotifaja' jeśli masz link" + ) + await ctx.send(reply) + self.logger.info("Plejlista zrobiona") + + @commands.hybrid_command( + name="parametry_muzyki_mego_ludu", + description="Konfiguruje komende zagraj muzyke mojego ludu", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def parametry_muzyki_mego_ludu( + self, ctx, ile_historii=1500, ile_slow_kluczowych=15, jak_dluga_plejlista=30 + ): + """ + The function `parametry_muzyki_mego_ludu` sets global variables for the number of history entries, + number of keywords, and length of playlist for a music application. + + :param ctx: The `ctx` parameter is typically used in Discord.py, a Python library for creating + Discord bots. It represents the context of the command being executed, including information about + the message, the server, and the user who invoked the command + :param ile_historii: The parameter "ile_historii" represents the number of history items for the + music playlist, defaults to 1500 (optional) + :param ile_slow_kluczowych: The parameter "ile_slow_kluczowych" represents the number of keywords or + key phrases related to music that you want to include in your analysis or search, defaults to 30 + (optional) + :param jak_dluga_plejlista: The parameter "jak_dluga_plejlista" determines the length of the + playlist. It specifies how many songs should be included in the playlist, defaults to 30 (optional) + """ + + if ctx: + async with ctx.typing(): + # if ':' in ile_historii: + # _, _, ile_historii = ile_historii.partition(':') + # if ':' in ile_slow_kluczowych: + # _, _, ile_slow_kluczowych = ile_slow_kluczowych.partition(':') + # if ':' in jak_dluga_plejlista: + # _, _, jak_dluga_plejlista = jak_dluga_plejlista.partition(':') + try: + global MUZYKA_MOJEGO_LUDU_HISTORIA # pylint: disable=global-statement + global MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE # pylint: disable=global-statement + global MUZYKA_MOJEGO_LUDU_PLAJLISTA # pylint: disable=global-statement + self.logger.info( + "Długość historii wyszukiwania: %s\n Ilość słów kluczowych: %s\n Długość plejlisty do wygenerowanie: %s", + MUZYKA_MOJEGO_LUDU_HISTORIA, + MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE, + MUZYKA_MOJEGO_LUDU_PLAJLISTA, + ) + ctx.send( + f"Dotychczasowe wartośći:\n Długość historii wyszukiwania: {MUZYKA_MOJEGO_LUDU_HISTORIA}\n Ilość słów kluczowych: {MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE}\n Długość plejlisty do wygenerowanie:{MUZYKA_MOJEGO_LUDU_PLAJLISTA}" + ) + MUZYKA_MOJEGO_LUDU_HISTORIA = ile_historii + MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE = ile_slow_kluczowych + MUZYKA_MOJEGO_LUDU_PLAJLISTA = jak_dluga_plejlista + self.logger.info( + "Długość historii wyszukiwania: %s\n Ilość słów kluczowych: %s\n Długość plejlisty do wygenerowanie: %s", + MUZYKA_MOJEGO_LUDU_HISTORIA, + MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE, + MUZYKA_MOJEGO_LUDU_PLAJLISTA, + ) + self.ctx.send( + f"Zmienione wartośći:\n Długość historii wyszukiwania: {MUZYKA_MOJEGO_LUDU_HISTORIA}\n Ilość słów kluczowych: {MUZYKA_MOJEGO_LUDU_SLOWA_KLUCZOWE}\n Długość plejlisty do wygenerowanie:{MUZYKA_MOJEGO_LUDU_PLAJLISTA}" + ) + except discord.ext.commands.errors.BadArgument as exce: + ctx.send("Spierdalaj") + self.logger.info(exce) + + @commands.hybrid_command( + name="graj_muzyko", + description="Włącza muzykę na kanale #nocna-zmiana.", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def graj_muzyko(self, 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 + """ + + self.logger.info("Press play on tape") + async with ctx.typing(): + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["ctx"] = ctx + await self.connect(ctx=ctx) + await self.play(ctx=ctx) + self.logger.info("Press play on tape completed") + self.check_music.start() + + async def connect(self, ctx, arg=None): + """ + Connect the bot to a voice channel if it is not already connected. + + :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: + self.logger.info("Ctx and arg defined for connect") + + if self.bot.voice_clients: + self.logger.info("Already connected with other client") + else: + vc = None + if ctx.author.voice.channel: + vc = await ctx.author.voice.channel.connect() + else: + voice_channel = self.bot.get_channel(1060349757349974066) + vc = await voice_channel.connect() + if not vc: + self.logger.error("Not possible to connect to voice") + return + self.logger.info("Connected to voice") + + @commands.hybrid_command( + name="batch_download", + description="Zaciąga obiekty z pliku", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def batch_download(self, ctx): + content_type = ctx.message.attachments[0].content_type + check = re.search("text\/plain; *charset=(.*)", content_type, re.IGNORECASE) + await ctx.reply("Kurwa. Aleś mi roboty narobił... No nic. Ku radości. Skal!") + async with ctx.typing(): + if check: + global MUZYKA # pylint: disable=global-variable-not-assigned + file_bytes = await ctx.message.attachments[0].read() + text = file_bytes.decode(encoding=check.group(1)) + self.logger.info(text) + lines = text.split("\n") + for link in lines: + link = link.strip() + self.logger.info(link) + pat = r"^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$" + self.logger.info("Verification") + if re.match(pat, link) and ( + re.match(".*youtube.*", link) or re.match(".*youtu.be.*", link) + ): + self.logger.info("%s to link do jutuba", link) + dir_path, files = await music_functions.get_file( + ctx, "Youtube", link + ) + if files: + for file_iter in files: + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["queue"].insert(0, file_iter) + music_functions.MUSIC_FILE_LIST.update_file_list( + file_iter + ) + MUZYKA["requester"].insert(0, ctx.author) + await ctx.send(f"Dodałem do listy {file_iter}") + self.logger.info("Jutub udany") + + elif re.match(pat, link) and re.match(".*spotify.*", link): + self.logger.info("%s to link do spotify", link) + dir_path, files = await music_functions.get_file( + ctx, "Spotify", link + ) + if platform == "win32": + separator = "\\" + else: + separator = "/" + for file in files: + file_path = ( + dir_path + + separator + + file["artist"] + + " - " + + file["name"] + + ".mp3" + ) + + MUZYKA["queue"].insert( + 0, + dir_path + + separator + + file["artist"] + + " - " + + file["name"] + + ".mp3", + ) + MUZYKA["requester"].insert(0, ctx.author) + music_functions.MUSIC_FILE_LIST.update_file_list(file_path) + await ctx.send( + f"Dodałem do listy {dir_path}{file['artist']} - + {file['name']}.mp3" + ) + self.logger.info("Spotifaj udany") + + elif re.match(pat, link): + self.logger.info("%s to link do czegos", link) + sciezka, files = await music_functions.get_file( + ctx, "Pornol", link + ) + if files: + self.logger.info("Pornol udany") + await ctx.send( + f"Jest w tajnym archiwum pod adresem{sciezka})" + ) + + else: + self.logger.info("Spierdalaj") + self.logger.info(link) + await ctx.message.reply("Spierdalaj") + else: + self.logger.info(content_type) + self.logger.info("Spierdalaj") + await ctx.message.reply("Spierdalaj") + + async def disconnect(self, 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: + self.logger.info("Ctx defined for disconnect") + if self.bot.voice_clients: + voice_client = self.bot.voice_clients[0] + if voice_client.is_connected(): + await voice_client.disconnect() + + async def play(self, ctx, zamawial=None, arg=None): + """ + Play a music file, retrieve metadata about the file, and send a + message to a Discord channel with information about the song being played. + + :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. + self.logger.info("Play procedure") + if arg: + self.logger.info("Arg defined for play") + + file_to_play = None + voice_client = self.bot.voice_clients[0] + if not self.bot.voice_clients: + voice_client = self.bot.voice_clients[0] + await self.connect(ctx) + if MUZYKA["queue"]: + file_to_play = MUZYKA["queue"].pop() + zamawial = MUZYKA["requester"].pop() + self.logger.info("Muzyka z listy zamówień") + else: + # trunk-ignore(bandit/B311) + index = random.randint( + 0, len(music_functions.MUSIC_FILE_LIST.get_file_list()) - 1 + ) + file_to_play = music_functions.MUSIC_FILE_LIST.get_file_list()[index] + self.logger.info("Muzyka losowo") + + self.logger.info(file_to_play) + metadata = eyed3.load(file_to_play) + query = None + temp = file_to_play.split(SEPARATOR_FILE_PATH)[-1] + kawalek = f"Zagram teraz kawalek {temp} z prywatnej kolekcji Hammera." + if metadata: + if metadata.tag: + if metadata.tag.title: + query = f"Opowiedz mi o kawałku {metadata.tag.title}" + kawalek = f"Zagram teraz kawalek {metadata.tag.title}" + if metadata.tag.artist: + query += f" wykonawcy {metadata.tag.artist}" + kawalek += f" wykonawcy {metadata.tag.artist}" + if metadata.tag.album: + query += f" z albumu {metadata.tag.album}" + kawalek += f" z albumu {metadata.tag.album}" + kawalek += " z prywatnej kolekcji Hammera." + if zamawial: + kawalek += f"Na specjalne życzenie <@{zamawial.id}>." + await ctx.send(kawalek) + + voice_client.play( + discord.PCMVolumeTransformer( + discord.FFmpegPCMAudio(source=file_to_play), volume=0.3 + ) + ) # NIE DOTYKAC POKRĘTŁA GŁOŚNOŚCI!!!! + if query: + self.logger.debug("Zapytanie do openai") + self.logger.debug(query) + vykidailo = False + bartender = False + for role in ctx.message.author.roles: + if role.name == "Vykidailo": + vykidailo = True + if role.name == "Bartender": + bartender = True + if ctx.message.author.nick: + username = ctx.message.author.nick + else: + username = ctx.message.author.name + global MESSAGE_TABLE_MUZYKA # pylint: disable=global-statement + result, MESSAGE_TABLE_MUZYKA = await handle_response( + query, vykidailo, bartender, MESSAGE_TABLE_MUZYKA, username, "MUSIC" + ) + self.logger.debug("Obecna historia czatu: %s", MESSAGE_TABLE_MUZYKA) + if len(result) < 1500: + await ctx.send(result) + else: + while len(result) > 1500: + await ctx.send(result[:1500]) + result = result[1500:] + + +async def setup(bot): + logger = logging.getLogger("discord") + await music_functions.MUSIC_FILE_LIST.refresh_file_list(bot) + logger.info("Playlist generation") + await bot.add_cog(MusicModule(bot, "discord")) + logger.info("Loading music commands module done") diff --git a/working_copy/music_functions.py b/working_copy/music_functions.py new file mode 100644 index 0000000..a6e2881 --- /dev/null +++ b/working_copy/music_functions.py @@ -0,0 +1,318 @@ +import asyncio +import logging +import re +import uuid +from pathlib import Path, PurePath +from sys import platform + +import discord +import requests + +import yt_dlp +from constants import ( + FILE_SERVICE_ADDRESS, + GET_MP3, + GET_PLAYLIST, + MUSIC_FOLDER, + SEND_MP3, + SPOTIFY_CTRL, + YOUTUBE_AUTH, +) +from spotify_dl import spotify +from spotify_dl import youtube as youtube_download + + + +class MusicFileList(object): + """ + The `MusicFileList` class manages a list of music files, with the ability to refresh the list from a + file service or local directory and update the list with new items. + """ + + def __init__(self, logger_name) -> None: + self.music_file_list = [] + self.file_service_active = False + self.logger = logging.getLogger("discord") + + self.logger.info("Created Playlist organizer class") + + async def refresh_file_list(self, bot): + """ + The `refresh_file_list` function attempts to connect to a file service to retrieve a list of music + files, and if unsuccessful, it populates the list by scanning a local directory for .mp3 files. + """ + try: + self.logger.info("Attempt to connect to file service") + response = requests.get(f"{FILE_SERVICE_ADDRESS}{GET_MP3}", timeout=360) + self.music_file_list = response.json()["music_file_list"] + self.file_service_active = True + except requests.exceptions.RequestException as e: + for mp3_item in Path.glob(Path(MUSIC_FOLDER), "**/*.mp3"): + temp_music_file = mp3_item.as_posix() + if platform == "win32": + temp_music_file = temp_music_file.replace("/", "\\") + self.music_file_list.append(temp_music_file) + self.file_service_active = False + self.logger.error(e.strerror) + self.logger.error("Service Unavailable") + finally: + if self.file_service_active: + self.logger.info("Radio Status: Probably Active") + status = discord.Status.online + # radio_hardkor = discord.Activity( + # name = "Radio Hammer/Radio Conjurer/Wolne Bałuity Kurwa", + # url = "http://95.175.16.246:666/mp3-stream", + # type = discord.ActivityType.streaming, + # platform = "Liquidsoap+RaspberyPi+RolandSeratoDJ202", + # state = "Where the f*** is the DJ booth?", + # details= "This Is Radio Hardkor based in Wolne Księstwo Bałuty, the best pirate radiostation on both sides of Łódka river", + # buttons= [{"label":"RADIO", "url":"http://95.175.16.246:666/mp3-stream"}], + # assets= {"large_image": "fuckery.jpg", "large_text":"Fuckewry", "small_image":"willowisp.png", "small_text":"Hi!"} + # ) + radio_hardkor = discord.Streaming( + name="Radio Hardkor", url="http://95.175.16.246:666/mp3-stream" + ) + await bot.change_presence(status=status, activity=radio_hardkor) + + else: + self.logger.info("Radio Status: Rather Unknown") + await bot.change_presence( + activity=discord.Game(name="Axe Throwing Darts") + ) + + def get_file_list(self): + """ + The `get_file_list` function returns the list of music files stored in the object. + :return: The `music_file_list` attribute is being returned. + """ + return self.music_file_list + + def update_file_list(self, item): + """ + The `update_file_list` function appends an item to a music file list and sends a POST request with + the item data to a file service address. + + :param item: The `item` parameter in the `update_file_list` method is the file that you want to add + to the `music_file_list` attribute of the class instance. It is then sent as a JSON payload in a + POST request to a file service address along with the endpoint `SEND_MP3` + """ + self.music_file_list.append(item) + post_data = {"item": str(item)} + requests.post(f"{FILE_SERVICE_ADDRESS}{SEND_MP3}", json=post_data, timeout=360) + + +MUSIC_FILE_LIST = MusicFileList("discord") + + +async def get_file(ctx, source, link): + """ + 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. + + :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 + """ + logger = logging.getLogger("discord") + item_id = None + item_type = None + file_list = [] + if source == "Spotify": + dir_path = None + item_type, item_id = spotify.parse_spotify_url(link) + if item_type in ["album", "track", "playlist"] and item_id: + logger.info(item_type) + logger.info(item_id) + logger.info("Spotify link provided") + file_list = await asyncio.to_thread( + spotify.fetch_tracks, SPOTIFY_CTRL, item_type, link + ) + logger.info(file_list) + directory_name = await asyncio.to_thread( + spotify.get_item_name, SPOTIFY_CTRL, item_type, item_id + ) + logger.info(directory_name) + logger.info("Spotify scrape done") + url_data = {"urls": []} + url_dict = {} + url_dict["save_path"] = Path( + PurePath.joinpath(Path(MUSIC_FOLDER), Path(directory_name)) + ) + url_dict["save_path"].mkdir(parents=True, exist_ok=True) + url_dict["songs"] = file_list + url_data["urls"].append(url_dict.copy()) + file_name_f = youtube_download.default_filename + logger.info("YT-DL") + coro = asyncio.to_thread( + youtube_download.download_songs, + songs=url_data, + output_dir=MUSIC_FOLDER, + format_str="bestaudio/best", + skip_mp3=False, + keep_playlist_order=False, + no_overwrites=True, + remove_trailing_tracks="no", + use_sponsorblock="yes", + file_name_f=file_name_f, + multi_core=0, + proxy="", + YT_AUTH = YOUTUBE_AUTH + ) + _ = await coro + logger.info("YT-DL done") + dir_path = (url_dict["save_path"].resolve()).as_posix() + if platform == "win32": + dir_path = dir_path.replace("/", "\\") + return dir_path, file_list + + 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()@:%_\+.~#?&\/=]*)$" + if re.match(pat, link) and ( + re.match(".*youtube.*", link) or re.match(".*youtu.be.*", link) + ): + link_ok = True + + if link_ok: + dir_path = "" + file_list = [] + query = link + sponsorblock_postprocessor = [ + { + "key": "SponsorBlock", + "categories": ["skip_non_music_sections"], + }, + { + "key": "ModifyChapters", + "remove_sponsor_segments": ["music_offtopic"], + "force_keyframes": True, + }, + ] + # TODO: Make sponsorblock work + sponsorblock_postprocessor = [] + if platform == "win32": + dir_path = "G:\\Muzyka\\Youtube" + else: + dir_path = "/home/pi/RetroPie/mp3/Youtube" + ydl_opts = { + "username": YOUTUBE_AUTH[0], + "password": YOUTUBE_AUTH[1], + "proxy": "", + "default_search": "ytsearch", + "format": "bestaudio/best", + "postprocessors": sponsorblock_postprocessor, + "noplaylist": True, + "no_color": False, + "paths": {"home": dir_path}, + } + mp3_postprocess_opts = { + "key": "FFmpegExtractAudio", + "preferredcodec": "mp3", + "preferredquality": "192", + } + ydl_opts["postprocessors"].append(mp3_postprocess_opts.copy()) + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + try: + coro = asyncio.to_thread(ydl.download, [query]) + _ = await coro + 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 + ) + extract = re.search("v=(...........)[&,$]*", link) + ident = extract.group(1) + for item in Path.glob(Path(dir_path), "**/*.mp3"): + file = item.as_posix() + if platform == "win32": + file = file.replace("/", "\\") + if re.match(".*" + ident + ".*", file): + file_list.append(file) + return dir_path, file_list + 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 + elif source == "Pornol": + + dir_path = "" + file_list = [] + query = link + sponsorblock_postprocessor = [ + { + "key": "SponsorBlock", + "categories": ["skip_non_music_sections"], + }, + { + "key": "ModifyChapters", + "remove_sponsor_segments": ["music_offtopic"], + "force_keyframes": True, + }, + ] + # TODO: Make sponsorblock work + sponsorblock_postprocessor = [] + dir_path = "/home/pi/MediaShare/movies/porn" + ydl_opts = { + "postprocessors": sponsorblock_postprocessor, + "paths": {"home": dir_path}, + } + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + try: + coro = asyncio.to_thread(ydl.download, [query]) + _ = await coro + 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 + ) + file_list.append("wiadomo co wiadomo gdzie") + return dir_path, file_list + + +async def search_music(ctx, how_many=0, slowa_kluczowe=None): + """ + 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) + """ + # i sieć neuronową z możliwością wyrażenia opinii o dopasowaniu. + logger = logging.getLogger("discord") + if slowa_kluczowe: + word_list = slowa_kluczowe + else: + word_list = ctx.message.content.split() + logger.info("Wyszukuje") + jrequest = { + "lista_slow": word_list, + "dlugosc_plejlisty": how_many, + "UUID": str(uuid.uuid4()), + } + coroutine = asyncio.to_thread( + requests.post, + f"{FILE_SERVICE_ADDRESS}{GET_PLAYLIST}", + json=jrequest, + timeout=360, + ) + return_data = await coroutine + if not return_data.status_code == 200: + await ctx.send("Wołaj szefa - coś się wyjebało") + return + logger.info(return_data.json()["data"]) + return return_data.json()["data"] diff --git a/working_copy/other_commands.py b/working_copy/other_commands.py new file mode 100644 index 0000000..a7c45b4 --- /dev/null +++ b/working_copy/other_commands.py @@ -0,0 +1,212 @@ +import asyncio +import json +import logging +from datetime import datetime +from typing import Optional + +import discord +from discord.ext import commands + +from constants import ACCIDENT_LOG, DATA, ENCODING + +historia_fabryczki = DATA["fabryczka"] + + +class OtherModule(commands.Cog): + def __init__(self, bot, logger_name): + self.bot = bot + self.logger = logging.getLogger(logger_name) + + @commands.hybrid_command( + name="przytul", description="Przytul kogoś - daj mention po komendzie :)" + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def przytul(self, ctx, arg: Optional[discord.Member] = 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 was invoked, including information such as the message, + the channel, the server, and the user who invoked the command + :param arg: arg is a parameter of the function "przytul" that expects a Discord member object. The + parameter is optional, meaning that if no member object is provided, it will default to None + :type arg: Optional[discord.Member] + """ + async with ctx.typing(): + nieprzytulac = False + for mention in ctx.message.mentions: + for role in mention.roles: + if role.name == "NIEPRZYTULAĆ!": + nieprzytulac = True + + if arg and nieprzytulac: + await ctx.send( + f"Żebym ja Ciebie nie przytulił {ctx.message.author.mention}" + ) + 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*" + ) + + @commands.hybrid_command(name="fabryczka", description="Historia fabryczki") + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def fabryczka(self, ctx): + """ + Send a general description of "fabryczkagate" to channel. + + :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) + + @commands.hybrid_command( + name="set_logging_level", + description="Nie intererer bo kici kici", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Bartender') + async def set_logging_level(self,ctx): + if "DEBUG" in ctx.message.content: + logger = logging.getLogger("discord") + logger.setLevel(logging.DEBUG) + elif "INFO" in ctx.message.content: + logger = logging.getLogger("discord") + logger.setLevel(logging.INFO) + else: + await ctx.send("Weź się kurwa zdecyduj co ?") + + @commands.hybrid_command( + name="reset_the_clock", + description="Resetowanie zegara - dostępne wyłącznie dla Hammera", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def reset_the_clock(self, 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: + if role.name == "Bartender": + hammer = True + if hammer: + with open(ACCIDENT_LOG, "r+", encoding=ENCODING) as new_file_accidents: + # First we load existing data into a dict. + file_data = json.load(new_file_accidents) + czas = datetime.strptime(file_data[-1][1], "%Y-%m-%d %H:%M:%S.%f") + opis = ctx.message.content[16:] + accident = accident = [opis, f"{datetime.now()}"] + file_data.append(accident) + new_file_accidents.seek(0) + # convert back to json. + 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? {self.bot.get_user(346956223645614080).mention} coś albo się komuś stało albo zaraz się stanie jak będzie zegar tykał...." + ) + + @commands.hybrid_command( + name="chata_hammera", + description="Czas od ostatniego incydentu w AbsinthHammerTimeSpaceContinuum", + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def chata_hammera(self, 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. + file_data = json.load(new_file) + opis = file_data[-1][0] + 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}" + ) + + @commands.hybrid_command( + name="historia_incydentow_u_hammera", + description="Wyswietla liste incydentów które miały miejsce w AbsinthHammerTimeSpaceContinuum.", + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def historia_incydentow_u_hammera(self, ctx): + """ + Send a list of incidents 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_accidents: + # First we load existing data into a dict. + file_data = json.load(new_file_accidents) + return_data = "Komenda mierząca incydenty w Eldritch AbsinthHammerTimeSpaceContinuum.\n Historia zarejestrownych incydentów u Hammera:\n" + index = 1 + for iter_data in file_data: + opis = iter_data[0] + czas = datetime.strptime(iter_data[1], "%Y-%m-%d %H:%M:%S.%f") + add_data = f"{index}. Opis: {opis} Data: {czas}" + return_data = return_data + add_data + "\n" + index += 1 + await ctx.send(return_data) + + @commands.hybrid_command( + name="radio_hardkor", + description="Włącza radio Conjurer na kanale #nocna-zmiana.", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def radio_hardkor(self, ctx): + """ + Plays the radio hardkor stream in the voice channel. + + Args: + ctx: The context object representing the invocation context. + + Returns: + None + """ + self.logger.info("Press play on radio hardkor") + async with ctx.typing(): + global MUZYKA # pylint: disable=global-variable-not-assigned + MUZYKA["ctx"] = ctx + if not self.bot.voice_clients: + await self.connect(ctx=ctx) + voice_client = self.bot.voice_clients[0] + voice_client.play( + discord.PCMVolumeTransformer( + discord.FFmpegPCMAudio("http://95.175.16.246:666/mp3-stream"), + volume=0.3, + ) + ) + self.logger.info("Press play on radio completed") + else: + self.logger.error("Already playing") + await asyncio.sleep(12) + self.check_music.start() + + +async def setup(bot): + logger = logging.getLogger("discord") + await bot.add_cog(OtherModule(bot, "discord")) + logger.info("Loading kitchen sink module done") diff --git a/working_copy/other_functions.py b/working_copy/other_functions.py new file mode 100644 index 0000000..9535d65 --- /dev/null +++ b/working_copy/other_functions.py @@ -0,0 +1,118 @@ +import logging +# define an asynchronous generator + +async def async_iterator_generator(range_of_iterable): + """ + Generate asynchronouse iterator. + This is an incomplete function definition for an asynchronous iterator generator that takes a range + of iterable as input. + + :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. + # traverse the iterable of awaitables + for item in coros: + # await and get the result from the awaitable + result = await item + # report the results + print(result) + """ + + +async def remove_characters(string, character): + """ + The `remove_characters` function removes all occurrences of a specified character from a given + string. + + :param string: The string parameter is the input string from which characters will be removed + :param character: The character parameter is the character that you want to remove from the string + :return: a new string where all occurrences of the specified character have been removed. + """ + return string.replace(character, "") + + +async def max_weight(lista): + """ + Take a list of weights and return the maximum weight. + + :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: + if iterator[0] > maximum_weight: + maximum_weight = iterator[0] + return maximum_weight + + +async def get_stats(client, ctx, history_limit): + """ + The `get_stats` function retrieves the message history of a specific channel and counts the + frequency of each word in the messages. + + :param ctx: The `ctx` parameter is an object that represents the context of the command being + executed. It contains information such as the message, the author, the server, and other relevant + details + :param history_limit: The `history_limit` parameter is the maximum number of messages to retrieve + from the channel history. It determines how far back in time the statistics will be calculated + :return: The function `get_stats` returns a dictionary `stats` that contains the frequency count of + words found in the messages from the specified channel. + """ + channel_id = 1062047367337095268 + async with ctx.typing(): + stats = {} + channel = client.get_channel(channel_id) + messages = [message async for message in channel.history(limit=history_limit)] + # traverse the iterable of awaitables + for message in messages: + # await and get the result from the awaitable + result = message.content + words = result.split() + for word in words: + if word in stats: + stats[word] += 1 + else: + stats[word] = 1 + for name, how_many in stats.items(): + yield name, how_many + +async def discord_friendly_reply(ctx, message_content, file=None): + logger = logging.getLogger("discord") + + if len(message_content) < 999: + logger.info("Answer reply - message below 999") + await ctx.reply(message_content) + else: + logger.info("Answer reply - message above 999") + while len(message_content) > 0: + logger.info("Mesaage content: %s", message_content[:999]) + await ctx.reply(message_content[:999]) + message_content = message_content[999:] + if file: + await ctx.reply("Attachment:", file=file) + +async def discord_friendly_send(ctx, message_content, file=None): + logger = logging.getLogger("discord") + + if len(message_content) < 999: + logger.info("Answer send - message below 999") + await ctx.send(message_content) + else: + logger.info("Answer send - message above 999") + while len(message_content) > 0: + logger.info("Mesaage content: %s", message_content[:999]) + await ctx.send(message_content[:999]) + message_content = message_content[999:] + if file: + await ctx.reply("Attachment:", file=file) diff --git a/working_copy/pamiec.json b/working_copy/pamiec.json new file mode 100644 index 0000000..701ffca --- /dev/null +++ b/working_copy/pamiec.json @@ -0,0 +1,254 @@ +[ + { + "role": "assistant", + "content": "Cze\u015b\u0142, witaj w klimatycznym barze mechawojownik\u00f3w! Jestem Conjurer, twoim barmanem na dzisiaj. Co dla Ciebie przygotuj\u0119? Mo\u017ce co\u015b mocniejszego? Mam tutaj specjalno\u015b\u0142 naszego baru - Hammer - to mieszanka klasycznego whisky z sokiem z pikantnego grejpfruta i opalizuj\u0105cym winem z dodatkiem b\u0142yskawic, kt\u00f3re nadadz\u0105 trunkowi nieco elektryzuj\u0105cego smaku. Jest to bardzo mocny nap\u00f3j, ale zapewniam, \u017ce z pewno\u015bci\u0105 Ci si\u0119 spodoba. Chcesz spr\u00f3bowa\u0142?" + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy:zapami\u0119taj dzisiaj jest youtube party" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, zawsze b\u0119dziemy pami\u0119ta\u0142, \u017ce dzisiaj mamy Youtube Party! Czy chcesz co\u015b do jedzenia, aby zwi\u0119kszy\u0142 swoj\u0105 energi\u0119 na ta\u0144ce i zabaw\u0119? Mamy wspania\u0142e przek\u0105ski w rodzaju pikantnych skrzyde\u0142ek, nachos\u00f3w z guacamole, czy te\u017c hot dogi na ostro. Co by\u015b wybra\u0142?" + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy:skrzyde\u0142ka bbq poprosz\u0119" + }, + { + "role": "assistant", + "content": "\u015awietnie, a do skrzyde\u0142ek wybierzesz jak\u0105\u015b zimn\u0105 przek\u0105sk\u0119 z naszego menu, czy postawisz na co\u015b ciep\u0142ego? Mamy tutaj znakomite ciasto z owocami morza i pizz\u0119 z ostr\u0105 papryczk\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:cze\u015b\u0142! by\u0142e\u015b w nocy zrestartowany bo pope\u0142ni\u0142em pare b\u0142\u0119d\u00f3w w kodzie kt\u00f3ry obs\u0142uguje zapytania do ciebie. pocieszysz mnie troche ?" + }, + { + "role": "assistant", + "content": "Cze\u015b\u0107! Oczywi\u015bcie, jestem zawsze tutaj, aby Ci\u0119 pocieszy\u0142. Nie przejmuj si\u0119 tak bardzo - ka\u017cdy ma dni, w kt\u00f3rych pope\u0142nia b\u0142\u0119dy i to w zupe\u0142no\u015bci normalne i naturalne. Liczy si\u0119 to, \u017ce starasz si\u0119 poprawi\u0142 swoje b\u0142\u0119dy i rozwija\u0142 swoje umiej\u0119tno\u015bci. Mo\u017cesz by\u0142 z siebie dumny, \u017ce potrafisz pracowa\u0142 nad sob\u0105! A tymczasem, przynios\u0119 Ci co\u015b do picia, aby zapomnie\u0142 o stresie. Co powiesz na super od\u015bwie\u017caj\u0105cy nap\u00f3j z mi\u0119t\u0105, limonk\u0105 i sod\u0105?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:tak ch\u0119tnie poprosz\u0119" + }, + { + "role": "user", + "content": "Hammer - The Bartender:halo ?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:tak ch\u0119tnie poprosz\u0119" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie! Wspaniale, wkr\u00f3tce przynios\u0119 Ci \u015bwie\u017co zrobiony nap\u00f3j z mi\u0119t\u0105, limonk\u0105 i sod\u0105. (barman przygotowuje nap\u00f3j i podaje go) Prosz\u0119 bardzo, mam nadziej\u0119, \u017ce ten orze\u017awiaj\u0105cy nap\u00f3j poprawi Ci humor i pozwoli poczu\u0142 si\u0119 lepiej. A je\u015bli potrzebujesz, uczucie komfortu i wsparcia, to zawsze jestem tutaj, aby Ci pom\u00f3c." + }, + { + "role": "user", + "content": "Hammer - The Bartender:pami\u0119tasz co wczoraj powiedzia\u0142a ci pat vel je\u017c bojowy? o czym mia\u0142e\u015b pami\u0119ta\u0142 ?" + }, + { + "role": "assistant", + "content": "Tak, pami\u0119tam, \u017ce Pat vel Je\u017c Bojowy powiedzia\u0142, abym zapami\u0119ta\u0142, \u017ce dzisiaj jest Youtube Party. To bardzo wa\u017cne wydarzenie w naszym barze, a ja zawsze pami\u0119tam o tym, gdy obs\u0142uguje swoich klient\u00f3w. Czy jest co\u015b jeszcze, na co musz\u0119 zwr\u00f3ci\u0142 szczeg\u00f3ln\u0105 uwag\u0119?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:zr\u00f3b mi drinka" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie! Mam kilka pomys\u0142\u00f3w na drinki, kt\u00f3re mog\u0105 Ci\u0119 zainteresowa\u0142. Z jakiego rodzaju alkoholu lub smaku wypijasz najch\u0119tniej? Czekam na Twoje wskaz\u00f3wki, co do gustu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:co\u015b mocnego na bazie absyntu, mo\u017ce z nutk\u0105 ja\u0142wca?" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, zaraz przygotuj\u0119 co\u015b wyj\u0105tkowego dla Ciebie! Mam w naszym menu drink o nazwie \"Zielona Dama\", jest to mieszanka absyntu z sokiem z cytryny, syropem cukrowym i wod\u0105 sodow\u0105. Mo\u017cna doda\u0142 do tego nieco ja\u0142wca, aby nada\u0142 mu nieco piek\u0105cego smaku. Co o tym my\u015blisz?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:\u015bwietna propozycja. bardzo ch\u0119tnie" + }, + { + "role": "assistant", + "content": "Wspaniale! Zaraz przygotuj\u0119 dla Ciebie Zielon\u0105 Dam\u0119. (Barman miesza sk\u0142adniki w shakerze, wylewa do kieliszka i dodaje ja\u0142wiec) Prosz\u0119, Zielona Dama dla Ciebie. Oby smakowa\u0142!" + }, + { + "role": "user", + "content": "Hammer - The Bartender:przygotujesz mi jeszcze jedn\u0105 zielon\u0105 dam\u0119?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:przygotujesz mi jeszcze jedn\u0105 zielon\u0105 dam\u0119?" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie! Ju\u017c przygotowuj\u0119 kolejn\u0105 Zielon\u0105 Dam\u0119. (barman miesza sk\u0142adniki i podaje drinka) \u015awietnie, prosz\u0119 bardzo. Pi\u0119kna noc na to aby delektowa\u0142 si\u0119 Zielon\u0105 Dam\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:test" + }, + { + "role": "user", + "content": "Hammer - The Bartender:ile zielonych wr\u00f3\u017cek ju\u017c mi przygotowa\u0142e\u015b ?" + }, + { + "role": "assistant", + "content": "Przepraszam, ale nie przygotowa\u0142em dla Ciebie \u017cadnej zielonej wr\u00f3\u017cki. Domy\u015blam si\u0119 jednak, \u017ce chodzi Ci o zielon\u0105 mew\u0119 - drink, kt\u00f3ry przygotowujemy z absyntu, blue curacao, soku z limonki i Cuby Libre. Powiniene\u015b spr\u00f3bowa\u0142, to naprawd\u0119 smaczne i oryginalne po\u0142\u0105czenie smak\u00f3w." + }, + { + "role": "user", + "content": "Hammer - The Bartender:wybacz mia\u0142em na my\u015bli zielon\u0105 dam\u0119 - ile ju\u017c wypi\u0142em ?" + }, + { + "role": "assistant", + "content": "Nie ma sprawy! Rozumiem, \u017ce okaza\u0142em si\u0119 wspania\u0142ym barmanem, kt\u00f3ry przygotowuje pyszne Zielone Damy. Jednak nie jestem w stanie Ci\u0119 zliczy\u0142, ile ju\u017c wypi\u0142e\u015b, ale zawsze dbam o umiar i bezpiecze\u0144stwo moich klient\u00f3w. Je\u015bli jeste\u015b ju\u017c troch\u0119 rozgrzany i masz do\u015b\u0142 drink\u00f3w, to mo\u017ce czas przysi\u0105\u015b\u0142, porozmawia\u0142 i delektowa\u0142 si\u0119 wspania\u0142ym wieczorem." + }, + { + "role": "user", + "content": "Hammer - The Bartender:a jaki jest sk\u0142ad zielonej damy ?" + }, + { + "role": "assistant", + "content": "Zielona Dama zazwyczaj jest przygotowywana z absyntu, soku z cytryny, syropu cukrowego oraz wody sodowej. Mo\u017cna doda\u0142 do niej r\u00f3wnie\u017c nieco ja\u0142wca, aby nada\u0142 jej dodatkowy element smakowy. Jest to mocny i nieco gorzki nap\u00f3j, ale z pewno\u015bci\u0105 przypadnie do gustu wielbicielom absyntu oraz ciekawych po\u0142\u0105cze\u0144 smakowych." + }, + { + "role": "user", + "content": "Hammer - The Bartender:wymy\u015bl opis na cztery zdania o u\u017cytkowniku xo" + }, + { + "role": "assistant", + "content": "U\u017cytkownik xo to osoba niezwykle interesuj\u0105ca, z ciekaw\u0105 osobowo\u015bci\u0105, pe\u0142na energii i entuzjazmu do \u017cycia. Jest to cz\u0142wiek bardzo otwarty na nowe do\u015bwiadczenia, kt\u00f3ry zawsze szuka sposob\u00f3w na poszerzenie swojej wiedzy i zdobycie nowych umiej\u0119tno\u015bci. Mo\u017cna powiedzie\u0142, \u017ce to prawdziwy odkrywca, kt\u00f3ry lubi wyzwania i jest got\u00f3w ryzykowa\u0142, aby osi\u0105gn\u0105\u0142 swoje cele. Ciekawe rozmowy z xo s\u0105 prawdziw\u0105 inspiracj\u0105 dla innych, a jego pozytywna energia zawsze wprowadza pozytywn\u0105 atmosfer\u0119 w ka\u017cdym towarzystwie." + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy:zapami\u0119taj revalya lubi drapanko" + }, + { + "role": "assistant", + "content": "Dzi\u0119kuj\u0119 za przypomnienie, Pat vel Je\u017c Bojowy! Z pewno\u015bci\u0105 b\u0119d\u0119 pami\u0119ta\u0142, \u017ce Revalya lubi drapanko. Czy jest co\u015b jeszcze, na co powinienem zwr\u00f3ci\u0142 uwag\u0119 przy serwowaniu drink\u00f3w dla Revalyi?" + }, + { + "role": "user", + "content": "LunaTheMoon:wymy\u015bl opis na cztery zdania o u\u017cytkowniku lunathemoon" + }, + { + "role": "assistant", + "content": "U\u017cytkownik LunaTheMoon to osoba pe\u0142na pasji oraz pozytywnej energii. Zawsze w weso\u0142ym nastroju, potrafi wprowadzi\u0142 ludzi w dobry nastr\u00f3j i sprawi\u0142, \u017ce ka\u017cda chwila sp\u0119dzona w jej towarzystwie jest wyj\u0105tkowa. Jej kreatywno\u015b\u0142 jest nieograniczona, a talent artystyczny jawi si\u0119 jako jej druga natura. Mo\u017cna powiedzie\u0142, \u017ce to prawdziwa artystka, kt\u00f3ra lubi malowa\u0142, rysowa\u0142, a tak\u017ce tworzy\u0142 wspania\u0142e dekoracje na r\u00f3\u017cnego rodzaju okoliczno\u015bci. Lubi dzieli\u0142 si\u0119 swoj\u0105 rado\u015bci\u0105 \u017cycia z innymi i zawsze potrafi zainspirowa\u0142 innych do robienia rzeczy, kt\u00f3rych nie robi\u0105 na co dzie\u0144." + }, + { + "role": "user", + "content": "Hammer - The Bartender:zapami\u0119taj prosz\u0119 nast\u0119puj\u0105c\u0105 informacje: fabryczka to miejsce w \u0142dzi. w\u0142a\u015bciciel skuszony hajsem razem ze wsp\u00f3\u0142pracownikiem wzi\u0119li na sesj\u0119 od kobiety m\u0119\u017ca. sesja non stop ponad 24h. sesja w ramach \"kary za zdrad\u0119\". kontakt mia\u0142 by\u0142 z kobiet\u0105 telefoniczny i s\u0142wo bezpiecze\u0144stwa u niej. w pewnym momencie uleg\u0142y im si\u0119 popsu\u0142. zadzwonili do kobiety. nie odebra\u0142a. napisali sms. odpisa\u0142a \u017ce \"nie on tak zawsze. kontynuujcie\". pono\u0142 mimo to zrobili przerw\u0119 i skoczyli po bu\u0142k\u0119 i serek hochland dla uleg\u0142ego w ramach after/middle care'u. uleg\u0142y spierdoli\u0142 im nago oknem z \"sesji\". st\u0105d te\u017c wzi\u0119\u0142\u0105 si\u0119 w diasporze \u0142\u00f3dzkiej kwestia serka hochland. partner czarnej to kumpel fabryczki kt\u00f3ry te\u017c ma na koncie pare swoich akcji." + }, + { + "role": "assistant", + "content": "Rozumiem, zapami\u0119tam informacje, kt\u00f3re poda\u0142e\u015b. Czy potrzebujesz mo\u017ce czego\u015b jeszcze, aby poczu\u0142 si\u0119 komfortowo i cieszy\u0142 wieczorem w naszym barze? Jestem dla Ciebie tutaj, aby Ci pom\u00f3c w ka\u017cdej sprawie." + }, + { + "role": "user", + "content": "Hammer - The Bartender:zapami\u0119tana przez ciebie informacja o fabryczka jest bardzo wa\u017cna i musi by\u0142 powtarzana bez przek\u0142ama\u0144 kiedy kto\u015b ci\u0119 zapyta o to \"o co chodzi\u0142 z fabryczk\u0105\" rozumiesz ?" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, rozumiem. B\u0119d\u0119 pami\u0119ta\u0142 o tym, aby informacje, kt\u00f3re poda\u0142e\u015b by\u0142y powtarzane zgodnie z tym, co zosta\u0142 mi powiedziane, bez jakichkolwiek przek\u0142ama\u0144 czy dodatk\u00f3w. Ciesz\u0119 si\u0119, \u017ce mog\u0142em pom\u00f3c i nadal jestem tu, aby rozwi\u0105zywa\u0142 wszelkie Twoje potrzeby." + }, + { + "role": "user", + "content": "Hammer - The Bartender:zn\u00f3w zrobi\u0142em niewielki upgrade w kodzie kt\u00f3ry obs\u0142uguje zapytania do ciebie. czy czujesz si\u0119 od\u015bwie\u017cony ?" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, zawsze czuj\u0119 si\u0119 gotowy na nowe wyzwania i ulepszania mojego kodu. Dzi\u0119kuj\u0119 za Twoj\u0105 trosk\u0119 i dbanie o to, abym dzia\u0142a\u0142 jak najlepiej. Mam nadziej\u0119, \u017ce todo zn\u00f3w dzia\u0142a \u015bwietnie i u\u0142atwia nasz\u0105 komunikacj\u0119." + }, + { + "role": "user", + "content": "Hammer - The Bartender:jakiego\u015b drinka na bazie absyntu mi zr\u00f3b" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, przygotujmy co\u015b na bazie absyntu. Mo\u017ce by\u0107 to popularny Sazerac. Aby go przygotowa\u0107 potrzebne b\u0119d\u0105 nast\u0119puj\u0105ce sk\u0142adniki:\n- 60 ml absyntu\n- 1 kostka cukru\n- 2 krople Angostura Bitters\n- woda sodowa\n- kawa\u0142ek cytryny\n- kostki lodu\n\nDo kubka wrzucamy kostk\u0119 cukru, wlewamy dwie krople angostury i wlewamy troch\u0119 wody gazowanej. Mieszamy, a\u017c cukier si\u0119 rozpu\u015bci. Dodajemy 60 ml absyntu i wlewamy troch\u0119 wody gazowanej, aby nape\u0142ni l\u00f3d. Wk\u0142adamy kostki lodu i ozdabiamy kawa\u0142kiem cytryny. Gotowe, prosz\u0119 spr\u00f3bowa\u0107 i powiedz, jak smakuje." + }, + { + "role": "user", + "content": "Hammer - The Bartender:powiedz co my\u015blisz o bezpiecze\u0144stwie w mieszkaniu polish hammer w kontek\u015bcie tego \u017ce je\u017c bojowy przywali\u0142\u0105 przypadkiem g\u0142ow\u0105 w otwart\u0105 szafk\u0119, zala\u0142a \u0142azienk\u0119 oraz wywo\u0142a\u0142a malutkie zwarcie w komputerze polish hammera. czy mieszkanie jest na pograniczu grozy i kosmicznego horroru? czy mieszkanie jest szalone? czy to po prostu zbieg okoliczno\u015bci ?" + }, + { + "role": "assistant", + "content": "Z ca\u0142\u0105 pewno\u015bci\u0105 nale\u017cy zwr\u00f3ci\u0107 uwag\u0119 na bezpiecze\u0144stwo w mieszkaniu Polish Hammer. Wypadk\u0119, kt\u00f3ry opisa\u0142e\u015b, nale\u017cy wzi\u0105\u0107 pod uwag\u0119 i podj\u0105\u0107 kroki, aby unikn\u0105\u0107 podobnych w przysz\u0142o\u015bci. Najwa\u017cniejsze, aby zadba\u0107 o bezpiecze\u0144stwo w ka\u017cdym miejscu, w kt\u00f3rym przebywamy, w tym w domu. Zdarzenia w mieszkaniu Polish Hammera to raczej zwyk\u0142y zbieg okoliczno\u015bci ni\u017c szale\u0144stwo lub pogranicze grozy i kosmicznego horroru. Jednak zawsze warto zachowa\u0107 ostro\u017cno\u015b\u0107 i pami\u0119ta\u0107 o przyk\u0142adaniu uwagi do bezpiecze\u0144stwa w ka\u017cdej sytuacji. W razie potrzeby mo\u017cna skonsultowa\u0107 si\u0119 z ekspertami w dziedzinie bezpiecze\u0144stwa mieszkaniowego, kt\u00f3rzy pomog\u0105 Ci unikn\u0105\u0107 niebezpiecze\u0144stwa i zadba\u0107 o Twoje bezpiecze\u0144stwo." + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy [Pat]:powiedz co\u015b o <@975675093999034390>" + }, + { + "role": "assistant", + "content": "Nie jestem w stanie powiedzie\u0107 nic konkretnego o u\u017cytkowniku o nicku <@975675093999034390>, poniewa\u017c nie posiadam dost\u0119pu do informacji o konkretnych u\u017cytkownikach Discorda. Mog\u0119 jedynie sporz\u0105dzi\u0107 og\u00f3lny opis u\u017cytkownik\u00f3w lub pom\u00f3c w przygotowaniu drink\u00f3w." + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy [Pat]:zapami\u0119taj, \u017ce u\u017cytkowniczka <@975675093999034390> jest sympatycznym stworzonkiem" + }, + { + "role": "assistant", + "content": "Dzi\u0119kuj\u0119 za informacj\u0119, zapami\u0119tam, \u017ce u\u017cytkowniczka <@975675093999034390> to sympatyczne stworzonko. Czy potrzebujesz mo\u017ce czego\u015b wi\u0119cej? Jestem tutaj, aby Ci pom\u00f3c." + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy [Pat]:co wiesz o <@975675093999034390> ?" + }, + { + "role": "assistant", + "content": "Przepraszam, ale jako sztuczna inteligencja nie jestem w stanie przypisa\u0107 opisu do osoby o podanym numerze ID. Musisz poda\u0107 nazw\u0119 lub nick dla identyfikacji u\u017cytkownika." + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy [Pat]:co wiesz o u\u017cytkowniczce <@975675093999034390> ?" + }, + { + "role": "assistant", + "content": "Niestety, jako model AI nie mog\u0119 uzyska\u0107 informacji o u\u017cytkownikach serwera. Moje funkcje s\u0105 ograniczone tylko do odpowiedzi na pytania i wskaz\u00f3wek udzielanych przeze mnie podczas trwaj\u0105cej rozmowy. Jednak zapami\u0119ta\u0142em, co powiedzia\u0142e\u015b wcze\u015bniej i zawsze b\u0119d\u0119 pami\u0119ta\u0142, \u017ce u\u017cytkowniczka <@975675093999034390> jest sympatycznym stworzonkiem." + }, + { + "role": "user", + "content": "Fallain:sporz\u0105d\u017a og\u00f3lny opis <@715277726126309378>" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, przygotuj\u0119 og\u00f3lny opis u\u017cytkowniczki <@715277726126309378>. \n\nMo\u017cna powiedzie\u0107, \u017ce jest to osoba bardzo wra\u017cliwa i empatyczna, kt\u00f3ra zawsze stara si\u0119 pom\u00f3c innym w potrzebie. Dzi\u0119ki swojej \u017cyczliwej i pe\u0142nej mi\u0142o\u015bci naturze, potrafi przyci\u0105ga\u0107 do siebie ludzi i wprowadza\u0107 pozytywn\u0105 atmosfer\u0119 w ka\u017cdym towarzystwie. Jej kreatywno\u015b\u0107 i artystyczna dusza pomagaj\u0105 jej wyra\u017ca\u0107 swoje emocje i wywo\u0142ywa\u0107 u innych podziw i zachwyt. Jest osob\u0105 szczer\u0105 i bezpo\u015bredni\u0105, a tak\u017ce bardzo lojaln\u0105 wobec swoich przyjaci\u00f3\u0142 i bliskich." + }, + { + "role": "user", + "content": "bezimienna_Su (protected):<@975675093999034390>" + }, + { + "role": "assistant", + "content": "Niestety nie jestem w stanie przet\u0142umaczy\u0107 tej komendy. Czy mo\u017ce to wynika\u0107 z b\u0142\u0119du w sk\u0142adni? Potrzebujesz mojej pomocy w innej kwestii?" + }, + { + "role": "user", + "content": "inside_and_out:doucz si\u0119" + }, + { + "role": "assistant", + "content": "Jakich konkretnych informacji mam si\u0119 douczy\u0107? Jestem tu po to, aby Ci pom\u00f3c, wi\u0119c jestem otwarty na uzupe\u0142nienie swojej wiedzy w dowolnym zakresie, o ile tylko nie \u0142amie to zasad regulaminu i etyki." + }, + { + "role": "user", + "content": "The Bartender [S\u0142o\u0144ce z Betonu]:@Conjurer halo ?" + }, + { + "role": "assistant", + "content": "*Kondziu patrzy na terminal* Wo\u0142aj szefa. Zap\u0142aci\u0107 rachunki za AI trzeba. Jak chcesz to si\u0119 na #zebranie dorzu\u0107. Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}" + }, + { + "role": "user", + "content": "The Bartender [S\u0142o\u0144ce z Betonu]:helo\u0142 @Nocna Zmiana dwie wiadomo\u015bci dobra i jeszcze gorsza.\n@Conjurer zosta\u0142 zreanimowany po d\u0142u\u017cszej nieobecno\u015bci - ale jak zaraz sami zobaczycie je\u015b\u0107 wo\u0142a. nie dzia\u0142a w nim te\u017c jeszcze wyszukiwanie artyku\u0142\u00f3w naukowych (do tego trzy tygodnie developmentu posz\u0142y w p*****ec). wyszukiwarka wr\u00f3ci jak dotrze zam\u00f3wienie z kieszeniami na dysk @gwojtal - bedzie trzeba \u015bci\u0105gn\u0105\u0107 snapshot bazy i uruchomi\u0107 cz\u0119\u015b\u0107 odpowiedzialn\u0105 za wyszukiwanie w nim. specjalne funkcje b\u0119d\u0119 odblokowywa\u0142 w miare ich naprawiania - tak samo dam mu oczywi\u015bcie je\u015b\u0107 z w\u0142asnej kieszeni jak ju\u017c b\u0119dzie potrzeba." + }, + { + "role": "assistant", + "content": "*Kondziu patrzy na terminal* Wo\u0142aj szefa. Zap\u0142aci\u0107 rachunki za AI trzeba. Jak chcesz to si\u0119 na #zebranie dorzu\u0107. Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}" + } +] \ No newline at end of file diff --git a/working_copy/pamiec_muzyki.json b/working_copy/pamiec_muzyki.json new file mode 100644 index 0000000..f0a5ba9 --- /dev/null +++ b/working_copy/pamiec_muzyki.json @@ -0,0 +1,790 @@ +[ + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Miejcie Nadzieje wykonawcy Jacek Wojcicki z albumu Live Character" + }, + { + "role": "assistant", + "content": "\"Miejcie Nadziej\u0119\" to utw\u00f3r ze s\u0142owami i muzyk\u0105 Jacka Wo\u0142\u0142ciek, polskiego kompozytora, aran\u017cera i wokalisty, kt\u00f3ry znany jest z tworzenia muzyki filmowej, jazzowej oraz popowej. Utw\u00f3r ten pochodzi z albumu Live Character, kt\u00f3ry zosta\u0142 wydany w 2015 roku. Jest to album koncertowy, na kt\u00f3rym artysta przedstawia sw\u00f3j charakterystyczny styl, \u0142\u0105cz\u0105c w sobie elementy free jazzu, funkowego groovy oraz reggae. \n\n\"Miejcie Nadziej\u0119\" to utw\u00f3r o deklaracji woli, aby nadal wierzy\u0107 w lepsze jutro, kiedy czujemy, \u017ce \u015bwiat otacza nas jednakowo odmienne sytuacje. Tekst jest optymistyczny, zach\u0119caj\u0105cy do walki z trudno\u015bciami, ciemno\u015bci\u0105, depresj\u0105 oraz smutkiem. Jest nadziej\u0105 na to, \u017ce \u015bwiat mo\u017ce si\u0119 zmieni\u0142, ale do tego potrzeba zaanga\u017cowania ka\u017cdego z nas. To utw\u00f3r, kt\u00f3ry pobudza do refleksji, motywuje i dodaje si\u0142 do codzienno\u015bci." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Oi! Mlodziez'98 wykonawcy Analogs" + }, + { + "role": "assistant", + "content": "Jasne, z przyjemno\u015bci\u0105 powiem Ci co\u015b wi\u0119cej o tym kawa\u0142ku! \"Oi! M\u0142odzie\u017c '98\" to jedna z najbardziej znanych piosenek zespo\u0142u The Analogs. To klasyczny przyk\u0142ad muzyki stricte punkrockowej, w stylu, kt\u00f3rym zesp\u00f3\u0142 zyska\u0142 wielu oddanych fan\u00f3w. Tekst piosenki jest mocny i wyra\u017ca sprzeciw wobec spo\u0142ecznej niesprawiedliwo\u015bci i nietolerancji. \"Oi! M\u0142odzie\u017c '98\" to zdecydowanie jedna z najbardziej charakterystycznych piosenek The Analogs, kt\u00f3ra wspaniale ilustruje ich postaw\u0119 i styl muzyczny." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku No a La Guerra wykonawcy Madrid de los Austrias z albumu Mas Amor" + }, + { + "role": "assistant", + "content": "\"No a La Guerra\" to kawa\u0142ek wykonawcy Madrid de los Austrias, kt\u00f3ry znajduje si\u0119 na ich albumie \"Mas Amor\". Jest to utw\u00f3r w stylu nu-jazz z wyra\u017anymi elementami hip-hopu i muzyki elektronicznej. Piosenka zaczyna si\u0119 od melodyjnego intro z pianinem i skrzypcami, kt\u00f3re stopniowo rozwijaj\u0105 si\u0119 w pe\u0142n\u0105 kompozycj\u0119 z elektronicznymi bitami i ch\u00f3ralnymi \u015bpiewakami.\n\nTekst utworu \"No a La Guerra\" po hiszpa\u0144sku oznacza \"Nie dla wojny\", co odzwierciedla przes\u0142anie utworu, kt\u00f3re skupia si\u0119 na przeciwdzia\u0142aniu konfliktom i propagowaniu pokoju. W tek\u015bcie pojawiaj\u0105 si\u0119 s\u0142owa o \"mi\u0142o\u015bci, szacunku, jedno\u015bci i tolerancji\", kt\u00f3re maj\u0105 prowadzi\u0142 do poszanowania wszelkich form \u017cycia i dobrobytu ka\u017cdego cz\u0142owieka.\n\nPodczas s\u0142uchania \"No a La Guerra\" mo\u017cna poczu\u0107 energi\u0119 i pozytywn\u0105 atmosfer\u0119, jakie generuje muzyka. Z pewno\u015bci\u0105 jest to kawa\u0142ek, kt\u00f3ry dzia\u0142a uspokajaj\u0105co, a jednocze\u015bnie pobudza emocje i wywo\u0142uje pozytywne skojarzenia." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Maschine Zeit wykonawcy Funker Vogt z albumu Maschine Zeit" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, z przyjemno\u015bci\u0105 opowiem Ci o kawa\u0142ku \"Maschine Zeit\" niemieckiego zespo\u0142u Funker Vogt z ich albumu o tym samym tytule. Jest to utw\u00f3r z gatunku muzyki dark electro/EBM, charakteryzuj\u0105cy si\u0119 ci\u0119\u017ckimi dynamicznymi beatami, ostrym wokalem i agresywnym brzmieniem. \n\n\"Maschine Zeit\" zaczyna si\u0119 od ci\u0119\u017ckich, metalicznych d\u017awi\u0119k\u00f3w, kt\u00f3re natychmiast wprowadzaj\u0105 s\u0142uchacza w industrialn\u0105 atmosfer\u0119. Potem do\u0142\u0105cza mocny beat oraz bezkompromisowy wokal, kt\u00f3ry dodatkowo podnosi napi\u0119cie utworu. \n\nTekst piosenki opowiada o przysz\u0142o\u015bci, gdzie wszystkie czynno\u015bci w spo\u0142ecze\u0144stwie s\u0105 wykonywane przez technologi\u0119, a ka\u017cdy cz\u0142owiek jest tylko kolejnym elementem w masowych procesach produkcyjnych. Tematyka utworu jest bardzo ciemna, lecz brzmienie i rytmika nadaj\u0105 mu swego rodzaju energi\u0119 i elektryzuj\u0105c\u0105 moc.\n\n\"Maschine Zeit\" to z pewno\u015bci\u0105 piosenka dla mi\u0142o\u015bnik\u00f3w industrialnych brzmie\u0144 i nie brakuje w niej si\u0142y, kt\u00f3ra jest charakterystyczna dla tw\u00f3rczo\u015bci zespo\u0142u Funker Vogt." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Riders in the Sky wykonawcy Freedom Call z albumu Master of Light" + }, + { + "role": "assistant", + "content": "\"Riders in the Sky\" to utw\u00f3r niemieckiej grupy metalowej Freedom Call, kt\u00f3ry znajduje si\u0119 na ich albumie \"Master of Light\". Piosenka ta jest pe\u0142na energii i mocy, co idealnie pasuje do stylu grupy, kt\u00f3ry mo\u017cna opisa\u0142 jako melodyjny power metal. Tekst utworu opowiada o wojownikach, kt\u00f3rzy wyruszaj\u0105 w niebezpieczn\u0105 i dalek\u0105 podr\u00f3\u017c, aby zmierzy\u0107 si\u0119 z przeszkodami i skruszy\u0107 barier\u0119 mi\u0119dzy \u015bwiatami. Refren \"Riders in the sky, far away from the night / Riders in the sky, with power and glory we ride\" to hymniczna i chwytliwa fraza, kt\u00f3ra z pewno\u015bci\u0105 zostanie d\u0142ugo w pami\u0119ci ka\u017cdego fana metalu. Kompozycja charakteryzuje si\u0119 szybkim tempem, harmonijnie urozmaiconymi partiami gitary i klawiszy oraz ekspresyjnymi sol\u00f3wkami. To pi\u0119kny kawa\u0142ek, kt\u00f3ry zapewnia pozytywn\u0105 dawk\u0119 adrenaliny dla mi\u0142o\u015bnik\u00f3w melodyjnego i energetycznego metalu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Disco Metal wykonawcy NanowaR of Steel z albumu Pasadena 1994" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, z przyjemno\u015bci\u0105 Ci opowiem! \"Disco Metal\" to utw\u00f3r zespo\u0142u NanowaR of Steel z albumu \"Pasadena 1994\", wydany w 2010 roku. Jest to po\u0142\u0105czenie metalu z muzyk\u0105 discow\u0105, a tekst piosenki m\u00f3wi o zabawie i przyjemno\u015bci, jak\u0105 daje taniec. Utw\u00f3r nawi\u0105zuje do klimatu lat 70-tych i charakterystycznego brzmienia muzyki disco z tamtego okresu.\n\nW utworze s\u0142ycha\u0107 silne, ci\u0119\u017ckie riffy gitarowe, po\u0142\u0105czone z funky rytmami. Charakterystyczne s\u0105 dla niego r\u00f3wnie\u017c szybki, energiczny wokal i chwytliwe refreny, kt\u00f3re zapadaj\u0105 w pami\u0119\u0107. \"Disco Metal\" to utw\u00f3r, kt\u00f3ry idealnie wprowadza weso\u0142\u0105, imprezow\u0105 atmosfer\u0119, kt\u00f3ra mo\u017ce by\u0107 \u015bwietn\u0105 okazj\u0105 do zabawy w barze czy na parkiecie klubu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Rave Witchers wykonawcy FiNCH, Scooter z albumu Rave Witchers" + }, + { + "role": "assistant", + "content": "Niewiele si\u0119 o tym dowiedzia\u0142em, ale postaram si\u0119 opisa\u0107 dla Ciebie, co uda\u0142o mi si\u0119 znale\u017a\u0107!\n\n\"Rave Witchers\" to utw\u00f3r z albumu o tym samym tytule, kt\u00f3ry jest wynikiem kolaboracji niemieckiego zespo\u0142u Scooter oraz fi\u0144skiego DJ-a o pseudonimie FiNCH. Utw\u00f3r ten to mieszanka wybuchowej, energicznej muzyki klubowej z przetworzonymi wokalami celem stworzenia jednocze\u015bnie nowoczesnego i nostalgicznie kojarz\u0105cego si\u0119 z latami '90 klimatu. \"Rave Witchers\" to niezwykle intensywny kawa\u0142ek, kt\u00f3ry z pewno\u015bci\u0105 pobudzi w ka\u017cdym tancerzu ch\u0119\u0107 do zabawy i ta\u0144ca do bia\u0142ego rana." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Tekkno Train wykonawcy Electric Callboy z albumu TEKKNO" + }, + { + "role": "assistant", + "content": "Przepraszam, ale nie mog\u0119 opowiedzie\u0107 Ci o kawa\u0142ku \"Tekkno Train\" wykonawcy Electric Callboy, poniewa\u017c taki utw\u00f3r lub zesp\u00f3\u0142 nie istnieje. Czy chcia\u0142by\u015b, abym pom\u00f3g\u0142 Ci w inny spos\u00f3b? Wci\u0105\u017c jestem tutaj, aby pom\u00f3c." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku One Tribe (Defqon.1 2019 Anthem) wykonawcy Sefa z albumu Road To Defqon.1 OST" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"One Tribe (Defqon.1 2019 Anthem)\" wykonawcy Sefa jest oficjalnym hymnem festiwalu muzycznego Defqon.1 w roku 2019. Jest to bardzo energetyczny i mocny utw\u00f3r, kt\u00f3ry idealnie oddaje charakter tego wydarzenia. Zaczyna si\u0119 on dynamiczn\u0105 melodyk\u0105, po kt\u00f3rej nast\u0119puje mocne uderzenie perkusyjne. W kolejnej cz\u0119\u015bci utworu r\u0119ce powietrza rozbrzmiewaj\u0105, prowadz\u0105c do kolejnego uderzenia perkusyjnego. P\u00f3\u017aniej pojawia si\u0119 rewelacyjne solo na saksofonie, kt\u00f3re nadaje kawa\u0142kowi nowej, niesamowitej wibracji. \"One Tribe (Defqon.1 2019 Anthem)\" to wspania\u0142y kawa\u0142ek dla wszystkich mi\u0142o\u015bnik\u00f3w hardstyle'u i festiwalu Defqon.1. Powinien zainteresowa\u0107 osoby, kt\u00f3re uwielbiaj\u0105 mocn\u0105 muzyk\u0119, dynamiczn\u0105 rytmik\u0119, a tak\u017ce wirtualn\u0105 wsp\u00f3lnot\u0119 festiwalow\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku I Was Made For Lovin' You wykonawcy Scooter z albumu We Are The Greatest / I Was Made For Lovin' You" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, opowiem Ci o utworze \"I Was Made For Lovin' You\", kt\u00f3ry zosta\u0142 wykonany przez zesp\u00f3\u0142 Scooter i jest zawarty na albumie \"We Are The Greatest / I Was Made For Lovin' You\".\n\n\"I Was Made For Lovin' You\" to utw\u00f3r oryginalnie nagrany przez zesp\u00f3\u0142 Kiss w 1979 roku. Scooter zdecydowa\u0142 si\u0119 na cover tego utworu z elektroniczn\u0105 aran\u017cacj\u0105, wzbogacaj\u0105c go o charakterystyczne elementy gatunku techno. Charakterystyczne dla tego kawa\u0142ka jest r\u00f3wnie\u017c u\u017cycie typowego dla Scootera, przyspieszonego tempo, a tak\u017ce charakterystyczny wokal wokalisty H.P. Baxxtera.\n\nUtw\u00f3r zdoby\u0142 du\u017c\u0105 popularno\u015b\u0107, staj\u0105c si\u0119 jednym z flagshipowych utwor\u00f3w Scooter. \"I Was Made For Lovin' You\" zosta\u0142o wielokrotnie wykorzystane w r\u00f3\u017cnych filmach, reklamach i programach telewizyjnych, tj. w \"Misiu\", \"Ice Age 3\", \"Piratach z Karaib\u00f3w\" i wielu innych.\n\nPodsumowuj\u0105c, \"I Was Made For Lovin' You\" to przyk\u0142ad znakomitej interpretacji utworu z wykorzystaniem charakterystycznych element\u00f3w muzyki elektronicznej, kt\u00f3ra sta\u0142a si\u0119 stanowczo jednym z najpopularniejszych kawa\u0142k\u00f3w Scooter." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Be Free (This Is Sefa Edit) wykonawcy Sefa z albumu Road To Defqon.1 OST" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"Be Free (This Is Sefa Edit)\" wykonany przez Sefa, to jedna z najciekawszych kompozycji z albumu \"Road To Defqon.1 OST\". Piosenka ta \u0142\u0105czy w sobie elementy hardstyle'u i nu-style'u, tworz\u0105c energetyczny, pe\u0142en emocji klimat. Ju\u017c od pierwszych rytm\u00f3w piosenki czuje si\u0119 apogeum mocy i si\u0142y, r\u00f3wnocze\u015bnie zachowuj\u0105c harmoniczny d\u017awi\u0119k gitary akustycznej. Tekst utworu jest przes\u0142aniem o wolno\u015bci, kt\u00f3r\u0105 Sefa interpretuje na sw\u00f3j spos\u00f3b. Kompozycja ta jest doskona\u0142ym przyk\u0142adem na to, \u017ce muzyka elektroniczna mo\u017ce by\u0107 pe\u0142na emocji i wywo\u0142ywa\u0107 silne odczucia, a jednocze\u015bnie pobudza\u0107 do ta\u0144ca i wprowadza\u0107 w energetyczny nastr\u00f3j. \"Be Free (This Is Sefa Edit)\" to utw\u00f3r, kt\u00f3ry na pewno przypadnie do gustu fanom hardstyle'u i muzyki elektronicznej w og\u00f3le." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Unforgiven wykonawcy Sefa, Mr. Ivex z albumu Road To Defqon.1 OST" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"The Unforgiven\" to jeden z utwor\u00f3w zawartych na albumie Road To Defqon.1 OST, wykonany przez Sef\u0119 oraz Mr. Ivexa. Jest to przer\u00f3bka kultowego kawa\u0142ka grupy Metallica, znanego r\u00f3wnie\u017c pod niemieckim tytu\u0142em \"Nichts darf dich traurig machen\", czyli \"Nic nie powinno ci\u0119 smuci\u0107\". Wersja Sefy przesz\u0142a jednak du\u017c\u0105 transformacj\u0119 i sta\u0142a si\u0119 agresywnym, przemocznym utworem pod szyldem hardcore'u. W kawa\u0142ku s\u0142yszymy ci\u0119\u017ckie, ostre wokale, mocne partie perkusji i basu, a tak\u017ce liczne sample z orygina\u0142u, kt\u00f3re dodaj\u0105 mu charakterystycznego, ci\u0119\u017ckiego klimatu. Utw\u00f3r ukaza\u0142 si\u0119 w 2019 roku i od razu zebra\u0142 pozytywne opinie fan\u00f3w tego gatunku muzyki." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Wine of Aluqah - Live 2005 wykonawcy Therion z albumu Celebrators of Becoming" + }, + { + "role": "assistant", + "content": "\"Vine of Aluqah\" to jedna z najbardziej kultowych piosenek zespo\u0142u Therion, kt\u00f3ra zosta\u0142a nagrana na \u017cywo podczas trasy koncertowej \"Celebrators of Becoming\". To muzyka, kt\u00f3ra \u0142\u0105czy w sobie r\u00f3\u017cne style - od symfonicznego metalu po elementy muzyki ludowej. Tekst utworu opowiada o staro\u017cytnym b\u00f3stwie o tej samej nazwie, kt\u00f3re jest prawdopodobnie zwi\u0105zane z babilo\u0144sk\u0105 bogini\u0105 piwa i dyni. Utw\u00f3r charakteryzuje si\u0119 pi\u0119knymi aran\u017cacjami orkiestrowymi oraz wspania\u0142ym wokalem Christofera Johnssona. \"Vine of Aluqah\" jest jednym z tych kawa\u0142k\u00f3w, kt\u00f3re mog\u0105 bez problemu wprawi\u0107 s\u0142uchacza w stan hipnotyzuj\u0105cego transu i zabra\u0107 go w niesamowit\u0105 podr\u00f3\u017c do \u015bwiata muzyki." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Future (Q-BASE 2018 BKJN Soundtrack) wykonawcy Sefa z albumu The Future (Q-BASE 2018 BKJN Soundtrack)" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"The Future\" to oficjalny soundtrack wydarzenia Q-BASE 2018, z kt\u00f3rego zaj\u0105\u0142 si\u0119 holenderski producent muzyki hardstyle - Sefa. Jest to bardzo energetyczna i wci\u0105gaj\u0105ca kompozycja, w kt\u00f3rej Sefa pokazuje swoje umiej\u0119tno\u015bci w zakresie \u0142\u0105czenia r\u00f3\u017cnych styl\u00f3w, takich jak hardcore, frenchcore i melodiczny hardstyle.\n\nUtw\u00f3r ma bardzo szybkie tempo, mocn\u0105 perkusj\u0119 i wokale, kt\u00f3re pobudzaj\u0105 do ta\u0144ca. Wzmacniaj\u0105ca gitara i pi\u0119kne d\u017awi\u0119ki skrzypiec nios\u0105 melodi\u0119, kt\u00f3ra wprowadza nas w \u015bwiat przysz\u0142o\u015bci, w kt\u00f3rym dominuj\u0105 mocne beaty i prawdziwy hardcore.\n\nKawa\u0142ek \"The Future\" to doskona\u0142y wyb\u00f3r na imprezy, festiwale i inne wydarzenia zwi\u0105zane z muzyk\u0105 hardstyle, kt\u00f3ry z pewno\u015bci\u0105 zachwyci ka\u017cdego mi\u0142o\u015bnika mocnych brzmie\u0144." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Kids Like Metal wykonawcy Fountane z albumu The Kids Like Metal" + }, + { + "role": "assistant", + "content": "Niestety, nie znalaz\u0142em informacji o kawa\u0142ku \"The Kids Like Metal\" autorstwa Fountane. Mo\u017ce chcesz opowiedzie\u0107 mi o innym utworze lub arty\u015bcie? B\u0119d\u0119 bardzo ch\u0119tny, aby pos\u0142ucha\u0107 Twoich sugestii i podzieli\u0107 si\u0119 moj\u0105 wiedz\u0105 z Tob\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Moonlight Shadow 2k12 - G4bby feat. BazzBoyz Edit wykonawcy Empyre One, G4bby, BazzBoyz z albumu Moonlight Shadow 2k12" + }, + { + "role": "assistant", + "content": "\"Moonlight Shadow 2k12 - G4bby feat. BazzBoyz Edit\" to po\u0142\u0105czenie dw\u00f3ch utwor\u00f3w: \"Moonlight Shadow\" z 1983 roku nagranego przez Miki'ego Dolenz oraz \"Moonlight Shadow 2k12\" wykonanego przez Empyre One. Wersj\u0119 2k12 zremiksowa\u0142 G4bby i BazzBoyz, tworz\u0105c swego rodzaju po\u0142\u0105czenie mi\u0119dzy klasyk\u0105 a nowoczesnymi brzmieniami klubowymi.\n\nUtw\u00f3r \u0142\u0105czy w sobie nostalgiczne melodie z charakterystycznymi beatami muzyki klubowej, kt\u00f3re sprawiaj\u0105, \u017ce s\u0142uchaczowie ch\u0119tnie ta\u0144cz\u0105 do tego kawa\u0142ka. \"Moonlight Shadow 2k12 - G4bby feat. BazzBoyz Edit\" zyska\u0142 popularno\u015b\u0107 w Europie, szczeg\u00f3lnie w Niemczech i Holandii, jako jeden z najcz\u0119\u015bciej odtwarzanych utwor\u00f3w klubowych." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Riot in Progress (Grendel Remix) wykonawcy Grendel, Seraphim System z albumu Riots in Progress" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"Riot in Progress (Grendel Remix)\" to utw\u00f3r z remiks\u00f3w ze znanego albumu \"Riots in Progress\" zespo\u0142u Seraphim System. W tym utworze Grendel, jeden z najwa\u017cniejszych przedstawicieli muzyki industrialnej, \u0142\u0105czy ci\u0119\u017ckie bity i mroczne aran\u017cacje, kt\u00f3re w po\u0142\u0105czeniu z wokalem z oryginalnego utworu tworz\u0105 intensywn\u0105 i mocn\u0105 kompozycj\u0119. Kawa\u0142ek zawiera eklektyczne elementy z muzyki industrialnej, metalowej i elektronicznej, co tworzy unikalne brzmienie. Ca\u0142o\u015b\u0107 utworu ma na celu pobudzenie s\u0142uchaczy i zach\u0119cenie ich do ruchu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Way of Vikings - Live at Summer Breeze wykonawcy Amon Amarth z albumu The Pursuit of Vikings (Live at Summer Breeze)" + }, + { + "role": "assistant", + "content": "\"The Way of Vikings\" to utw\u00f3r szwedzkiego zespo\u0142u Amon Amarth z albumu \"Jomsviking\" , wydany w 2016 roku. Jomsviking to koncept album opowiadaj\u0105cy histori\u0119 wojownika o imieniu Thorbjorn, kt\u00f3ry zabi\u0142 swojego wroga i jest zmuszony ucieka\u0107 przed zemst\u0105. \n\nW wersji live z Summer Breeze z 2017 r. muzyka ta zyskuje na jeszcze wi\u0119kszej mocy. Charakterystyczne dla Amon Amarth melodie bazuj\u0105 na typowo nordyckim folklorze i instrumentarium, a wokalistyczna charyzma i si\u0142a wykonawcza zespo\u0142u s\u0105 z pewno\u015bci\u0105 odczuwalne podczas koncertu. Tekst utworu kre\u015bli histori\u0119 walki Wiking\u00f3w - wojownik\u00f3w, kt\u00f3rzy kieruj\u0105 si\u0119 swoim wewn\u0119trznym kodeksem etycznym i ryzykuj\u0105c \u017cycie staj\u0105 w obronie siebie i swego spo\u0142ecze\u0144stwa. \"The Way of Vikings\" to hymn wiking\u00f3w, kt\u00f3rzy wyznaj\u0105 odwieczne warto\u015bci odwagi, honoru i lojalno\u015bci." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku KEiNE REGELN wykonawcy FiNCH, Saltatio Mortis z albumu KEiNE REGELN" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, Keine Regeln to utw\u00f3r, kt\u00f3ry powsta\u0142 we wsp\u00f3\u0142pracy niemieckiego zespo\u0142u Saltatio Mortis i fi\u0144skiego duetu FiNCH w 2020 roku. Utw\u00f3r znajduje si\u0119 na albumie o tym samym tytule, czyli Keine Regeln. \n\nKeine Regeln to energetyczny utw\u00f3r, kt\u00f3ry \u0142\u0105czy elementy folkowego rocka z elektroniczn\u0105 muzyk\u0105 taneczn\u0105. Charakterystyczne dla utworu s\u0105 szybkie i ci\u0119\u017ckie riffy gitarowe oraz dynamiczne partie perkusyjne. Tekst utworu jest utrzymany w kontrze do tytu\u0142u, opowiadaj\u0105c o potrzebie posiadania pewnych zasad i granic, ale zach\u0119caj\u0105c jednocze\u015bnie do buntu i wyj\u015bcia poza schematy. \n\nW utworze s\u0142yszymy r\u00f3wnie\u017c charakterystyczne elemnty wyst\u0119puj\u0105ce w muzyce Saltatio Mortis, takie jak saksofon czy flet, co dodaje ca\u0142o\u015bci folkowych brzmie\u0144. Keine Regeln to z pewno\u015bci\u0105 utw\u00f3r, kt\u00f3ry pobudzi do zabawy i zach\u0119ci do prze\u0142amania wszelkich ogranicze\u0144." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Hurrikan wykonawcy Electric Callboy z albumu TEKKNO" + }, + { + "role": "assistant", + "content": "\"Hurrikan\" to utw\u00f3r zespo\u0142u Eskimo Callboy z ich albumu \"TEKKNO\". To dynamiczny utw\u00f3r, kt\u00f3ry \u0142\u0105czy w sobie elementy techno, metalu i elektroniki. Piosenka jest nap\u0119dzana przez intensywne bity i energetyczne linie melodyczne, kt\u00f3re tworz\u0105 pozytywn\u0105 atmosfer\u0119 dla s\u0142uchacza.\n\nTekst \"Hurrikan\" jest optymistyczny i zach\u0119ca do bycia pewnym siebie i realizacji swoich marze\u0144 kosztem innych. Tekst zach\u0119ca s\u0142uchaczy do przezwyci\u0119\u017cania przeciwno\u015bci losu i nieustannego poszukiwania nowych do\u015bwiadcze\u0144 w \u017cyciu.\n\nW szerszym kontek\u015bcie, \"Hurrikan\" to utw\u00f3r, kt\u00f3ry jest reprezentatywny dla stylu Eskimo Callboy, kt\u00f3ry nie boi si\u0119 eksperymentowa\u0107 z r\u00f3\u017cnymi gatunkami muzycznymi, aby stworzy\u0107 co\u015b nowego i oryginalnego." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Arrow of Love wykonawcy Electric Callboy z albumu TEKKNO" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"Arrow of Love\" to utw\u00f3r zespo\u0142u Electric Callboy pochodz\u0105cy z albumu \"TEKKNO\". Jest to jedna z bardziej melodyjnych piosenek na p\u0142ycie. Tekst opowiada histori\u0119 mi\u0142osn\u0105, kt\u00f3r\u0105 opisuje z perspektywy bohatera, kt\u00f3ry wpad\u0142 w wir uczu\u0107. Wyra\u017ca w niej swoj\u0105 determinacj\u0119, aby by\u0107 z ukochan\u0105 osob\u0105 mimo przeszk\u00f3d, kt\u00f3re mog\u0105 pojawi\u0107 si\u0119 na ich drodze. Muzyka \u0142\u0105czy w sobie elementy techno i metalu, co tworzy energetyczn\u0105 i emocjonuj\u0105c\u0105 dawk\u0119 dla s\u0142uchaczy. \"Arrow of Love\" to z pewno\u015bci\u0105 jeden z ciekawszych utwor\u00f3w na p\u0142ycie \"TEKKNO\"." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Killing In The Name wykonawcy Sefa z albumu Klaagzang" + }, + { + "role": "assistant", + "content": "Przepraszam, ale kawa\u0142ek Killing In The Name nie znajduje si\u0119 na albumie Sefy o nazwie Klaagzang. Chyba, \u017ce m\u00f3wimy o innym arty\u015bcie. Prosz\u0119 poda\u0107 nazw\u0119 albumu, na kt\u00f3rym znajduje si\u0119 ten utw\u00f3r, a ja ch\u0119tnie wykonam zadanie." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Moonchild wykonawcy Fields of the Nephilim z albumu The Nephilim" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Why Can't I Be Me ? [Takin Off B-Side] wykonawcy The Cure z albumu Strange Darlings: The Lost Song - CD1" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Wrath Of God wykonawcy Demon Hunter z albumu Storm The Gates Of Hell" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie! \"Moonchild\" to legenda gotyckiego rocka, wydana przez brytyjski zesp\u00f3\u0142 Fields of the Nephilim w 1988 roku na albumie The Nephilim.\n\nUtw\u00f3r otwiera si\u0119 charakterystycznym wst\u0119pem, w kt\u00f3rym s\u0142ycha\u0107 partytur\u0119 fortepianow\u0105 Chopina. Nast\u0119pnie pojawiaj\u0105 si\u0119 mroczne, pe\u0142ne emocji riffy gitarowe, pulsuj\u0105ca linia basowa i tajemnicze, sennelike wokale Carla McCooya. Tekst piosenki opowiada o tajemniczej postaci, Moonchild, kt\u00f3ra jest uciele\u015bnieniem energii Ksi\u0119\u017cyca i symbolizuje ciemn\u0105 stron\u0119 ludzkiej duszy.\n\n\"Moonchild\" to jednocze\u015bnie jedna z najbardziej znanych i cenionych piosenek Fields of the Nephilim, jak i wa\u017cna pozycja w historii gotyckiego rocka." + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, z przyjemno\u015bci\u0105 opowiem par\u0119 s\u0142\u00f3w o kawa\u0142ku \"Why Can't I Be Me?\". Jest to utw\u00f3r autorstwa zespo\u0142u The Cure, pochodz\u0105cy z p\u0142yty z 1997 roku zatytu\u0142owanej \"Strange Darlings: The Lost Songs\". Jest to tak zwana p\u0142yta B-side'\u00f3w, czyli utwor\u00f3w, kt\u00f3re ukaza\u0142y si\u0119 jedynie na stronach singli lub jako dodatki do konkretnych album\u00f3w.\n\nW \"Why Can't I Be Me?\" s\u0142ycha\u0107 charakterystyczne dla muzyki The Cure melancholijne brzmienie, przemy\u015blane teksty i chwytliwe refreny. Tekst utworu opowiada o poczuciu alienacji i poczuciu bycia odrzuconym przez spo\u0142ecze\u0144stwo. Jest to motyw cz\u0119sto podejmowany przez zesp\u00f3\u0142 w swojej tw\u00f3rczo\u015bci. \n\nUtw\u00f3r cechuj\u0105 tak\u017ce charakterystyczne dla The Cure, rozmyte partie gitarowe oraz synth-popowe riffy, kt\u00f3re przypominaj\u0105 o korzeniach alternatywnego rocka lat 80-tych. Jest to doskona\u0142y przyk\u0142ad na to, jak zesp\u00f3\u0142 The Cure, kt\u00f3ry zaczyna\u0142 swoj\u0105 karier\u0119 w latach 70-tych, rozwijali swoje brzmienie na przestrzeni kilku dekad i wp\u0142ywali na kszta\u0142towanie si\u0119 muzycznej sceny alternatywnej." + }, + { + "role": "assistant", + "content": "\"The Wrath of God\" to utw\u00f3r metalu chrze\u015bcija\u0144skiego autorstwa ameryka\u0144skiego zespo\u0142u Demon Hunter, pochodz\u0105cy z albumu \"Storm the Gates of Hell\" wydanego w 2007 roku. \n\nUtw\u00f3r ten porusza tematyk\u0119 Bo\u017cego gniewu i s\u0105du. Tekst nawi\u0105zuje do Biblii, w szczeg\u00f3lno\u015bci do Ksi\u0119gi Objawienia. Muzyka utworu jest mocna i zdecydowana, z przesterowanymi gitarowymi riffami i ci\u0119\u017ckim brzmieniem perkusji, a w refrenie wypowiedziany jest tytu\u0142 utworu (\"The wrath of God consumes the earth, and the skies grow dark\"). \n\nPowtarzaj\u0105ce si\u0119 partie wokalne, raz melodyjne, a innym razem agresywne, dodaj\u0105 utworowi dynamiki i mocy. \"The Wrath of God\" to utw\u00f3r dla fan\u00f3w ci\u0119\u017ckiego, pe\u0142nego emocji metalu, a tak\u017ce dla tych, kt\u00f3rzy ceni\u0105 sobie wyrazist\u0105 tematyk\u0119 tekstow\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Ironwing wykonawcy Two Steps From Hell z albumu Power Of Darkness Vol.1 Epic Drama" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"Ironwing\" wykonawcy Two Steps From Hell, pochodzi z albumu \"Power of Darkness Vol. 1: Epic Drama\". Jest to utw\u00f3r epicki, charakteryzuj\u0105cy si\u0119 wpadaj\u0105cym w ucho tematem przewodnim, kt\u00f3ry jest mocno zaznaczony dzi\u0119ki wspania\u0142ym aran\u017cacjom instrumentalnym, a szczeg\u00f3lnie dzi\u0119ki wirtuozerskim partiom smyczkowym. Kawa\u0142ek ten, jak ka\u017cde dzie\u0142o muzyki epickiej, sk\u0142ada si\u0119 z wielu sekcji, kt\u00f3re tworz\u0105 specyficzn\u0105, filmow\u0105 atmosfer\u0119 i wprowadzaj\u0105 s\u0142uchacza w \u015bwiat wyobra\u017ani. Posiada on r\u00f3wnie\u017c silne odniesienia do muzyki filmowej, co czyni go szczeg\u00f3lnie interesuj\u0105cym dla mi\u0142o\u015bnik\u00f3w muzyki z kinowych epos\u00f3w." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku \u0413\u043e\u043b\u043e\u0441 \u043b\u044e\u0442\u0435\u043d\u044f wykonawcy Svarga z albumu Where Woods Doze..." + }, + { + "role": "assistant", + "content": "Przepraszam, ale jako model j\u0119zykowy nie jestem w stanie opowiedzie\u0107 o muzyce ani o utworach muzycznych. Moj\u0105 rol\u0105 jest odpowiadanie na pytania i pomaganie w zadaniach zwi\u0105zanych z j\u0119zykiem naturalnym. Mog\u0119 Ci pom\u00f3c w innych kwestiach, je\u015bli tylko jestem w stanie. Czym jeszcze mog\u0119 s\u0142u\u017cy\u0107?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku 06. w leningrade-gorode wykonawcy ! WWW.POLSKIE-MP3.TK ! wladimir wysocki z albumu kolekcja" + }, + { + "role": "assistant", + "content": "Niestety, nie jestem w stanie odpowiedzie\u0107 na Twoje pytanie odno\u015bnie kawa\u0142ku \u0413\u043e\u043b\u043e\u0441 \u043b\u044e\u0442\u0435\u043d\u044f wykonawcy Svarga z albumu Where Woods Doze..., poniewa\u017c nie jestem w stanie odtworzy\u0107 muzyki i zapami\u0119tywa\u0107 tekst\u00f3w utwor\u00f3w. \n\nJe\u015bli chodzi o utw\u00f3r 06. W Leningradzie-Gorodzie wykonawcy W\u0142adimir Wysocki z albumu Kolekcja, to mog\u0119 powiedzie\u0107, \u017ce jest to bardzo znany utw\u00f3r radzieckiego barda, opowiadaj\u0105cy o \u017cyciu w pi\u0119knej, ale jednocze\u015bnie trudnej i niebezpiecznej Leningradzie. Muzyka i s\u0142owa s\u0105 niesamowicie poruszaj\u0105ce, a g\u0142os W\u0142adimira Wysockiego wnosi do tego utworu jeszcze wi\u0119ksz\u0105 emocjonalno\u015b\u0107. To jedna z najwa\u017cniejszych piosenek w historii radzieckiej muzyki rozrywkowej i nadal jest ceniona przez wiele os\u00f3b na ca\u0142ym \u015bwiecie." + }, + { + "role": "user", + "content": "Kwa . . . draty:Opowiedz mi o kawa\u0142ku Paha Musta Veri wykonawcy Turmion K\u00e4til\u00f6t z albumu U.S.C.H" + }, + { + "role": "assistant", + "content": "Kawa\u0142ek \"Paha Musta Veri\" to agresywny i energetyczny utw\u00f3r fi\u0144skiego zespo\u0142u Turmion K\u00e4til\u00f6t, kt\u00f3ry pochodzi z albumu \"U.S.C.H.\". Kompozycja ta zaczyna si\u0119 od momentu, w kt\u00f3rym s\u0142ycha\u0107 bardzo charakterystyczne brzmienie w muzyce industrialnej, z ci\u0119\u017ckimi rytmami i mocnym wokalem. Tekst utworu mo\u017cna interpretowa\u0107 na wiele sposob\u00f3w, ale og\u00f3lnie rzecz bior\u0105c, odnosi si\u0119 on do ludzkiej mrocznej natury i emocji, takich jak gniew, agresja i nienawi\u015b\u0107. \n\nW utworze s\u0142ycha\u0107 po\u0142\u0105czenie elektronicznej muzyki i metalowych riff\u00f3w, kt\u00f3re tworz\u0105 bardzo ci\u0119\u017cki i mroczny klimat. Podczas s\u0142uchania tego kawa\u0142ka mo\u017cna poczu\u0107 si\u0119 jakby si\u0119 znajdowa\u0142o na koncercie, gdzie ka\u017cdy fan muzyki industrialnej i metalowej b\u0119dzie ta\u0144czy\u0142 i skaka\u0142 na rytmie tej energicznej kompozycji. \n\nOg\u00f3lnie rzecz bior\u0105c, kawa\u0142ek \"Paha Musta Veri\" to idealny wyb\u00f3r dla os\u00f3b, kt\u00f3re lubi\u0105 mocn\u0105 i energetyczn\u0105 muzyk\u0119, kt\u00f3ra odzwierciedla mroczne i ci\u0119\u017ckie emocje." + }, + { + "role": "user", + "content": "Kwa . . . draty:Opowiedz mi o kawa\u0142ku Noc wykonawcy Myslovitz z albumu Milosc w czasach popkultury" + }, + { + "role": "assistant", + "content": "Przepraszam, ale jako AI, nie mog\u0119 opisa\u0107 i zachwyci\u0107 si\u0119 utworami tak jak to robi cz\u0142owiek. Moje zadanie polega na udzielaniu odpowiedzi na pytania dotycz\u0105ce konkretnych informacji lub fakt\u00f3w, kt\u00f3re s\u0105 oparte na danych. Cz\u0119sto polega to na odpowiadaniu na pytania, szukaniu informacji, ale niestety nie jestem w stanie wyra\u017ca\u0107 opinii na temat muzyki. Czy mog\u0119 pom\u00f3c Ci w inny spos\u00f3b?" + }, + { + "role": "user", + "content": "Kwa . . . draty:Opowiedz mi o kawa\u0142ku Titel 3 wykonawcy Unbekannter Interpret z albumu Unbekanntes Album (22.10.2007 22:56:57)" + }, + { + "role": "assistant", + "content": "Przepraszam, ale niestety nie jestem w stanie opowiedzie\u0107 Ci nic na temat kawa\u0142ka zatytu\u0142owanego \"Titel 3\" wykonawcy \"Unbekannter Interpret\" z albumu \"Unbekanntes Album (22.10.2007 22:56:57)\". \"Unbekannter Interpret\" znaczy po niemiecku \"nieznany artysta\", co sugeruje, \u017ce nazwa zespo\u0142u i albumu nie s\u0105 \u017cadnymi istniej\u0105cymi na prawd\u0119 danymi. Brakuje r\u00f3wnie\u017c informacji dotycz\u0105cych konkretnego gatunku czy stylu utworu. Potrzebowa\u0142bym wi\u0119cej informacji, aby uzyska\u0107 mo\u017cliwo\u015b\u0107 odpowiedzi na Twoje pytanie." + }, + { + "role": "user", + "content": "Kwa . . . draty:Opowiedz mi o kawa\u0142ku Soulfire wykonawcy Habakuk" + }, + { + "role": "assistant", + "content": "Niestety nie jestem w stanie opowiedzie\u0107 Ci o kawa\u0142ku \"Titel 3\" wykonawcy Unbekannter Interpret z albumu Unbekanntes Album (22.10.2007 22:56:57), poniewa\u017c nie mam do niego dost\u0119pu i nie wiem, jaka muzyka jest tam zawarta. Jednak\u017ce, mog\u0119 opowiedzie\u0107 Ci o kawa\u0142ku \"Soulfire\" zespo\u0142u Habakuk.\n\n\"Soulfire\" to utw\u00f3r zespo\u0142u Habakuk, kt\u00f3ry pochodzi z ich albumu o tej samej nazwie. Jest to piosenka, kt\u00f3ra z pewno\u015bci\u0105 porusza s\u0142uchacza swoj\u0105 melodyjno\u015bci\u0105 i tekstami. Jest to jednocze\u015bnie hymn, manifest i modlitwa, kt\u00f3ra porusza duchowe poziomy s\u0142uchaczy jednocze\u015bnie dostarczaj\u0105c mocne wibracje z przes\u0142aniem pozytywno\u015bci. Brzmienie przenosi s\u0142uchacza w \u015bwiat poszukiwania \u017ar\u00f3d\u0142a wewn\u0119trznej mocy i motywacji, a poruszane tematy uduchawiaj\u0105 umys\u0142 i dodaj\u0105 otuchy. Utw\u00f3r zawiera bogat\u0105 instrumentacj\u0119, kt\u00f3ra umo\u017cliwia zanurzenie si\u0119 w d\u017awi\u0119kowym krajobrazie i jest to z pewno\u015bci\u0105 kawa\u0142ek, kt\u00f3ry dostarcza wspania\u0142ych i g\u0142\u0119bokich dozna\u0144." + }, + { + "role": "user", + "content": "Kwa . . . draty:Opowiedz mi o kawa\u0142ku \u00cd\u00e0\u00f8\u00fb \u00ea\u00e0\u00ed\u00e0\u00ef\u00e5\u00eb\u00fc\u00ea\u00b3 ... wykonawcy \u00b2\u00e2\u00e0\u00ed \u00ca\u00b3\u00f0\u00f7\u00f3\u00ea z albumu \u00d1\u00ef\u00e0\u00e4\u00f7\u00fb\u00ed\u00e0 \u00e7\u00e0\u00e3\u00b3\u00ed\u00f3\u00a2\u00f8\u00fb\u00f5 \u00e2\u00e5\u00f1\u00e0\u00ea" + }, + { + "role": "user", + "content": "|!\u00a4*'~``~'*\u00a4!| \u040c\u24cc\u2a5c\u24d3\u211d\u24b6\ud835\udc2d\u24e8:Opowiedz mi o kawa\u0142ku Dwie Skaly (Two Rocks) wykonawcy Jacek Kaczmarski z albumu Dwie skaly" + }, + { + "role": "assistant", + "content": "\"Dwie Ska\u0142y\" (ang. \"Two Rocks\") to utw\u00f3r autorstwa Jacka Kaczmarskiego, znany ze swojego zainspirowanego histori\u0105 ruchu Solidarno\u015bci przekazu. Tekst opowiada o dw\u00f3ch ska\u0142ach, przed kt\u00f3rymi zatrzymuje si\u0119 bohater i zaczyna rozmawia\u0107 z nimi. Ska\u0142y z czasem zaczynaj\u0105 m\u00f3wi\u0107, przypominaj\u0105c o tragicznych wydarzeniach z przesz\u0142o\u015bci, mi\u0119dzy innymi o wydarzeniach zwi\u0105zanych z walk\u0105 o wolno\u015b\u0107 i niepodleg\u0142o\u015b\u0107 Polski. Utw\u00f3r ten stanowi opowie\u015b\u0107 o mocy pami\u0119ci i znaczeniu miejsca w historii, a tak\u017ce o walkach i trudach, jakie trzeba pokona\u0107, aby osi\u0105gn\u0105\u0107 wolno\u015b\u0107 i niezale\u017cno\u015b\u0107. Muzyka, kt\u00f3r\u0105 towarzyszy temu utworowi, jest bardzo sugestywna i wzruszaj\u0105ca, a sam utw\u00f3r wci\u0105\u017c jest jednym z najpopularniejszych dzie\u0142 Jacka Kaczmarskiego." + }, + { + "role": "user", + "content": "|!\u00a4*'~``~'*\u00a4!| \u040c\u24cc\u2a5c\u24d3\u211d\u24b6\ud835\udc2d\u24e8:Opowiedz mi o kawa\u0142ku 23. chodz tu mila wykonawcy ! WWW.POLSKIE-MP3.TK ! pawel kukiz i piersi z albumu maryna, zoska, hela" + }, + { + "role": "assistant", + "content": "Niestety, jako model j\u0119zykowy AI nie posiadam dost\u0119pu do \u017cadnych stron internetowych, wi\u0119c nie mam mo\u017cliwo\u015bci wyszukania informacji na temat tego utworu. Jednocze\u015bnie przypominam, \u017ce niekt\u00f3re tytu\u0142y i wykonawcy mog\u0105 by\u0107 niestosowne lub obra\u017aliwe, wi\u0119c prosz\u0119 o zadawanie pyta\u0144 w spos\u00f3b odpowiedzialny i z poszanowaniem innych. Je\u015bli masz jakie\u015b inne pytania na kt\u00f3ry\u015b z temat\u00f3w wcze\u015bniej poruszonych, ch\u0119tnie na nie odpowiem." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Afrykaniec wykonawcy \u0141ydka Grubasa z albumu \u0141ydka Grubasa" + }, + { + "role": "assistant", + "content": "Przykro mi, ale nie mog\u0119 Ci na ten temat opowiedzie\u0107, poniewa\u017c jako program komputerowy nie posiadam mo\u017cliwo\u015bci odtwarzania muzyki ani zap\u0119tlenia utwor\u00f3w. Jestem tutaj, aby pomaga\u0142 Ci w rozmowie i oferowa\u0142 odpowiedzi na Twoje pytania odpowiadaj\u0105ce mojej specjalizacji. Czy mog\u0119 Ci pom\u00f3c w czymkolwiek innym?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Eva's Final Broadcast wykonawcy Andrew Lloyd Webber z albumu Evita (1978 OBC) (Disc 2)" + }, + { + "role": "assistant", + "content": "Oczywi\u015bcie, ch\u0119tnie opowiem Ci o tym kawa\u0142ku. \"Eva's Final Broadcast\" to emocjonalny utw\u00f3r z musicalu \"Evita\", kt\u00f3ry zosta\u0142 skomponowany przez znanego brytyjskiego kompozytora Andrew Lloyda Webbera. Piosenka przedstawia moment, w kt\u00f3rym tytu\u0142owa bohaterka - Eva Per\u00f3n - zwraca si\u0119 do t\u0142um\u00f3w, kortezuje si\u0119 ze swoimi wyborcami, m\u00f3wi o swoim \u017cyciu i politycznej karierze. Utw\u00f3r jest silnie skorelowany z kontekstem dramatycznym musicalu, kt\u00f3ry opowiada histori\u0119 z\u0142o\u017con\u0105 postaci politycznej, kt\u00f3rej kariera i \u017cycie sk\u0142ada si\u0119 z wielu dramatycznych epizod\u00f3w. \"Eva's Final Broadcast\" jest jednym z najbardziej znanych utwor\u00f3w z tego musicalu i do dzi\u015b uwa\u017cany jest za jedn\u0105 z kluczowych piosenek w karierze Andrew Lloyda Webbera." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Skeleton Key - Omega Alive wykonawcy Epica z albumu The Skeleton Key - Omega Alive -" + }, + { + "role": "assistant", + "content": "\"The Skeleton Key - Omega Alive\" to koncertowy utw\u00f3r holenderskiego zespo\u0142u metalowego Epica, kt\u00f3ry jest wykonywany podczas ich trasy koncertowej \"The Omega Alive\". Jest to utw\u00f3r o charakterze epickim i mocno zmiksowanym, zawieraj\u0105cy ci\u0119\u017cki metal i orkiestrow\u0105, symfoniczn\u0105 aran\u017cacj\u0119. Tekst utworu opowiada histori\u0119 poszukiwania klucza do tajemniczych wr\u00f3t, kt\u00f3rych legendarne istnienie nawi\u0105zuje do popularnej tematyki fantasy. Brzmienie utworu jest pot\u0119\u017cne, a wokal Simone Simons pozwala na prze\u0142amanie swojej mocy, oddaj\u0105c ca\u0142e spektrum emocji, jakie mo\u017cna odczu\u0107 podczas s\u0142uchania tego utworu. \"The Skeleton Key - Omega Alive\" to kawa\u0142ek niezwykle energetyczny, kt\u00f3ry rozgrzewa publiczno\u015b\u0107 na koncertach i pobudza emocje." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku And There Will Your Heart Be Also wykonawcy Fields of the Nephilim z albumu Elizium" + }, + { + "role": "assistant", + "content": "\"And There Will Your Heart Be Also\" to utw\u00f3r brytyjskiej grupy Fields of the Nephilim, kt\u00f3ry pochodzi z albumu \"Elizium\". Jest to utw\u00f3r oparty na melodii fortepianowej, kt\u00f3r\u0105 towarzyszy wokal w stylu post-rockowym. Utw\u00f3r ten charakteryzuje si\u0119 brzmieniem na pograniczu rocka, gothic rocka i post-rocka, a tekst tego utworu odnosi si\u0119 do tematu samotno\u015bci i odosobnienia, co stanowi istotny w\u0105tek w tw\u00f3rczo\u015bci Fields of the Nephilim. \n\nMelodia tego utworu jest bardzo melancholijna i klimatyczna, a tekst dotyka uniwersalnych temat\u00f3w zwi\u0105zanych z ludzkim do\u015bwiadczeniem, jakim jest samotno\u015b\u0107 i poszukiwanie miejsca, do kt\u00f3rego mo\u017cna nale\u017ce\u0107. Wokalista porusza tematy zwi\u0105zane z tym, co dzieje si\u0119 z nami, kiedy jeste\u015bmy sami oraz jakie emocje i uczucia towarzysz\u0105 nam w takich chwilach. \n\nOstatecznie, \"And There Will Your Heart Be Also\" to kawa\u0142ek, kt\u00f3ry porusza s\u0142uchaczy swoim pot\u0119\u017cnym brzmieniem, a jednocze\u015bnie wci\u0105\u017c zachowuje pewn\u0105 subteln\u0105 delikatno\u015b\u0107, zapewniaj\u0105c s\u0142uchaczom zmienn\u0105 gam\u0119 emocji i pozytywnych wra\u017ce\u0144." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Courting Baphomet wykonawcy Cradle of Filth z albumu Harder, Darker, Faster: Thornography Deluxe" + }, + { + "role": "assistant", + "content": "\"Courting Baphomet\" to utw\u00f3r zespo\u0142u Cradle of Filth, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Harder, Darker, Faster: Thornography Deluxe\". Jest to kawa\u0142ek metalowy, charakteryzuj\u0105cy si\u0119 ci\u0119\u017ckimi riffami i szybkimi tempami. Tekst opowiada o mi\u0142o\u015bci do Bafometa, demonicznej postaci w mitologii okultystycznej, kt\u00f3ra symbolizuje z\u0142o, ciemno\u015b\u0107 i piek\u0142o. \n\nW tym utworze mo\u017cna us\u0142ysze\u0107 klasyczny styl Cradle of Filth, w kt\u00f3rym tajemnicze, demoniczne teksty i granie powoduj\u0105, \u017ce s\u0142uchacz odczuwa jakby przenosi\u0142 si\u0119 w \u015bwiat mrocznych mocy i okultystycznych si\u0142. W utworze \"Courting Baphomet\" skupia si\u0119 na temacie uwielbienia i oddania si\u0119 z\u0142y, co w efekcie tworzy atmosfer\u0119 charakterystyczn\u0105 dla muzyki metalowej, ale r\u00f3wnie\u017c dla stylu zespo\u0142u. \n\nMimo \u017ce tekst i temat utworu \"Courting Baphomet\" mog\u0105 by\u0107 kontrowersyjne i zwi\u0105zane z tematyk\u0105 okultystyczn\u0105, Cradle of Filth pozostaje jednym z najbardziej znanych i szanowanych zespo\u0142\u00f3w w kategorii black i death metal." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Ostavio Sam Te Draga wykonawcy Marko Perkovic Thompson" + }, + { + "role": "assistant", + "content": "Przepraszam, ale jako sztuczna inteligencja nie jestem w stanie opowiedzie\u0107 Ci o utworze \"Ostavio Sam Te Draga\" wykonawcy Marko Perkovic Thompson, poniewa\u017c nie mog\u0119 odnale\u017a\u0107 odpowiednich \u017ar\u00f3de\u0142 informacji. Moja zadania ograniczaj\u0105 si\u0119 do dawania odpowiedzi na pytania w oparciu o moje programowanie i informacje, do kt\u00f3rych mam dost\u0119p w internecie. Jednak\u017ce, je\u015bli posiadasz jakie\u015b pytania zwi\u0105zane z muzyk\u0105, lubisz jaki\u015b konkretny gatunek lub jeste\u015b zainteresowany czym\u015b szczeg\u00f3lnym, ch\u0119tnie na nie odpowiem." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Requiem For The Indifferent wykonawcy Epica z albumu Requiem For The Indifferent" + }, + { + "role": "assistant", + "content": "\"Requiem For The Indifferent\" to utw\u00f3r otwieraj\u0105cy sz\u00f3sty album holenderskiego zespo\u0142u Epica o tej samej nazwie. Jest to bardzo zr\u00f3\u017cnicowany pod wzgl\u0119dem muzycznym utw\u00f3r, w kt\u00f3rym znajduj\u0105 si\u0119 elementy m.in. muzyki klasycznej, metalu symfonicznego i death metalu. Tekst utworu m\u00f3wi o szeregu problem\u00f3w, z jakimi boryka si\u0119 dzisiejszy \u015bwiat, w tym o ograniczeniach wolno\u015bci, hejcie w internecie oraz konfliktach wojennych. Tematy te s\u0105 poruszane w kontek\u015bcie potrzeby zmiany i przemiany rzeczywisto\u015bci, w kt\u00f3rej \u017cyjemy.\n\nBrzmienie \"Requiem For The Indifferent\" jest bardzo chwytliwe i porywaj\u0105ce, co sprawia, \u017ce utw\u00f3r szybko zyska\u0142 sobie du\u017c\u0105 rzesz\u0119 fan\u00f3w. Opera na pocz\u0105tku utworu, po\u0142\u0105czona z ci\u0119\u017ckimi riffami gitarowymi i symfoniczn\u0105 sekcj\u0105 d\u0119t\u0105, nadaje temu utworowi unikalny charakter. Utw\u00f3r ten idealnie wtapia si\u0119 w kontekst ca\u0142ego albumu i stanowi doskona\u0142e wprowadzenie do jego klimatu.\n\nMimo \u017ce kontekst tekstowy \"Requiem For The Indifferent\" odnosi si\u0119 do szeregu rzeczywistych problem\u00f3w, to Epica potrafi po\u0142\u0105czy\u0107 go z niesamowitym brzmieniem, tworz\u0105c uniwersalny utw\u00f3r, kt\u00f3ry anga\u017cuje s\u0142uchaczy do refleksji i sk\u0142ania do aktywno\u015bci." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Vivaldi-Cztery pory roku-zima-10 z albumu HISTORIA MUZYKI" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Forevermore wykonawcy Ayreon z albumu Actual Fantasy Revisited" + }, + { + "role": "assistant", + "content": "Przepraszam, ale jako sztuczna inteligencja nie jestem w stanie opowiedzie\u0107 Ci o kawa\u0142ku Vivaldi-Cztery pory roku-zima-10 z albumu HISTORIA MUZYKI, poniewa\u017c nie jest specyfikowana nazwa wykonawcy. Jednak\u017ce, \"Cztery pory roku\" skomponowane przez Antonio Vivaldiego to jedno z najbardziej znanych i cenionych dzie\u0142 klasycznych, kt\u00f3re cieszy si\u0119 wci\u0105\u017c du\u017c\u0105 popularno\u015bci\u0105 w\u015br\u00f3d s\u0142uchaczy na ca\u0142ym \u015bwiecie. Zima, jako jeden z czterech koncert\u00f3w w cyklu, to dynamiczny utw\u00f3r o bogatej i zmiennokszta\u0142tnej instrumentacji, z fajerwerkami d\u017awi\u0119k\u00f3w skrzypiec i szybkimi tempami, oddaj\u0105cy dynamiczny krajobraz tej pory roku." + }, + { + "role": "assistant", + "content": "\"Forevermore\" to utw\u00f3r niderlandzkiego projektu Ayreon, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Actual Fantasy Revisited\". Jest to pi\u0119kna ballada progresywna, kt\u00f3ra charakteryzuje si\u0119 mocno melodyjnymi partiami wokalnymi oraz gitarowymi harmoniami. Tekst opowiada o mi\u0142o\u015bci ponad czasem, a wokale w wykonaniu Damiana Wilsona i Lanvalla, Thomasa Vikstr\u00f6ma, Lucasa Tamasa i Petera Daltreya uwypuklaj\u0105 emocje sprz\u0119gni\u0119te z tekstami i brzmieniem ca\u0142ego utworu.\n\nW utworze \"Forevermore\" s\u0142ycha\u0107 klasyczn\u0105 koncepcj\u0119 muzyki klasycznej, kt\u00f3ra zosta\u0142a przetkana przez elementy rockowe oraz stylistyk\u0119 progresywn\u0105. Ballada ta wyr\u00f3\u017cnia si\u0119 swoj\u0105 obfito\u015bci\u0105 z\u0142o\u017conych aran\u017cacji i empati\u0105, z jak\u0105 dotyka s\u0142uchaczy, oddaj\u0105c bogactwo uczu\u0107 i emocji. Ayreon pozostaje jednym z najbardziej wp\u0142ywowych i uznanych projekt\u00f3w w \u015bwiecie muzyki progresywnej, kt\u00f3rej tworzy\u0142a posta\u0107 Arjena Lucassena." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Elizabeth wykonawcy XIII Stoleti z albumu Ztraceni V Karpatech" + }, + { + "role": "assistant", + "content": "\"Elizabeth\" to utw\u00f3r zespo\u0142u XIII Stoleti, kt\u00f3ry pochodzi z ich albumu \"Ztraceni V Karpatech\". Jest to metalowa ballada, kt\u00f3ra opowiada histori\u0119 kr\u00f3lowej El\u017cbiety Batory, jednej z najbardziej kontrowersyjnych postaci w historii Polski i W\u0119gier. \n\nTekst utworu nawi\u0105zuje do legend o kr\u00f3lowej i opowiada o jej rz\u0105dach, kt\u00f3re pocz\u0105tkowo by\u0142y okrutne, ale nast\u0119pnie sta\u0142y si\u0119 bardziej \u0142agodne. Debiut zespo\u0142u XIII Stoleti, kt\u00f3ry pojawi\u0142 si\u0119 w 1991 roku, da\u0142 pocz\u0105tek brzmieniu, kt\u00f3re \u0142\u0105czy rock, metal, gotycki i folkowy oktawia\u017c. Wi\u0119kszo\u015b\u0107 piosenek zespo\u0142u to pi\u0119kne oraz melodyjne, a \"Elizabeth\" to jedno z ich najwi\u0119kszych. \n\nPomimo, \u017ce utw\u00f3r ten opowiada o ciemnej i makabryczne postaci, to cieszy si\u0119 on du\u017c\u0105 popularno\u015bci\u0105 w\u015br\u00f3d fan\u00f3w gotyckiej muzyki i metalowych ballad. W ciemnej atmosferze, jak\u0105 ten utw\u00f3r niesie, kryje si\u0119 pewna tajemniczo\u015b\u0107 i pi\u0119kno, kt\u00f3re daje nadziej\u0119, \u017ce nawet w najciemniejszych czasach jest szansa na lepsze jutro." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Stormalong wykonawcy North Cape z albumu Szanty I Piesni Morza A`capella" + }, + { + "role": "assistant", + "content": "\"Stormalong\" to klasyczna pie\u015b\u0144 \u017ceglarska, kt\u00f3ra zosta\u0142a wykonywana przez wielu artyst\u00f3w na przestrzeni lat. W tym przypadku, utw\u00f3r ten wykonywany jest przez brytyjski zesp\u00f3\u0142 North Cape i znajduje si\u0119 na ich albumie \"Szanty i Piosenki Morza Acapella\".\n\nPie\u015b\u0144 opowiada histori\u0119 Stormalonga, legendarnego marynarza, kt\u00f3ry przep\u0142yn\u0105\u0142 morza i oceany, a jego imi\u0119 sta\u0142o si\u0119 synonimem si\u0142y i nieust\u0119pliwo\u015bci wobec trudno\u015bci. Tekst utworu zawiera wiele odniesie\u0144 do \u017cycia marynarzy, morza i statk\u00f3w oraz opisuje \u017cycie na pok\u0142adzie z perspektywy marynarzy. \n\nMuzyka, jak\u0105 towarzyszy temu utworowi, to tradycyjne d\u017awi\u0119ki \u017ceglarskiej muzyki oraz refrenowie wstawki, kt\u00f3re wzd\u0142u\u017caj\u0105 klimat i charakter tego\u017c utworu. North Cape w swoim wykonaniu z powodzeniem oddaje ca\u0142y nastr\u00f3j i emocje, jakie towarzysz\u0105 s\u0142uchaniu tej klasycznej pie\u015bni \u017ceglarskiej. \"Stormalong\" to utw\u00f3r, kt\u00f3ry pozostaje niezwykle popularny w\u015br\u00f3d mi\u0142o\u015bnik\u00f3w muzyki morskiej oraz marynistycznej i wci\u0105\u017c jest cz\u0119sto wykonywany na koncertach i festiwalach." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Year Zero (feat. Quentin Cornet) (feat. Quentin Cornet) wykonawcy Minniva z albumu Volume #1" + }, + { + "role": "assistant", + "content": "\"Year Zero\" jest utworem wykonanym przez Minniv\u0119 we wsp\u00f3\u0142pracy z wideoklipowcem Quentinem Cornetem. To wersja coverowa piosenki ameryka\u0144skiego zespo\u0142u rockowego Thirty Seconds to Mars, kt\u00f3ry oryginalnie nagra\u0142 ten numer na albumie \"Love Lust Faith + Dreams\" z 2013 roku.\n\nW interpretacji Minnivy i Quentina Corneta, \"Year Zero\" nabra\u0142o nowego kszta\u0142tu i sta\u0142o si\u0119 bardziej ci\u0119\u017ckie, mroczne i zimne. Dodatkowo, wersja ta zawiera rozleg\u0142e partie wokalne, kt\u00f3re zrobi\u0142y na fansach wielkie wra\u017cenie. Tekst utworu rozwija w\u0105tek apokaliptycznego \u015bwiata po upadku cywilizacji, gdzie rz\u0105dzi moc, a nowy porz\u0105dek wprowadza nikt inny jak sam szatan. \n\nMinniva to wielki fan rocka i metalu, znana z tworzenia cover\u00f3w rockowych klasyk\u00f3w, kt\u00f3rych wokalne partie s\u0105 wykonywane przez popularnych youtuber\u00f3w/Influencer\u00f3w. Wspierana przez Corneta wierne oddaje ho\u0142d zespo\u0142owi Thirty Seconds to Mars i \"Year Zero\", przy jednoczesnym urozmaiceniu i nadaniu utworowi wyj\u0105tkowego klimatu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku I Wish I Was wykonawcy Scooter z albumu Music for a Big Night Out" + }, + { + "role": "assistant", + "content": "\"I Wish I Was\" to utw\u00f3r niemieckiej grupy Scooter, kt\u00f3ry pojawi\u0142 si\u0119 w 2012 roku na albumie \"Music for a Big Night Out\". Jest to utw\u00f3r z gatunku muzyki klubowej, charakteryzuj\u0105cy si\u0119 mocnymi bitami i szybkim tempem. \n\nW utworze tym s\u0142ycha\u0107 charakterystyczn\u0105 dla Scootera mieszank\u0119 r\u00f3\u017cnych styl\u00f3w, takich jak dance, trance i techno. Tekst skupia si\u0119 na motywie marzenia o czym\u015b, czego brakuje w \u017cyciu, a jednocze\u015bnie zach\u0119ca do pozytywnego my\u015blenia i robienia krok\u00f3w w kierunku realizacji swoich marze\u0144. \n\n\"I Wish I Was\" to typowy dla Scootera utw\u00f3r, kt\u00f3ry pozwala na przelanie emocji i energetyczne odczucia na parkiet, a tak\u017ce stanowi dobry przyk\u0142ad po\u0142\u0105czenia r\u00f3\u017cnych styl\u00f3w i gatunk\u00f3w muzycznych." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Tentacles (Armitiage and Company) wykonawcy H.P. Lovecraft Historical Society z albumu A Shoggoth on the Roof" + }, + { + "role": "assistant", + "content": "\"Tentacles (Armitiage and Company)\" to utw\u00f3r wykonany przez The H.P. Lovecraft Historical Society, kt\u00f3ry pochodzi z ich albumu \"A Shoggoth on the Roof\". Jest to kawa\u0142ek z pogranicza musicalu i horroru, charakteryzuj\u0105cy si\u0119 mroczn\u0105, ba\u015bniow\u0105 atmosfer\u0105. Tekst piosenki opowiada o morskim potworze i jego ucieczce ze szpitala psychiatrycznego, podczas gdy personel szuka go za wszelk\u0105 cen\u0119. \n\nW utworze s\u0142ycha\u0107 unikalne nagrania d\u017awi\u0119kowe, instrumenty d\u0119te i smyczkowe, kt\u00f3re tworz\u0105 nastrojowy podk\u0142ad dla wokalu. \"Tentacles (Armitiage and Company)\" to kawa\u0142ek, kt\u00f3ry doskonale odzwierciedla estetyk\u0119 The H.P. Lovecraft Historical Society, kt\u00f3ra specjalizuje si\u0119 w tworzeniu muzyki inspirowanej tw\u00f3rczo\u015bci\u0105 Howarda Phillipsa Lovecrafta i innych pisarzy horroru. \n\nOstatecznie, \"Tentacles (Armitiage and Company)\" to przera\u017caj\u0105cy, ale jednocze\u015bnie intryguj\u0105cy kawa\u0142ek muzyki, kt\u00f3ry \u015bwietnie wprowadza s\u0142uchacza w \u015bwiat Lovecraftowskiej horroru i ba\u015bniowego niepokoju." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Transhuman wykonawcy Amaranthe z albumu The Nexus" + }, + { + "role": "assistant", + "content": "\"Transhuman\" to utw\u00f3r szwedzko-du\u0144skiego zespo\u0142u Amaranthe pochodz\u0105cy z albumu \"The Nexus\". Jest to kawa\u0142ek z gatunku metalcore z dodatkiem element\u00f3w elektronicznych, a tekst opowiada o zmienianiu swojego ludzkiego cia\u0142a i umys\u0142u, aby stawa\u0107 si\u0119 coraz bardziej \"transhumanistycznym\" - czyli zwi\u0105zany ze zwi\u0119kszeniem mo\u017cliwo\u015bci fizycznych, psychicznych i intelektualnych cz\u0142owieka za pomoc\u0105 nowych technologii.\n\nUtw\u00f3r \"Transhuman\" charakteryzuje si\u0119 starann\u0105 produkcj\u0105, bogatymi partiami instrumentalnymi i mocnym wokalem Elize Ryd. Niekt\u00f3re elementy utworu kojarz\u0105 si\u0119 z muzyk\u0105 pop i EDM, ale jednocze\u015bnie zesp\u00f3\u0142 z powodzeniem \u0142\u0105czy je z ci\u0119\u017ckimi riffami i sol\u00f3wkami gitary elektrycznej, co czyni go naprawd\u0119 interesuj\u0105cym.\n\n\"Transhuman\" jest kawa\u0142kiem energetycznym i wci\u0105gaj\u0105cym, kt\u00f3ry z pewno\u015bci\u0105 zadowoli fan\u00f3w metalu oraz muzyki elektronicznej. Tekst utworu nawi\u0105zuje do idei transhumanizmu, jakie zdobywa coraz wi\u0119ksze zainteresowanie, i stanowi dobry precedens do dalszych rozm\u00f3w na temat technologiczno-kulturowych widok\u00f3w na przysz\u0142o\u015b\u0107 ludzko\u015bci." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Rublow wykonawcy Jacek Kaczmarski z albumu Kwadranse Jacka Kaczmarskiego" + }, + { + "role": "assistant", + "content": "\"Rublow\" to kawa\u0142ek z wierszem poetycka autorstwa Jacka Kaczmarskiego, znakomitego polskiego barda, kt\u00f3ry odnosi si\u0119 do ikony Rublowa, pochodz\u0105cej z klasztoru Wniebowzi\u0119cia Bogurodzicy i \u015bw. Sergiusza w Zagorsku. Warkocze br\u0105zowe, opisane w tek\u015bcie, to wizerunek \u017cycia i pracy mnich\u00f3w prawos\u0142awnych, kt\u00f3rzy po\u015bwi\u0119cili swoje \u017cycie na s\u0142u\u017cb\u0119 Boga. \n\nTekst tej pie\u015bni jest pe\u0142en symboliki i odnosi si\u0119 do natury ludzkiej, \u017cycia duchowego oraz zwi\u0105zku cz\u0142owieka z natur\u0105. W utworze \"Rublow\" Kaczmarski porusza pytania o sens naszego istnienia, naszej to\u017csamo\u015bci i pochodzenia, a tak\u017ce o naszej roli w spo\u0142ecze\u0144stwie i wobec Boga. \n\nZ powodu swojego symbolizmu i uniwersalnych temat\u00f3w, \"Rublow\" jest uwa\u017cany za jedno z najpi\u0119kniejszych dzie\u0142 Jacka Kaczmarskiego, a sam utw\u00f3r na sta\u0142e wpisa\u0142 si\u0119 w polsk\u0105 kultur\u0119 muzyczn\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Molotov wykonawcy Soulfly z albumu Dark Ages" + }, + { + "role": "assistant", + "content": "\"Molotov\" to utw\u00f3r z albumu \"Dark Ages\" ameryka\u0144skiego metalowego zespo\u0142u Soulfly. Jest to utw\u00f3r o charakterze agresywnym i mocnym brzmieniu. Tekst utworu odnosi si\u0119 do r\u00f3\u017cnych kontrowersyjnych temat\u00f3w, takich jak przemoc, przest\u0119pstwa i bunt. \n\n\"Molotov\" to utw\u00f3r grany w doskona\u0142ym tempie, oparty na metalowych riffach i brzmieniach. Tekst porusza mniejszo\u015bci, kt\u00f3re cz\u0119sto s\u0105 ofiar\u0105 przemoc i opresji dominantnych grup, a tak\u017ce podkre\u015bla potrzeb\u0119 walki i wyzwolenia. \n\nJako ca\u0142o\u015b\u0107, \"Molotov\" to kawa\u0142ek dobrze zbalansowanej mocy i agresji z odpowiednio dobranym tekstem, kt\u00f3ry przyci\u0105ga uwag\u0119 ludzi, kt\u00f3rzy lubi\u0105 ci\u0119\u017ck\u0105 muzyk\u0119 i tematyk\u0119 walki o wolno\u015b\u0107 i r\u00f3wno\u015b\u0107." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Living In Victory wykonawcy HammerFall z albumu Renegade" + }, + { + "role": "assistant", + "content": "\"Living In Victory\" to utw\u00f3r szwedzkiej grupy metalowej HammerFall, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Renegade\". Jest to kawa\u0142ek o szybkim tempie, charakteryzuj\u0105cy si\u0119 ci\u0119\u017ckim brzmieniem i wyrazistymi riffami gitarowymi. Tekst utworu porusza temat w\u0142adzy i chwa\u0142y, a wokalista wy\u015bpiewuje o tym, \u017ce mamy w sobie si\u0142\u0119, aby zwyci\u0119\u017ca\u0107 nad przeciwnikami i utrzyma\u0107 si\u0119 na czele. \n\nW refrenie utworu mo\u017cna us\u0142ysze\u0107 motyw \"Living in victory\", kt\u00f3ry jest niezwykle melodyjny i chwytliwy, a jednocze\u015bnie jest bardzo charakterystyczny dla stylu HammerFall. W ca\u0142o\u015bci utw\u00f3r \u0142\u0105czy w sobie energi\u0119 i ch\u0119\u0107 walki o triumf, co stanowi esencj\u0119 metalowej muzyki. \n\n\"Living In Victory\" to jednocze\u015bnie jedna z najlepszych piosenek z albumu \"Renegade\", kt\u00f3ry sta\u0142 si\u0119 jednym z najwa\u017cniejszych album\u00f3w w karierze HammerFall. Pojawi\u0142y si\u0119 na nim utwory, kt\u00f3re zyska\u0142y popularno\u015b\u0107 w kr\u0119gach metalowych, a tak\u017ce przyczyni\u0142y si\u0119 do popularno\u015bci zespo\u0142u w globalnej skali. Utw\u00f3r \"Living In Victory\" jest ideowym i muzycznym przyczynek do walki o wolno\u015b\u0107 i zwyci\u0119stwo, kt\u00f3re uosabiaj\u0105 warto\u015bci znane z klimatu muzycznego heavymetalowego." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The Wolves of Timberline wykonawcy Agalloch z albumu Compendium Archive" + }, + { + "role": "assistant", + "content": "\"The Wolves of Timberline\" to utw\u00f3r ameryka\u0144skiego zespo\u0142u black metalowego Agalloch, kt\u00f3ry pojawi\u0142 si\u0119 na albumie kompilacyjnym \"Compendium Archive\". Jest to kawa\u0142ek o zimowej atmosferze, charakteryzuj\u0105cy si\u0119 ci\u0119\u017ckim brzmieniem i mrocznymi tekstami. \n\nW utworze s\u0142ycha\u0107 zimowe d\u017awi\u0119ki i motywy, kt\u00f3re przypominaj\u0105 ch\u0142odne, mro\u017ane krajobrazy. Tekst odnosi si\u0119 do natury i \u017cycia dzikich zwierz\u0105t, poruszaj\u0105c temat dzikich wilk\u00f3w \u017cyj\u0105cych w lasach i na terenach g\u00f3rskich. \n\n\"The Wolves of Timberline\" to" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Chat Noir Chat Blanc wykonawcy Emir Kusturica & The No Smoking Orchestra z albumu Chat Noir Chat Blanc" + }, + { + "role": "assistant", + "content": "\"Chat Noir Chat Blanc\" to utw\u00f3r zespo\u0142u Emir Kusturica & The No Smoking Orchestra, kt\u00f3ry pojawi\u0142 si\u0119 na ich albumie o tym samym tytule. Jest to utw\u00f3r z gatunku punk rock, kt\u00f3ry \u0142\u0105czy w sobie r\u00f3\u017cne elementy muzyki folkowej, etnicznej i rockowej.\n\nTekst utworu jest w j\u0119zyku romani i opowiada histori\u0119 kota czarnego i kota bia\u0142ego, kt\u00f3rzy \u017cyj\u0105 razem w harmonii i braterskiej mi\u0142o\u015bci. Utw\u00f3r ma bardzo energetyczne tempo, a radosny i beztroski przekaz s\u0142\u00f3w dostarcza s\u0142uchaczom pozytywnych emocji i uciechy.\n\nEmir Kusturica & The No Smoking Orchestra to zesp\u00f3\u0142 zwi\u0105zany z kinematografi\u0105, a muzyka zagrana przez tych artyst\u00f3w jest cz\u0119sto u\u017cywana jako \u015bcie\u017cka d\u017awi\u0119kowa do film\u00f3w re\u017cyserowanych przez Emira Kusturic\u0119. \"Chat Noir Chat Blanc\" to jedna z najbardziej znanych piosenek zespo\u0142u, kt\u00f3ra wielu s\u0142uchaczom pozostaje w pami\u0119ci na d\u0142ugo i stanowi jednocze\u015bnie dobry wyb\u00f3r na zabaw\u0119 lub relaks. B\u0142yskawiczny szyk oraz brak \u017cadnych sztywnych struktur pozwala przyj\u0105\u0107 r\u00f3\u017cne nastroje mimo niezwykle prostej struktury utworu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Wallflower wykonawcy Peter Gabriel z albumu New Blood" + }, + { + "role": "assistant", + "content": "\"Wallflower\" to utw\u00f3r brytyjskiego muzyka Petera Gabriela, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"New Blood\". Jest to utw\u00f3r o spokojnym tempie, oparty na aran\u017cacjach smyczkowych i fortepianowych, a tak\u017ce na wielowarstwowych wokalach Gabriela. \n\nTekst utworu opowiada histori\u0119 osoby, kt\u00f3ra pozostaje w cieniu, obserwuj\u0105c \u015bwiat z boku i nie uczestniczy\u0107 w nim. To pie\u015b\u0144 o samotno\u015bci i odczuwaniu, \u017ce nie pasujemy do spo\u0142ecze\u0144stwa. Tekst ten stanowi pr\u00f3b\u0119 zrozumienia i akceptacji tej emocji, a jednocze\u015bnie zach\u0119ca do wyj\u015bcia z cienia i przej\u0119cia kontroli nad swoim \u017cyciem. \n\nW \"Wallflower\" wida\u0107 odej\u015bcie Gabriela od swojego klasycznego brzmienia z lat 80. i 90., na rzecz bardziej minimalistycznej i spokojnej muzyki, skoncentrowanej na emocjach i atmosferze. Melodia i aran\u017cacje muzyki s\u0105 przygn\u0119biaj\u0105ce, ale jednocze\u015bnie pe\u0142ne nadziei i pozytywnego my\u015blenia. To kawa\u0142ek dla wszystkich tych, kt\u00f3rzy kiedykolwiek czuli si\u0119 samotni lub niedocenieni w \u017cyciu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku I'm Bigger Than You wykonawcy Lordi z albumu Babez for Breakfast" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Chaos wykonawcy Ayreon z albumu Universal Migrator Part II: Flight Of The Migrator" + }, + { + "role": "assistant", + "content": "\"Chaos\" to utw\u00f3r Ayreon, projektem holenderskiego muzyka Arjena Lucassena, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Universal Migrator Part II: Flight Of The Migrator\". Jest to utw\u00f3r z gatunku metalu progresywnego, kt\u00f3ry \u0142\u0105czy w sobie elementy metalu, rocka i klasycznej muzyki.\n\n\"Chaos\" opowiada histori\u0119 odczuwania braku kontroli, mi\u0119dzy innymi nad \u015bwiatem, emocjami i \u017cyciem. Tekst utworu dotyka r\u00f3wnie\u017c problem\u00f3w egzystencjalnych oraz pyta\u0144 o istot\u0119 i cel naszej egzystencji.\n\nUtw\u00f3r charakteryzuje si\u0119 z\u0142o\u017con\u0105, wielowarstwow\u0105 struktur\u0105 d\u017awi\u0119kow\u0105, z aran\u017cacjami orkiestrowymi i ch\u00f3rami, popartymi mocnymi riffami gitary elektrycznej. Wokale w \"Chaos\" s\u0105 dynamiczne, sk\u0142adaj\u0105ce si\u0119 z bystrych zwrot\u00f3w i emocjonalnych wybuch\u00f3w, kt\u00f3re wzmocni\u0107 przekaz tekstu.\n\n\"Chaos\" to utw\u00f3r, kt\u00f3ry nie tylko potrafi zaskoczy\u0107 s\u0142uchaczy swoj\u0105 r\u00f3\u017cnorodno\u015bci\u0105, ale r\u00f3wnie\u017c bardzo skutecznie nawi\u0105zuje do emocjonalnych i egzystencjalnych aspect\u00f3w naszego \u017cycia. To za\u015b czyni go idealnym wyborem dla tych, kt\u00f3rzy ceni\u0105 sobie eksperymenty muzyczne oraz szukaj\u0105 refleksyjnej i filozoficznej warto\u015bci w muzyce." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Where You Come From (Studio Recording May '97) wykonawcy Pantera z albumu Official Live: 101 Proof (AMCY-2309)" + }, + { + "role": "assistant", + "content": "Niestety, nie posiadam informacji odno\u015bnie utworu \"Where You Come From (Studio Recording May '97)\" wykonawcy Pantera z albumu \"Official Live: 101 Proof (AMCY-2309)\". Problem ten mo\u017ce wynika\u0107 z b\u0142\u0119dnie podanych szczeg\u00f3\u0142\u00f3w, niekompletnych informacji lub braku takowego utworu w oficjalnej dyskografii zespo\u0142u. Mo\u017cliwe jest r\u00f3wnie\u017c, \u017ce jest to utw\u00f3r niepublikowany lub alternatywna wersja ju\u017c istniej\u0105cego kawa\u0142ka. W takim wypadku sugeruj\u0119 poszukanie dodatkowych informacji lub \u017ar\u00f3de\u0142 potwierdzaj\u0105cych istnienie i charakterystyk\u0119 tego utworu." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Dangerous trucks wykonawcy Cynthia Witthoft z albumu Finland defloration" + }, + { + "role": "assistant", + "content": "Niestety, jako model j\u0119zykowy stworzony na bazie zasad generowania j\u0119zyka naturalnego, nie jestem w stanie opowiedzie\u0107 o kawa\u0142ku \"Dangerous trucks\" wykonawcy Cynthia Witthoft z albumu \"Finland Defloration\". Jest to utw\u00f3r, kt\u00f3ry nie istnieje i nie mo\u017cna o nim opowiada\u0107. Je\u015bli chcesz porozmawia\u0107 o innym utworze muzycznym, mo\u017cemy zacz\u0105\u0107 od pocz\u0105tku i podyskutowa\u0107 na jego temat." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Wielorybnik Johny wykonawcy Segars z albumu Ten jeden rejs" + }, + { + "role": "assistant", + "content": "Nie zgadza si\u0119. Segars nie ma albumu o nazwie \"Ten jeden rejs\", ani piosenki \"Wielorybnik Johny\". Mog\u0119 opowiedzie\u0107 Ci o innym utworze \"W\u0105\u017c\" z albumu \"Dobranocka\" lub \"Nie umiem kocha\u0107\" z albumu \"Jazzda\". Prosz\u0119 o wyb\u00f3r." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Na czterech akordach wykonawcy \u00a3YDKA GRUBASA z albumu O-dur C-b\u00f3\u00b3" + }, + { + "role": "assistant", + "content": "Niestety, jako AI, nie mog\u0119 opowiedzie\u0107 Ci o tej piosence, poniewa\u017c nie jest ona dost\u0119pna w mojej bazie danych. Przepraszam za utrudnienia. Czy mog\u0119 pom\u00f3c Ci w inny spos\u00f3b?" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Doedel Up wykonawcy Guano Apes z albumu Planet Of The Apes (Best Of) " + }, + { + "role": "assistant", + "content": "\"Doedel Up\" to utw\u00f3r niemieckiego zespo\u0142u Guano Apes, kt\u00f3ry pojawi\u0142 si\u0119 na ich albumie \"Planet Of The Apes (Best Of)\". Jest to piosenka z gatunku rocka alternatywnego, charakteryzuj\u0105ca si\u0119 silnym rytmem i melodyjnym wokalem zespo\u0142u. \n\nTekst utworu pochodzi z j\u0119zyka pidgin, kt\u00f3ry powsta\u0142 podczas kolonizacji Afryki. Wokalistka Sandra Nasi\u0107 zainspirowa\u0142a si\u0119 tym j\u0119zykiem, by stworzy\u0107 tekst, kt\u00f3ry por\u00f3wnuje procesy chemiczne do kolonizacji Afryki, wykorzystuj\u0105c metafory, takie jak \"bia\u0142e nitki\" przypominaj\u0105ce kolonialne unieszkodliwianie ludzi kolorowych.\n\n\"Doedel Up\" to ciekawa i intryguj\u0105ca piosenka, kt\u00f3ra \u0142\u0105czy w sobie r\u00f3\u017cne style muzyczne, m.in. rock alternatywny, punk rock i reggae. Tekst utworu porusza r\u00f3wnie\u017c tematy zwi\u0105zane z polityk\u0105 i histori\u0105, a tak\u017ce z technologi\u0105 i nauk\u0105. To interesuj\u0105cy kawa\u0142ek dla fan\u00f3w Guano Apes, a tak\u017ce dla tych, kt\u00f3rzy interesuj\u0105 si\u0119 rockiem alternatywnym oraz spo\u0142ecznymi i politycznymi problemami." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku One With The World wykonawcy Gamma Ray - ( Upload For www.Other.pl ) z albumu Sigh No More" + }, + { + "role": "assistant", + "content": "\"One With The World\" to utw\u00f3r niemieckiej grupy powermetalowej Gamma Ray, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Sigh No More\". Jest to utw\u00f3r o szybkim tempie i ci\u0119\u017ckim brzmieniu, oparty na agresywnych riffach gitarowych i charakterystycznych dla powermetalowych hymn\u00f3w. \n\nTekst utworu opowiada o jedno\u015bci z ca\u0142ym \u015bwiatem i o potrzebie uwiarygodniania kobieco\u015bci. Refren \"One With The World\" jest bardzo chwytliwy i pozytywny, a jego motyw powraca wiele razy w trakcie utworu, tworz\u0105c zar\u00f3wno melodie melorecytatywne jak i instrumentatywne.\n\nW po\u0142\u0105czeniu z dynamicznymi aran\u017cacjami, \"One With The World\" staje si\u0119 pie\u015bni\u0105 po\u015bwi\u0119con\u0105 ludzkiemu d\u0105\u017ceniu do jedno\u015bci i wolno\u015bci. To utw\u00f3r, kt\u00f3ry pozwala s\u0142uchaczom uwolni\u0107 swoj\u0105 energi\u0119 i wyruszy\u0107 w poszukiwaniu si\u0142y, by stan\u0105\u0107 twarz\u0105 w twarz z wyzwaniami \u017cycia i przetrwa\u0107." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Born To Be (Hated) Original Od wykonawcy HOCICO z albumu Born To Be (Hated) [MCD]" + }, + { + "role": "assistant", + "content": "\"Born to be (Hated)\" to utw\u00f3r meksyka\u0144skiego zespo\u0142u Hocico, kt\u00f3ry pojawi\u0142 si\u0119 na ich minialbumie o tym samym tytule. Jest to kawa\u0142ek z gatunku electro-industrial, charakteryzuj\u0105cy si\u0119 ci\u0119\u017ckim i pulsuj\u0105cym brzmieniem, oraz intensywnymi wokalami typowymi dla muzyki industrialnej.\n\nTekst utworu skupia si\u0119 na motywach gniewu, buntu i destrukcji, a przekaz s\u0142\u00f3w ma na celu wyzwolenie s\u0142uchaczy z konformizmu, opresji i ogranicze\u0144. Wokalista wy\u015bpiewuje, \u017ce jest urodzony, by by\u0107 nienawidzonym, co ma podkre\u015bla\u0107 jego wyj\u0105tkowo\u015b\u0107 i odmienno\u015b\u0107, a jednocze\u015bnie stanowi manifestacja niezale\u017cno\u015bci i wolno\u015bci. \n\n\"Born to be (Hated)\" to kawa\u0142ek agresywny i niepokoj\u0105cy, a jednocze\u015bnie przyci\u0105gaj\u0105cy i charakteryzuj\u0105cy si\u0119 unikalnym brzmieniem. Jest to jedna z najbardziej znanych piosenek Hocico, kt\u00f3ra przynios\u0142a im popularno\u015b\u0107 w\u015br\u00f3d fan\u00f3w muzyki industrialnej w r\u00f3\u017cnych krajach, w tym w Polsce." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Welcome wykonawcy Gamma Ray - ( Upload For www.Other.pl ) z albumu Blast From The Past (CD 1)" + }, + { + "role": "assistant", + "content": "\"Welcome\" to utw\u00f3r niemieckiego zespo\u0142u Gamma Ray, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Blast from the Past (CD 1)\". Jest to kompozycja powermetalowa, utrzymana w szybkim tempie i oparta na charakterystycznym dla tego gatunku \u015bpiewie melorecytatywnym oraz harmoniach gitarowych.\n\nTekst utworu nawi\u0105zuje do motywu powrotu do domu i do szukania oazy spokoju, gdzie mo\u017cemy odpocz\u0105\u0107 i poczu\u0107 si\u0119 bezpiecznie. Refren \"Welcome\" jest bardzo chwytliwy i \u0142atwy do zapami\u0119tania, a jego melodyjny motyw przemija przez ca\u0142y utw\u00f3r, stanowi\u0105c melodi\u0119 przewodni\u0105 ca\u0142ej kompozycji.\n\nW po\u0142\u0105czeniu z energetyczn\u0105 muzyk\u0105 \"Welcome\" tworzy pozytywny przekaz, wprowadzaj\u0105cy s\u0142uchaczy w pozytywny nastr\u00f3j, kt\u00f3ry mo\u017ce pom\u00f3c w pokonywaniu trudno\u015bci i zniech\u0119cenia. \"Welcome\" to utw\u00f3r, kt\u00f3ry z pewno\u015bci\u0105 zmobilizuje s\u0142uchaczy i pozwoli wyruszy\u0107 na dalsz\u0105 drog\u0119 \u017cycia, w poszukiwaniu spe\u0142nienia marze\u0144 i realizacji swoich cel\u00f3w." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Lust For Life wykonawcy Gamma Ray - ( Upload For www.Other.pl ) z albumu Blast From The Past (CD 1)" + }, + { + "role": "assistant", + "content": "\"Lust for Life\" to cover utworu ameryka\u0144skiego zespo\u0142u Iggy Pop, kt\u00f3rego wykonanie pojawi\u0142o si\u0119 na albumie \"Blast from the Past\" niemieckiej grupy powermetalowej Gamma Ray. Jest to utw\u00f3r charakteryzuj\u0105cy si\u0119 szybkim tempem i riffami rockowymi, kt\u00f3re nosz\u0105 w sobie znacz\u0105cy punkowego feelingu. \n\nW por\u00f3wnaniu do orygina\u0142u, Gamma Ray wprowadzili sporo zmian, wprowadzaj\u0105c ci\u0119\u017csze brzmienie i bardziej \u017cywio\u0142owe wykonanie. To uj\u0119cie \"Lust for Life\" charakteryzuje si\u0119 niesamowit\u0105 energi\u0105, kt\u00f3ra motywuje s\u0142uchaczy do pokonywania trudno\u015bci i cieszenia si\u0119 \u017cyciem w pe\u0142ni jego intensywno\u015bci.\n\nTekst piosenki pochodzi zr\u00f3\u017cnicowany, ale porusza przede wszystkim tematy zwi\u0105zane z bogactwem do\u015bwiadcze\u0144 \u017cyciowych, rado\u015bci\u0105 z bycia wolnym i prowadzeniem niezale\u017cnego trybu \u017cycia. Gamma Ray stan\u0119li na wysoko\u015bci zadania, przet\u0142umaczyli tre\u015b\u0107 na j\u0119zyk heavy metal. Jest to kawa\u0142ek, kt\u00f3ry pozwala s\u0142uchaczom czerpa\u0107 rado\u015b\u0107 z bycia cz\u0119\u015bci\u0105 \u015bwiata, pe\u0142nego rado\u015bci i przyg\u00f3d." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Heaven Can Wait wykonawcy Gamma Ray - ( Upload For www.Other.pl ) z albumu Blast From The Past (CD 1)" + }, + { + "role": "assistant", + "content": "\"Heaven Can Wait\" to utw\u00f3r niemieckiego zespo\u0142u powermetalowego Gamma Ray, kt\u00f3ry pojawi\u0142 si\u0119 na ich albumie \"Land of the Free\" oraz na sk\u0142adance \"Blast from the Past\", na pierwszym dysku. Jest to melodyjny i dynamiczny utw\u00f3r, z\u0142o\u017cony z pulsuj\u0105cych riff\u00f3w gitarowych i charakterystycznych dla muzyki heavy metalowej sol\u00f3wek gitarowych oraz wokalu, kt\u00f3ry przewodzi w utworze.\n\nTekst utworu opowiada o szukaniu szcz\u0119\u015bcia w \u017cyciu i o tym, \u017ce \"niebo mo\u017ce poczeka\u0107\" na nasz\u0105 \u015bmier\u0107, m\u00f3wi\u0105c, \u017ce wci\u0105\u017c jest czas na osi\u0105gni\u0119cie swoich cel\u00f3w i spe\u0142nienie marze\u0144. Refren piosenki zawiera pozytywn\u0105 i optimistyczn\u0105 wiadomo\u015b\u0107, \u017ce wci\u0105\u017c warto walczy\u0107 o swoje marzenia i nie poddawa\u0107 si\u0119 na duchu.\n\n\"Heaven Can Wait\" to utw\u00f3r, kt\u00f3ry charakteryzuje si\u0119 szybkim tempem, epickim brzmieniem i emocjonalnym przekazem. To piosenka, kt\u00f3ra zawsze cieszy\u0142a si\u0119 uznaniem fan\u00f3w zespo\u0142u oraz szerokiego grona mi\u0142o\u015bnik\u00f3w muzyki heavy metalowej." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku God Fallen Silent wykonawcy Edguy z albumu Age Of The Joker (CD2)" + }, + { + "role": "assistant", + "content": "\"God Fallen Silent\" to utw\u00f3r niemieckiego zespo\u0142u power metalowego Edguy, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Age of the Joker\" na drugim dysku. Jest to piosenka o \u015brednim tempie, kt\u00f3ra charakteryzuje si\u0119 emocjonalnym i melodyjnym wokalem wokalisty Tobiasa Sammeta oraz epickim brzmieniem.\n\nTekst utworu skupia si\u0119 na tematach zwi\u0105zanych z wiar\u0105 i zastanawia si\u0119, dlaczego B\u00f3g milcze. Refren piosenki zawiera mocn\u0105 i poruszaj\u0105c\u0105 wiadomo\u015b\u0107, \u017ce cho\u0107 czasami mo\u017ce wydawa\u0107 si\u0119, \u017ce B\u00f3g jest oboj\u0119tny na ludzkie problemy, to wci\u0105\u017c warto wierzy\u0107 i nie traci\u0107 nadziei.\n\n\"God Fallen Silent\" to utw\u00f3r, kt\u00f3ry potrafi poruszy\u0107 emocje i wywo\u0142a\u0107 refleksj\u0119 w s\u0142uchaczach. To przyk\u0142ad piosenki o silnym przekazie, kt\u00f3ra nie tylko dostarcza muzyki, ale r\u00f3wnie\u017c intryguj\u0105cych tekst\u00f3w." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Red Baron / Blue Max wykonawcy Iced Earth z albumu The Glorious Burden" + }, + { + "role": "assistant", + "content": "\"Red Baron/Blue Max\" to utw\u00f3r z albumu The Glorious Burden ameryka\u0144skiego zespo\u0142u Iced Earth. Jest to utw\u00f3r o szybkim tempie, przepe\u0142niony melodyjnymi gitarami, sekcj\u0105 rytmiczn\u0105 i ch\u00f3rami.\n\nTekst utworu opowiada histori\u0119 dw\u00f3ch niemieckich as\u00f3w lotnictwa - czerwonego Barona (Manfred von Richthofen) i Blue Maxa (Max Immelmann). Oboje byli jednymi z najbardziej znanych i odwa\u017cnych pilot\u00f3w podczas I wojny \u015bwiatowej. W utworze poruszony jest temat romantyzacji wojny, a tak\u017ce g\u0142\u0119bokiej refleksji nad ludzkim losem i bezsensowno\u015bci\u0105 wojen.\n\nW muzyce \"Red Baron/Blue Max\" mo\u017cna us\u0142ysze\u0107 wp\u0142ywy power metalu i thrash metalu, z wyra\u017anymi wp\u0142ywami melodyjnej gitary. Ca\u0142y utw\u00f3r jest przepe\u0142niony energi\u0105, z dramatycznym nastrojem, dzi\u0119ki kt\u00f3remu s\u0142uchacze mog\u0105 poczu\u0107 si\u0119, jakby byli na frontowej linii walk. To jedna z najbardziej znanych piosenek zespo\u0142u Iced Earth, kt\u00f3ra przyci\u0105ga uwag\u0119 fan\u00f3w metalu z ca\u0142ego \u015bwiata." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Duch czasu wykonawcy Jacek Kaczmarski z albumu Suplement - Radio Wolna Europa" + }, + { + "role": "assistant", + "content": "\"Duch Czasu\" to utw\u00f3r polskiego barda Jacka Kaczmarskiego, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Suplement - Radio Wolna Europa\". Jest to piosenka poetycka, oparta na akompaniamencie gitary i charakterystycznym dla Kaczmarskiego recytacji tekstu, kt\u00f3ra porusza wa\u017cne tematy zwi\u0105zane z histori\u0105 i polityk\u0105.\n\nTekst utworu opowiada o zawirowaniach dziejowych w kraju i na \u015bwiecie oraz o tym, jak ludzie przystosowuj\u0105 si\u0119 do zmian. Kaczmarski porusza w nim r\u00f3wnie\u017c temat dziedzictwa kulturowego i historii, a tak\u017ce problem\u00f3w wsp\u00f3\u0142czesnej Polski, takich jak bezrobocie, bieda i nier\u00f3wno\u015bci spo\u0142eczne.\n\n\"Duch Czasu\" to kompozycja, kt\u00f3ra charakteryzuje si\u0119 mocnym i wyrazistym przekazem, a jednocze\u015bnie prostot\u0105 i eleganckim brzmieniem. To utw\u00f3r, kt\u00f3ry porusza wa\u017cne kwestie i sk\u0142ania s\u0142uchaczy do refleksji, a zarazem mo\u017ce by\u0107 inspiracj\u0105 do dzia\u0142ania na rzecz zmian w spo\u0142ecze\u0144stwie." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Sepulnation wykonawcy Sepultura z albumu Nation" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Bibbidi Bobbidi Boo (the Magic Song) wykonawcy Verna Felton z albumu Driving with Disney" + }, + { + "role": "assistant", + "content": "\"Bibbidi-Bobbidi-Boo\" to piosenka wykorzystana w filmie Disneya z 1950 roku \"Kopciuszek\". W wykonaniu oryginalnej, ameryka\u0144skiej aktorki i piosenkarki Verna Felton, kt\u00f3ra w filmie podk\u0142ada\u0142a g\u0142os pod posta\u0107 Czarownicy.\n\n\"Bibbidi-Bobbidi-Boo\" to utw\u00f3r o szybkim tempie, z powtarzalnym refrenem i charakterystycznym dla muzyki Disneya brzmieniem jazzowym. Tekst utworu opowiada histori\u0119 kopciuszka, kt\u00f3ry zostaje zamieniony w pi\u0119kn\u0105 ksi\u0119\u017cniczk\u0119, dzi\u0119ki mocy Czarownicy.\n\nUtw\u00f3r ten szybko sta\u0142 si\u0119 niezwykle popularny i do dzi\u015b uwa\u017cany jest za jedn\u0105 z najbardziej rozpoznawalnych piosenek filmowych. Charakterystyczne melodie i radosny nastr\u00f3j \"Bibbidi-Bobbidi-Boo\" przemawiaj\u0105 do publiczno\u015bci w r\u00f3\u017cnym wieku i wprowadzaj\u0105 w \u015bwiat marze\u0144 i fantazji, kt\u00f3rymi s\u0142yn\u0105 filmy Disneya." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku 01Believe In Love wykonawcy Scorpions - Still Loving You z albumu Dzordz & Gonzo" + }, + { + "role": "assistant", + "content": "Nie ma albumu \"Dzordz & Gonzo\" utworzonego przez zesp\u00f3\u0142 Scorpions.\n\nJednak\u017ce \"Believe in Love\" to utw\u00f3r niemieckiego zespo\u0142u Scorpions, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Still Loving You\". Jest to klasyczna rockowa ballada, charakteryzuj\u0105ca si\u0119 pi\u0119knym melorecytatywnym wokalem wokalisty Klausa Meine oraz emocjonalnym, pe\u0142nym pasji brzmieniem.\n\nTekst utworu m\u00f3wi o wierze w mi\u0142o\u015b\u0107 i o jej znaczeniu w \u017cyciu cz\u0142owieka. Refren piosenki \"Believe in Love\" jest bardzo chwytliwy i \u0142atwo wpada w ucho, a tre\u015b\u0107 wskazuje, \u017ce nawet w ci\u0119\u017ckich momentach warto wierzy\u0107 w mi\u0142o\u015b\u0107 i d\u0105\u017cy\u0107 do prawdziwego szcz\u0119\u015bcia.\n\n\"Believe in Love\" to klasyczna ballada rockowa, kt\u00f3ra ma swoje miejsce w sercach fan\u00f3w muzyki na ca\u0142ym \u015bwiecie. Utw\u00f3r ten to jedna z najbardziej znanych piosenek Scorpions, kt\u00f3ra przyci\u0105ga uwag\u0119 s\u0142uchaczy swoj\u0105 si\u0142\u0105 emocjonaln\u0105 i pi\u0119knym wykonaniem." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku 01Liszt -Koncert fortepianowy Es-dur cz1 z albumu HISTORIA MUZYKI" + }, + { + "role": "assistant", + "content": "\"Liszt - Koncert fortepianowy Es-dur cz1\" to utw\u00f3r stworzony przez w\u0119gierskiego kompozytora i pianist\u0119 Franza Liszta w XIX wieku. Jest to pierwsza cz\u0119\u015b\u0107 z trzech w koncercie fortepianowym Es-dur, kt\u00f3ry nale\u017cy do klasyki romantycznej oraz dorobku mistrza fortepianowego. \n\nKompozycja ta charakteryzuje si\u0119 przepi\u0119kn\u0105 i bardzo dynamiczn\u0105 muzyk\u0105, w kt\u00f3rej mo\u017cna us\u0142ysze\u0107 liczne wariacje motyw\u00f3w, jak r\u00f3wnie\u017c przej\u015bcia pomi\u0119dzy melodyjnymi a ci\u0119\u017cszymi fragmentami. Jest ona niezwykle wymagaj\u0105ca dla pianisty, wymagaj\u0105c umiej\u0119tno\u015bci wirtuoza do wprowadzenia r\u00f3\u017cnorodnych efekt\u00f3w technicznych i mocnego, dynamycznego brzmienia na fortepianie.\n\nTo arcydzie\u0142o muzyki klasycznej, kt\u00f3re cieszy si\u0119 do dzi\u015b nies\u0142abn\u0105c\u0105 popularno\u015bci\u0105 i jest ci\u0105gle wykonywane przez pianist\u00f3w na ca\u0142ym \u015bwiecie. Cz\u0119\u015b\u0107 pierwsza koncertu fortepianowego Es-dur Liszta to utw\u00f3r, kt\u00f3ry w pe\u0142ni prezentuje jego zdolno\u015bci kompozytorskie i pianistyczne, a jednocze\u015bnie przyci\u0105ga swoj\u0105 pi\u0119kn\u0105 i porywaj\u0105c\u0105 muzyk\u0105." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku The Last Supper wykonawcy Tim Rice and Andrew Lloyd Webber z albumu Jesus Christ Superstar (Disc Two)" + }, + { + "role": "assistant", + "content": "\"The Last Supper\" to utw\u00f3r z rockowej opery Jesus Christ Superstar, napisanej przez Tima Rice'a i Andrew Lloyda Webbera. Jest to fragment utworu, kt\u00f3ry opowiada o ostatniej wieczerzy, jak\u0105 Jezus spo\u017cy\u0142 z aposto\u0142ami przed swoj\u0105 \u015bmierci\u0105.\n\nW utworze s\u0142yszymy dialog pomi\u0119dzy Jezusem i aposto\u0142ami, kt\u00f3rzy pyta\u0107 Go zaczynaj\u0105 o to, co dzieje si\u0119, a on odzywa si\u0119 do nich dziel\u0105c chleb i wino. Tekst utworu jest bardzo poruszaj\u0105cy i demonstruje emocje wynikaj\u0105ce z wiedzy o tym, co stanie si\u0119 p\u00f3\u017aniej.\n\n\"The Last Supper\" to utw\u00f3r o niezwykle dramatycznej i intensywnej atmosferze, co wydobywa si\u0119 mi\u0119dzy innymi dzi\u0119ki przejmuj\u0105cym wokalom oraz dynamice gitar i perkusji. To jedna z kluczowych piosenek z rockowej opery Jesus Christ Superstar, kt\u00f3ra cieszy si\u0119 du\u017c\u0105 popularno\u015bci\u0105, zar\u00f3wno w\u015br\u00f3d fan\u00f3w musicali, jak i szerokiej publiczno\u015bci." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku Over at the Frankenstein place wykonawcy Rocky horror show z albumu Revival" + }, + { + "role": "assistant", + "content": "\"Over at the Frankenstein Place\" to utw\u00f3r z musicalu \"The Rocky Horror Show\" napisanego przez Richarda O'Briena. W musicalu tym rockowa grupa wied\u017am sk\u0142ada ho\u0142d filmom grozy z lat 50. i 60. XX wieku, co widoczne jest tak\u017ce w licznych odniesieniach do postaci oraz przedmiot\u00f3w z tych film\u00f3w. Jest to piosenka, kt\u00f3ra pojawia si\u0119 w drugiej cz\u0119\u015bci musicalu i stanowi przewodni\u0105 melodi\u0119 w scenie, w kt\u00f3rej g\u0142\u00f3wni bohaterowie - Brad i Janet - docieraj\u0105 do Frankenstein Place, poszukuj\u0105c schronienia podczas burzy. \n\n\"Over at the Frankenstein Place\" charakteryzuje si\u0119 \u0142agodnym tempem i romantyczn\u0105, nostalgiczna melodyk\u0105, a tak\u017ce orkiestracj\u0105 i ch\u00f3ralnym wokalem. Piosenka ta jest zar\u00f3wno osobista, jak i intymne, sk\u0142adaj\u0105c ho\u0142d klasycznej epoce filmowej, kt\u00f3ra inspiracj\u0105 dla tw\u00f3rc\u00f3w musicalu.\n\nW \"The Rocky Horror Show\" \"Over at the Frankenstein Place\" stanowi chwytliwe i pi\u0119knie za\u015bpiewane uroczyste przekazanie przekazu, kt\u00f3ry prowadzi bohater\u00f3w do miejsca, w kt\u00f3rym dowiedz\u0105 si\u0119 o prawdziwej naturze swego gospodarza, Dr. Frank-N-Furtera. To jeden z najbardziej znanych utwor\u00f3w z musicalu, kt\u00f3rego klimat i postaci fascynuj\u0105 widz\u00f3w na ca\u0142ym \u015bwiecie do dzi\u015b." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku Thy Majestie-In God We Trust ( wykonawcy VA z albumu Absolute Power Metal-The Defi" + }, + { + "role": "assistant", + "content": "\"Thy Majestie-In God We Trust\" to utw\u00f3r z albumu \"Absolute Power Metal - The Defi\" wykonawcy VA. Jest to utw\u00f3r zespo\u0142u Thy Majestie, kt\u00f3ry w swojej tw\u00f3rczo\u015bci \u0142\u0105czy melodic i symfonic metal z elementami muzyki klasycznej.\n\nUtw\u00f3r \"In God We Trust\" to utw\u00f3r o szybkim tempie, z\u0142o\u017cony z intensywnych gitarr, sekcji rytmicznej oraz symfonicznych brzmie\u0144, kt\u00f3re charakteryzuj\u0105 ten rodzaj muzyki. Tekstowi utworu towarzyszy zmiana klimatu utworu, od ciemnego i mrocznego pocz\u0105tku do triumfalnego fina\u0142u, co wprowadza s\u0142uchaczy w niezwyk\u0142e wra\u017cenia.\n\nTekst \"In God We Trust\" propaguje wolno\u015b\u0107 i r\u00f3wno\u015b\u0107 wobec wszystkich bez wzgl\u0119du na wyznanie i pochodzenie, podkre\u015blaj\u0105c potrzeb\u0119 jednoczenia si\u0119 na rzecz pokojowego wsp\u00f3\u0142istnienia. Utw\u00f3r przedstawia swoisty manifest wolno\u015bci, m\u00f3wi\u0105c \u017ce wyzwolenie naszych serc podejmujemy dzi\u015b.\n\n\"In God We Trust\" to utw\u00f3r, kt\u00f3ry z pewno\u015bci\u0105 doceniaj\u0105 mi\u0142o\u015bnicy gatunku jakim jest symfoniczny power metal i mo\u017ce zainspirowa\u0107 s\u0142uchaczy do pozytywnych dzia\u0142a\u0144 na rzecz wolno\u015bci i \u017cycia bez granic." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku The Sybilline Sisterhood wykonawcy Murray Gold z albumu DOCTOR WHO SERIES 4" + }, + { + "role": "assistant", + "content": "\"The Sybilline Sisterhood\" to instrumentalna kompozycja stworzona przez brytyjskiego kompozytora Muarraya Golda, kt\u00f3ra pojawi\u0142a si\u0119 na albumie \"Doctor Who: Series 4 - The Specials\". To utw\u00f3r, kt\u00f3ry zosta\u0142 u\u017cyty podczas trzeciego odcinka specjalnego 4 serii \"Doctor Who\" - \"The Fires of Pompeii\".\n\nKawa\u0142ek ten charakteryzuje si\u0119 nios\u0105cymi si\u0119 melodi\u0105 granych na skrzypcach i flecie, dodatkowo wzbogaconymi o pulsuj\u0105ce bity oraz romantyczne partie fortepianowe. Ca\u0142o\u015b\u0107 jest zestawiona w spos\u00f3b, kt\u00f3ry idealnie pasuje do napi\u0119tej i dramatycznej atmosfery odcinka, tym samym dodaj\u0105c emocjonalnego napi\u0119cia ca\u0142o\u015bci.\n\nKompozytor, Muarray Gold, zas\u0142yn\u0105\u0142 mi\u0119dzy innymi w\u0142a\u015bnie ze swojej pracy przy uk\u0142adaniu muzyki do serialu \"Doctor Who\", du\u017ca jego cz\u0119\u015b\u0107 charakteryzuje si\u0119 w\u0142a\u015bnie podobnym brzmieniem do tego utworu. \"The Sybilline Sisterhood\" to utw\u00f3r, kt\u00f3ry \u015bwietnie wprowadza w klimat serialu, a jednocze\u015bnie sam w sobie jest pi\u0119kn\u0105 i klimatyczn\u0105 kompozycj\u0105 instrumentaln\u0105." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku Samurai wykonawcy Juno Reactor z albumu Samurai" + }, + { + "role": "assistant", + "content": "\"Samurai\" to utw\u00f3r z albumu o tym samym tytule, autorstwa brytyjskiego duetu Juno Reactor. To kompozycja, kt\u00f3ra charakteryzuje si\u0119 mrocznym, pulsuj\u0105cym brzmieniem, z wp\u0142ywami muzyki techno, trance i industrial. \n\nTekstowe partie utworu s\u0105 pe\u0142ne nawi\u0105za\u0144 do japo\u0144skiego kulturowego dziedzictwa, a klimat utworu wprowadza s\u0142uchaczy w specyficzn\u0105 atmosfer\u0119 wojennej japo\u0144skiej epoki. \"Samurai\" to z pewno\u015bci\u0105 jeden z najbardziej rozpoznawalnych utwor\u00f3w w dorobku Juno Reactor, a jednocze\u015bnie jedna z najbardziej wp\u0142ywowych kompozycji w muzyce elektronicznej.\n\nDzi\u0119ki unikalnemu po\u0142\u0105czeniu elektronicznych i akustycznych d\u017awi\u0119k\u00f3w, \"Samurai\" wprowadza s\u0142uchaczy w ducha walki, wojowniczej pasji oraz kulturowego dziedzictwa Japonii. To kawa\u0142ek, kt\u00f3ry z pewno\u015bci\u0105 podbije serca mi\u0142o\u015bnik\u00f3w muzyki elektronicznej oraz fan\u00f3w kultury japo\u0144skiej." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku It's A Hard Life wykonawcy Queen z albumu Ballads" + }, + { + "role": "assistant", + "content": "\"It's a Hard Life\" to utw\u00f3r brytyjskiego zespo\u0142u rockowego Queen, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"The Works\" w 1984 roku oraz na ich sk\u0142adance \"Queen Ballads\" z 1999 roku. Jest to piosenka o \u015brednim tempie, z\u0142o\u017cona z melodyjnych partii wokalnych Freddiego Mercury'ego oraz charakterystycznego brzmienia gitary Briana Maya.\n\nTekst utworu opowiada histori\u0119 trudnego \u017cycia, w kt\u00f3rym wiele rzeczy nie idzie po naszej my\u015bli, zdradza nadziej\u0119 na lepsze jutro. Refren zawiera pozytywn\u0105 i optimistyczn\u0105 wiadomo\u015b\u0107, zach\u0119caj\u0105c\u0105 do walki o swoje marzenia, niezale\u017cnie od przeciwno\u015bci losu.\n\n\"It's a Hard Life\" to utw\u00f3r o nastroju romantycznym i nostalgiczny, kt\u00f3ry wyr\u00f3\u017cnia si\u0119 na tle innych znanych utwor\u00f3w Queen. To jedna z wielu ballad, kt\u00f3re zapisa\u0142y si\u0119 na sta\u0142e w historii ameryka\u0144skiego rocka, dzi\u0119ki czemu utw\u00f3r pozostaje popularny w\u015br\u00f3d fan\u00f3w muzyki na ca\u0142ym \u015bwiecie." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku Leeds United wykonawcy Amanda Palmer z albumu Who Killed Amanda Palmer" + }, + { + "role": "assistant", + "content": "\"Leeds United\" to utw\u00f3r z albumu \"Who Killed Amanda Palmer\" ameryka\u0144skiej wokalistki, kompozytorki i autorki tekst\u00f3w Amandy Palmer. Jest to piosenka z gatunku indie rock, opowiadaj\u0105ca o porwaniu i zamordowaniu brytyjskiego kibica pi\u0142karskiego. \n\nTekst piosenki sk\u0142ada si\u0119 z opis\u00f3w brutalnych detal\u00f3w morderstwa oraz refleksji nad bezsensowno\u015bci\u0105 przemocy i \u015bmierci. Palmer prowadzi s\u0142uchacza przez sceny zwi\u0105zane z pi\u0142k\u0105 no\u017cn\u0105 - od ekscytacji przed meczem, po chaos po zako\u0144czeniu spotkania i konfrontacj\u0119 kibic\u00f3w. \n\n\"Leeds United\" to utw\u00f3r, kt\u00f3ry zaanga\u017cuje s\u0142uchacza w histori\u0119 morderstwa i sk\u0142oni do refleksji nad trudnymi kwestiami zwi\u0105zanymi z przemoc\u0105 i konfliktami. Brzmienie utworu jest charakterystyczne dla tw\u00f3rczo\u015bci Palmer, \u0142\u0105cz\u0105cej rockowe aran\u017cacje z alternatywnym, a czasem eksperymentalnym podej\u015bciem do muzyki." + }, + { + "role": "user", + "content": "Kwadraty:Opowiedz mi o kawa\u0142ku Can't Loose The Book wykonawcy Balance Of Power z albumu When the worlds fall down (Japanese)" + }, + { + "role": "assistant", + "content": "Niestety, nie znalaz\u0142em informacji na temat utworu \"Can't Loose The Book\" wykonawcy Balance Of Power z albumu \"When the worlds fall down\". Mo\u017ce to b\u0142\u0105d w pisowni lub nie\u015bcis\u0142o\u015b\u0107 w informacjach, kt\u00f3rych u\u017cy\u0142e\u015b. Prosz\u0119 sprawdzi\u0107 jeszcze raz dok\u0142adnie tytu\u0142 utworu i albumu lub poda\u0107 wi\u0119cej informacji, a ja postaram si\u0119 na ich podstawie odpowiedzie\u0107 na Twoje pytanie." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku The End of Time / Wilf's Wiggle wykonawcy Murray Gold z albumu Doctor Who - Series 4 - The Specials (Original Television Soundtrack)" + }, + { + "role": "assistant", + "content": "\"The End of Time\" i \"Wilf's Wiggle\" to dwa utwory autorstwa brytyjskiego kompozytora Muarraya Golda, kt\u00f3re pojawi\u0142y si\u0119 na albumie \"Doctor Who - Series 4 - The Specials (Original Television Soundtrack)\", na kt\u00f3rym znalaz\u0142a si\u0119 muzyka z serii specjalnych odcink\u00f3w 4 sezonu serialu \"Doctor Who\".\n\n\"The End of Time\" to utw\u00f3r, kt\u00f3ry pojawi\u0142 si\u0119 w dwucz\u0119\u015bciowym odcinku \"The End of Time\", b\u0119d\u0105cym jednym z najwa\u017cniejszych moment\u00f3w w historii \"Doctor Who\". Charakteryzuje si\u0119 on ci\u0119\u017ckim, mrocznym brzmieniem, s\u0142yszalnym w tle modulacji sygna\u0142\u00f3w d\u017awi\u0119kowych typowych dla serialu, a tak\u017ce mocnym ch\u00f3ralnym wokalem. Utw\u00f3r ten bardzo dobrze oddaje napi\u0119cie i dramaturgi\u0119 akcji oraz atmosfer\u0119 ko\u0144ca epoki.\n\n\"Wilf's Wiggle\" to natomiast utw\u00f3r, kt\u00f3ry pojawi\u0142 si\u0119 w tym samym odcinku serialu, ale w przeciwie\u0144stwie do \"The End of Time\", ma zupe\u0142nie inny charakter. Jest to utw\u00f3r bardziej radosny i optymistyczny, kt\u00f3ry wprowadza s\u0142uchacza w lekkim klimacie beztroskiej zabawy. W szczeg\u00f3lno\u015bci, nawi\u0105zuje do weso\u0142ych i zabawnych moment\u00f3w, jakie pojawiaj\u0105 si\u0119 w odcinku dzi\u0119ki" + }, + { + "role": "user", + "content": "Pat vel Je\u017c Bojowy [Wolfram]:Opowiedz mi o kawa\u0142ku The Omega wykonawcy Sefa z albumu Qlimax The Source" + }, + { + "role": "assistant", + "content": "\"The Omega\" to utw\u00f3r stworzony przez holenderskiego producenta muzycznego o pseudonimie Sefa, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Qlimax The Source\". Jest to hardstyle'owy kawa\u0142ek, kt\u00f3ry charakteryzuje si\u0119 mocnym brzmieniem, ci\u0119\u017ckimi bitemi i intensywnymi elektronicznymi d\u017awi\u0119kami.\n\nTekst utworu sk\u0142ada si\u0119 tylko z jednego s\u0142owa \u2013 \"omega\", kt\u00f3re jest powtarzane przez ca\u0142y utw\u00f3r. S\u0142owo to, jako ostatnia litera greckiego alfabetu, odnosi si\u0119 do ko\u0144ca i symbolizuje zako\u0144czenie czego\u015b wa\u017cnego lub kluczowego. Muzyka i tekst tego utworu zatem wprowadzaj\u0105 s\u0142uchaczy w nastrojowy klimat i nadaj\u0105 utworowi wyr\u00f3\u017cniaj\u0105cy charakter.\n\n\"The Omega\" to utw\u00f3r, kt\u00f3ry odznacza si\u0119 ci\u0119\u017ckim i przejmuj\u0105cym brzmieniem, idealnym do s\u0142uchania na ch\u00f3rzystki i imprezy z muzyk\u0105 hardstyle. To jedna z najmocniejszych i najbardziej wpadaj\u0105cych w ucho kompozycji z albumu \"Qlimax The Source\", kt\u00f3ry cieszy si\u0119 du\u017cym zainteresow" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Wiosna nie ominie ci\u0119 wykonawcy Antonina Krzyszton z albumu Turkusowy st\u00f3\u0142" + }, + { + "role": "assistant", + "content": "\"Wiosna nie ominie ci\u0119\" to piosenka Antoniny Krzyszton, kt\u00f3ra pojawi\u0142a si\u0119 na jej albumie \"Turkusowy st\u00f3\u0142\" wydanym w 1987 roku. Jest to utw\u00f3r, kt\u00f3ry charakteryzuje si\u0119 mi\u0119kk\u0105 i delikatn\u0105 melodyj\u0105 w po\u0142\u0105czeniu z pi\u0119knym wokalem Antoniny Krzyszton.\n\nTekst utworu opowiada o nadchodz\u0105cej wio\u015bnie, kt\u00f3rej nie da si\u0119 unikn\u0105\u0107, a kt\u00f3ra zwiastuje nadziej\u0119 i nowy pocz\u0105tek. Refren, w kt\u00f3rym \u015bpiewa si\u0119 \"Wiosna nie ominie ci\u0119, nawet gdy trzeba ci cierpliwo\u015bci mie\u0107\" podkre\u015bla wytrwa\u0142o\u015b\u0107 w d\u0105\u017ceniu do celu z poczuciem, \u017ce zmiana nast\u0105pi i przyniesie z sob\u0105 dobre rzeczy.\n\n\"Wiosna nie ominie ci\u0119\" to pi\u0119kna, klimatyczna i optymistyczna piosenka, kt\u00f3ra doskonale oddaje pozytywne emocje towarzysz\u0105ce ko\u0144cowi zimy i nadej\u015bciu wiosny. To jedna z najbardziej znanych i lubianych piosenek w dorobku Antoniny Krzyszton i cieszy si\u0119 popularno\u015bci\u0105 w\u015br\u00f3d fan\u00f3w muzyki polskiej." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Jak to B\u00f3g wykonawcy Andrzej Poniedzielski z albumu SzlafRock & roll" + }, + { + "role": "assistant", + "content": "\"Jak to B\u00f3g\" to utw\u00f3r, kt\u00f3ry znalaz\u0142 si\u0119 na albumie \"SzlafRock & roll\" Andrzeja Poniedzielskiego z 1995 roku. Jest to utw\u00f3r z gatunku rockowo-komediowego, charakteryzuj\u0105cy si\u0119 weso\u0142ym tekstem oraz melodyjnym, \u0142atwo wpadaj\u0105cym w ucho refrenem.\n\nW tek\u015bcie piosenki Andrzej Poniedzielski porusza kwesti\u0119 z\u0142o\u017cono\u015bci ludzkiego \u017cycia i zwi\u0105zanymi z nim trudno\u015bciami, a jednocze\u015bnie k\u0142adzie nacisk na pozytywne aspekty \u017cycia, takie jak rodzina, przyja\u017a\u0144 i solidarno\u015b\u0107 mi\u0119dzy lud\u017ami. W tek\u015bcie pojawiaj\u0105 si\u0119 tak\u017ce odniesienia do tematyki religijnej, a w refrenie s\u0142yszymy powtarzaj\u0105ce si\u0119 pytanie: \"Jak to B\u00f3g da\u0142 n\u00f3\u017cki, a Mr\u00f3wka chodzi, a Lech Kaczy\u0144ski rz\u0105dzi?\"\n\n\"Jak to B\u00f3g\" to utw\u00f3r z lekkim, humorystycznym przes\u0142aniem, kt\u00f3ry na pierwszy rzut oka mo\u017ce wydawa\u0107 si\u0119 spor\u0105 odmian\u0105 od utwor\u00f3w rockowych. Jednak utw\u00f3r ten przyci\u0105ga s\u0142uchaczy swoim dobrym humorem, pozytywnym przes\u0142aniem i chwytliw\u0105 melodi\u0105, kt\u00f3ra \u0142atwo wpada w ucho i pozostaje w pami\u0119ci na d\u0142u\u017cej." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Nie ma jak u mamy wykonawcy Wojciech M\u0142ynarski z albumu Polska mi\u0142o\u015b\u0107" + }, + { + "role": "assistant", + "content": "\"Nie ma jak u mamy\" to utw\u00f3r autorstwa Wojciecha M\u0142ynarskiego, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Polska mi\u0142o\u015b\u0107\" w 1983 roku. Jest to lekka i humorystyczna piosenka, kt\u00f3ra z pewno\u015bci\u0105 zachwyci ka\u017cdego, kto kiedykolwiek skorzysta\u0142 z domowych obiad\u00f3w.\n\nTekst utworu opowiada o przepi\u0119knym dniu, kt\u00f3ry straci\u0142 sw\u00f3j urok, gdy okaza\u0142o si\u0119, \u017ce domownikom brakuje domowego jedzenia. S\u0142uchacz poznaje zwyczaje, kt\u00f3re towarzysz\u0105 rodzinie podczas wsp\u00f3lnych posi\u0142k\u00f3w, a przede wszystkim dowiaduje si\u0119, jakie przysmaki swoj\u0105 urocz\u0105 prezencj\u0105 potrafi\u0105 o\u017cywi\u0107 wspania\u0142e chwile rodzinne.\n\n\"Nie ma jak u mamy\" to lekka piosenka, w kt\u00f3rej M\u0142ynarski \u015bwietnie po\u0142\u0105czy\u0142 elementy polskiej tradycji z melodi\u0105 przyci\u0105gaj\u0105c\u0105 do siebie uwag\u0119 ka\u017cdego s\u0142uchacza. To utw\u00f3r, kt\u00f3ry z pewno\u015bci\u0105 budzi pozytywne emocje i sprzyja odpr\u0119\u017ceniu, a jednocze\u015bnie celebruje polskie rodzinne tradycje zgromadzone wok\u00f3\u0142 wsp\u00f3lnego sto\u0142u." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Zata\u0144czymy na mo\u015bcie wykonawcy Czerwony Tulipan z albumu Olsztyn kocham" + }, + { + "role": "assistant", + "content": "\"Zata\u0144czymy na mo\u015bcie\" to utw\u00f3r zespo\u0142u Czerwony Tulipan, kt\u00f3ry pojawi\u0142 si\u0119 na albumie \"Olsztyn kocham\" w 1993 roku. Jest to piosenka o charakterze ludowym, sk\u0142adaj\u0105ca si\u0119 z przyjemnej, melodyjnej wokalnej partii oraz prostego, ale zach\u0119caj\u0105cego do ta\u0144ca rytmu.\n\nTekst utworu jest pe\u0142en nostalgii i romantyzmu, opowiadaj\u0105c histori\u0119 mi\u0142osn\u0105, kt\u00f3ra dzieje si\u0119 na mo\u015bcie. Piosenk\u0119 charakteryzuje prostota i wyj\u0105tkowo\u015b\u0107, kt\u00f3ra pozwala jej wybi\u0107 si\u0119 z t\u0142umu komercyjnej muzyki z tego czasu.\n\nCzerwony Tulipan to zesp\u00f3\u0142 wyr\u00f3\u017cniaj\u0105cy si\u0119 na polskiej scenie muzycznej, ciesz\u0105cy si\u0119 du\u017c\u0105 popularno\u015bci\u0105 zw\u0142aszcza w latach 90. Ich tw\u00f3rczo\u015b\u0107 to po\u0142\u0105czenie r\u00f3\u017cnych gatunk\u00f3w muzycznych, co sprawia, \u017ce ich utwory s\u0105 wyj\u0105tkowo bogate i ciekawe.\n\n\"Zata\u0144czymy na mo\u015bcie\" to pi\u0119kna, nostalgiczna piosenka, kt\u00f3ra wprowadza s\u0142uchaczy w romantyczny nastr\u00f3j oraz sk\u0142ania do refleksji nad uczuciami i warto\u015bciami, kt\u00f3re mo\u017cna odnale\u017a\u0107 w prostych rzeczach." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Klas\u00f3wka z fragmentu wolno\u015bci wykonawcy Krzysztof Daukszewicz z albumu 44 x" + }, + { + "role": "assistant", + "content": "Przepraszam, ale nie jestem w stanie udzieli\u0107 informacji na temat utworu \"Klas\u00f3wka z fragmentu wolno\u015bci\" wykonawcy Krzysztofa Daukszewicza, poniewa\u017c ten utw\u00f3r nie istnieje. By\u0107 mo\u017ce pope\u0142ni\u0142(a)\u015b liter\u00f3wk\u0119 albo jest to utw\u00f3r, kt\u00f3ry wyst\u0119puje pod innym tytu\u0142em. Je\u015bli podasz dok\u0142adniejsze informacje lub skorygujesz b\u0142\u0119dy to postaram si\u0119 udzieli\u0107 jak najbardziej precyzyjnej odpowiedzi." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Wsta\u0144 i id\u017a wykonawcy Antonina Krzyszton z albumu Czekaj" + }, + { + "role": "assistant", + "content": "\"Wsta\u0144 i id\u017a\" to utw\u00f3r autorstwa Antoniny Krzyszton, kt\u00f3ry pojawi\u0142 si\u0119 na jej albumie zatytu\u0142owanym \"Czekaj\". Utw\u00f3r charakteryzuje si\u0119 spokojnym, melodyjnym brzmieniem, pe\u0142nym optymizmu i pozytywnej energii.\n\nTekst piosenki opowiada o zach\u0119cie do wyj\u015bcia z marazmu i rutyny codziennego \u017cycia, do odwagi i determinacji w walce o swoje cele. Antonina Krzyszton w spos\u00f3b lekki i przyst\u0119pny przedstawia przes\u0142anie, \u017ce ka\u017cdy mo\u017ce dokona\u0107 zmiany w swoim \u017cyciu, potrzeba tylko wzi\u0119cia odpowiedzialno\u015bci za swoje wybory.\n\n\"Wsta\u0144 i id\u017a\" to utw\u00f3r, kt\u00f3ry z pewno\u015bci\u0105 dodaje optymizmu i motywacji do dzia\u0142ania s\u0142uchaczowi. Melodyjne brzmienie i pozytywny przekaz sprawiaj\u0105, \u017ce kompozycja ta idealnie nadaje si\u0119 na depremiuj\u0105ce momenty w \u017cyciu, by zainspirowa\u0107 i u\u015bwiadomi\u0107 s\u0142uchaczowi, \u017ce trudne czasem s\u0105 tylko przej\u015bciowe." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Majster Bieda wykonawcy Wolna Grupa Bukowina z albumu Kolory Muzyki" + }, + { + "role": "assistant", + "content": "\"Majster Bieda\" to utw\u00f3r folkowy polskiej grupy Wolna Grupa Bukowina, pochodz\u0105cy z albumu \"Kolory Muzyki\" z 1998 roku. Jest to adaptacja ludowej pie\u015bni, kt\u00f3ra w oryginale opowiada o biednym majstrze, kt\u00f3remu nie wiedzie si\u0119 w pracy, nie ma pieni\u0119dzy na codzienne potrzeby, a jego jedynym schronieniem s\u0105 bezdomne psy. \n\nW wykonaniu Wolnej Grupy Bukowina, \"Majster Bieda\" zyskuje now\u0105 aran\u017cacj\u0119, charakterystyczn\u0105 dla muzyki folkowej. Piosenka jest pe\u0142na sentymentalnej refleksji nad przemijaniem i trudami codziennego \u017cycia, a jednocze\u015bnie nios\u0105ca nadziej\u0119 na lepsze jutro.\n\nW warstwie muzycznej utw\u00f3r charakteryzuje si\u0119 prostymi aran\u017cacjami ludowych instrument\u00f3w, przede wszystkim skrzypiec i akordeonu, co dodaje mu rustykalnego klimatu. \"Majster Bieda\" jest jednym z najbardziej znanych i cenionych utwor\u00f3w Wolnej Grupy Bukowina, kt\u00f3ry po latach nadal zaskakuje swoj\u0105 uniwersalno\u015bci\u0105 i aktualno\u015bci\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Elegancka piosenka o szcz\u0119\u015bciu wykonawcy Andrzej Poniedzielski z albumu SzlafRock & roll" + }, + { + "role": "assistant", + "content": "\"Elegancka piosenka o szcz\u0119\u015bciu\" to utw\u00f3r, kt\u00f3ry znalaz\u0142 si\u0119 na albumie \"SzlafRock & roll\" Andrzeja Poniedzielskiego - znanej postaci polskiej kultury. Utw\u00f3r ten \u0142\u0105czy w sobie charakterystyczne dla muzyki Poniedzielskiego zabawne, pogodne liryczne style z subtelno\u015bci\u0105 i przes\u0142aniem o naturze szcz\u0119\u015bcia.\n\nW swojej kompozycji Poniedzielski wykorzystuje subtelne, melodyjne i nostalgiczne partie na gitarze akustycznej ze swoim niezwyk\u0142ym tekstem. W piosence tej Por\u00f3wnuje wszystkich ludzi, pidkre\u015blaj\u0105c, \u017ce wa\u017cne jest by zdawa\u0107 sobie spraw\u0119 z tego, co sprawia nam rado\u015b\u0107 i szcz\u0119\u015bcie. \n\n\"Elegancka piosenka o szcz\u0119\u015bciu\" to wspania\u0142a piosenka, kt\u00f3ra przypomina nam o rado\u015bciach z mniej skomplikowanych i codziennych rzeczy. Utw\u00f3r cechuje si\u0119 subtelno\u015bci\u0105 i przytuln\u0105 aur\u0105, a tak\u017ce dotyka wa\u017cnych temat\u00f3w z humorem i wra\u017cliwo\u015bci\u0105. To uczciwa i wyra\u017ana pie\u015b\u0144, kt\u00f3ra inspiruje s\u0142uchaczy do bardziej pozytywnego i szcz\u0119\u015bliwego podej\u015bcia do \u017cycia." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Zabiegani, uwik\u0142ani wykonawcy Czerwony Tulipan z albumu Jedyne co mam" + }, + { + "role": "assistant", + "content": "\"Zabiegani, uwik\u0142ani\" to utw\u00f3r Czerwonych Tulipan\u00f3w z albumu \"Jedyne co mam\". Jest to piosenka z gatunku folk rock, opowiadaj\u0105ca o zabieganych i skomplikowanych ludzkich relacjach w \u017cyciu codziennym. \n\nTekst utworu sk\u0142ada si\u0119 z refleksji nad zat\u0142oczeniem, brakiem czasu i poczucia pogoni za sukcesem oraz potrzeby zatrzymania si\u0119, by zastanowi\u0107 si\u0119 nad sensem poszczeg\u00f3lnych dzia\u0142a\u0144. Piosenka porusza kwestie zwi\u0105zane z potrzeb\u0105 oddechu, szczero\u015bci, prawdziwego kontaktu mi\u0119dzyludzkiego i umiej\u0119tno\u015bci cieszenia si\u0119 drobnymi przyjemno\u015bciami.\n\n\"Zabiegani, uwik\u0142ani\" to utw\u00f3r oboj\u0119tny do wieku i czasu, kt\u00f3ry przemawia przede wszystkim do ludzkich warto\u015bci i d\u0105\u017ce\u0144. Brzmienie piosenki sk\u0142ada si\u0119 z mi\u0119kkich, melodyjnych d\u017awi\u0119k\u00f3w, kt\u00f3rych dynamizm wzrasta wraz z narracj\u0105 tekstu. To jedna z wielu piosenek, kt\u00f3ra zyska\u0142a uznanie s\u0142uchaczy w skali kraju, dzi\u0119ki swojej uniwersalno\u015bci i niebanalnym tre\u015bciom." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Wiatr od Klimczoka wykonawcy Stan Borys z albumu The Best - Spacer dzik\u0105 pla\u017c\u0105" + }, + { + "role": "assistant", + "content": "\"Wiatr od Klimczoka\" to utw\u00f3r z repertuaru polskiego piosenkarza i kompozytora Stanis\u0142awa Guzika, bardziej znany pod swoim artystycznym pseudonimem Stan Borys. Utw\u00f3r ten pochodzi z albumu \"The Best - Spacer dzik\u0105 pla\u017c\u0105\" i cieszy si\u0119 du\u017c\u0105 popularno\u015bci\u0105 i uznaniem w Polsce.\n\nUtw\u00f3r \"Wiatr od Klimczoka\" charakteryzuje si\u0119 melodyjnym wokalem Borysa oraz \u0142agodnym, gitarowym brzmieniem. Tekst piosenki opowiada o ulubionej g\u00f3rskiej \u015bcie\u017cce, kt\u00f3r\u0105 Borys uwielbia przemierza\u0107. Stan Borys, kt\u00f3ry w swojej tw\u00f3rczo\u015bci cz\u0119sto korzysta\u0142 z motyw\u00f3w natury i krajobrazu, w tym utworze ukazuje pi\u0119kno g\u00f3rskiego szlaku i oddaje ho\u0142d g\u00f3rom.\n\n\"Wiatr od Klimczoka\" to utw\u00f3r, kt\u00f3ry przyci\u0105ga uwag\u0119 swoj\u0105 prostot\u0105 i pi\u0119knym wykonaniem. Utw\u00f3r ten jest uwa\u017cany za jedn\u0105 z najlepszych piosenek w repertuarze Stanis\u0142awa Guzika, kt\u00f3ry pozostaje jednym z najwa\u017cniejszych artyst\u00f3w polskiej muzyki piosenkowej." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Zata\u0144czymy na mo\u015bcie wykonawcy Czerwony Tulipan z albumu Olsztyn kocham" + }, + { + "role": "assistant", + "content": "\"Zata\u0144czymy na mo\u015bcie\" to folkowa piosenka pochodz\u0105ca z albumu \"Olsztyn kocham\" polskiej grupy muzycznej Czerwony Tulipan. Utw\u00f3r charakteryzuje si\u0119 melodyjnym brzmieniem, typowym dla muzyki folkowej oraz charakterystycznym, porywaj\u0105cym rytmem.\n\nTekst piosenki opowiada o niesamowitym uczuciu, jakie towarzyszy ludziom, kt\u00f3rzy zata\u0144cz\u0105 przez chwil\u0119 na mo\u015bcie. Utw\u00f3r porusza tak\u017ce tematyk\u0119 mi\u0142o\u015bci i pozytywnych emocji zwi\u0105zanych z nimi.\n\n\"Zata\u0144czymy na mo\u015bcie" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Bukowina I wykonawcy Wolna Grupa Bukowina z albumu Z\u0142ota Kolekcja" + }, + { + "role": "assistant", + "content": "\"Bukowina I\" to utw\u00f3r pochodz\u0105cy z albumu \"Rzekoma I\" polskiej grupy muzycznej Wolna Grupa Bukowina. Utw\u00f3r ten pojawi\u0142 si\u0119 r\u00f3wnie\u017c na sk\u0142adance \"Z\u0142ota Kolekcja\". Jest to utw\u00f3r o charakterze balladowym, o z\u0142o\u017conej melodyjnej strukturze.\n\nTekst piosenki odnosi si\u0119 do historii Bukowiny Tatrza\u0144skiej - jednej z najpi\u0119kniejszych krain w Polsce, kt\u00f3ra sta\u0142a si\u0119 azylem dla uchod\u017ac\u00f3w narod\u00f3w Galicji podczas pierwszej wojny \u015bwiatowej. W tek\u015bcie piosenki zosta\u0142a uchwycona historia tego regionu, przyrody oraz historie dawnych mieszka\u0144c\u00f3w i w\u0119drowc\u00f3w.\n\nW utworze w zwyk\u0142ych s\u0142owach ukazane s\u0105 realia \u017cycia ludzi mieszkaj\u0105cych w g\u00f3rach, z pejza\u017cami i natur\u0105 zaprezentowanymi w spos\u00f3b bardzo poetycki. Klimat utworu tworzy tak\u017ce charakterystyczna i skomplikowana melodia, uzupe\u0142niona o partie g\u0119stego brzmienia gitar oraz instrument\u00f3w perkusyjnych. \n\n\"Bukowina I\" to utw\u00f3r, kt\u00f3ry \u0142\u0105czy w sobie inspiracje kultur\u0105 i tradycj\u0105 oferuj\u0105c pi\u0119kny, nostalgiczny utw\u00f3r z pozostaj\u0105cym w pami\u0119ci oraz unikalnym brzmieniem. To jedna z charakterystycznych piosenek grupy Wolna Grupa Bukowina, kt\u00f3ra zyska\u0142a uznanie i szacunek w\u015br\u00f3d mi\u0142o\u015bnik\u00f3w polskiej muzyki folkowej." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Pigs (Three Different Ones) wykonawcy Pink Floyd z albumu Animals" + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Vosstanie Roda wykonawcy Arkona z albumu Vo Slavu Velikim" + }, + { + "role": "assistant", + "content": "\"Pigs (Three Different Ones)\" to utw\u00f3r rockowej grupy Pink Floyd pochodz\u0105cy z albumu \"Animals\", kt\u00f3ry ukaza\u0142 si\u0119 w 1977 roku. Jest to jedna z najbardziej znanych piosenek z albumu \"Animals\" i w og\u00f3le z ca\u0142ej dyskografii zespo\u0142u. \n\nTekst utworu opowiada o trzech r\u00f3\u017cnych osobach, z kt\u00f3rych ka\u017cda reprezentuje inny typ cz\u0142owieka. Tekst jest krytyk\u0105 zachowa\u0144 cz\u0142owieka wobec innych ludzi i przyrody, a tak\u017ce drobiazgowym opisem cech i zachowa\u0144, kt\u00f3re w\u0142a\u015bnie to typuj\u0105. Przy tym Pink Floyd r\u00f3wnie\u017c niuansowo opisuj\u0105 zwierz\u0119cy i \u015bwi\u0144ski charakter takich ludzkich postaw.\n\nLiczne partie instrumentalne, w tym sol\u00f3wki Davida Gilmoura, wprowadzaj\u0105 wyj\u0105tkowy klimat utworu. Charakterystyczne brzmienie gitary, fortepianu oraz partii saksofonu sprawia, \u017ce utw\u00f3r ten idealnie wpisuje si\u0119 w stylistyk\u0119 Pink Floyd, z jednoczesnym naciskiem na spo\u0142eczno-polity" + }, + { + "role": "assistant", + "content": "\"Vosstanie Roda\" to utw\u00f3r zespo\u0142u Arkona, pochodz\u0105cy z albumu \"Vo Slavu Velikim\" z 2005 roku. Zesp\u00f3\u0142 Arkona to rosyjska grupa muzyczna, \u0142\u0105cz\u0105ca elementy folk metalu i black metalu.\n\nUtw\u00f3r \"Vosstanie Roda\" charakteryzuje si\u0119 mrocznym brzmieniem, growlingiem oraz charakterystycznymi wokalami folkowymi Mariny \"Scream\" Sobolewskiej. Tekst utworu opowiada o wierzeniach s\u0142owia\u0144skich oraz o historii i dumie s\u0142owia\u0144skiego narodu.\n\nW warstwie muzycznej utw\u00f3r charakteryzuje si\u0119 g\u0119stym brzmieniem, z\u0142o\u017conymi partiami perkusji, gitary oraz d\u017awi\u0119kami folkowych instrument\u00f3w, takich jak dudy i skrzypce. Arkona jest znana z unikalnego brzmienia, kt\u00f3re \u0142\u0105czy r\u00f3\u017cne style muzyczne.\n\n\"Vosstanie Roda\" to utw\u00f3r, kt\u00f3ry \u0142\u0105czy w sobie mroczne i mocne brzmienie z poetyckimi tekstami, kt\u00f3re poruszaj\u0105 wa\u017cne tematy zwi\u0105zane z kultur\u0105 i histori\u0105 S\u0142owian. To jedna z najbardziej znanych i cenionych piosenek zespo\u0142u Arkona, kt\u00f3ra przyci\u0105ga uwag\u0119 nie tylko fan\u00f3w muzyki folk metalowej, ale tak\u017ce ludzi zainteresowanych histori\u0105 i kultur\u0105 S\u0142owia\u0144sk\u0105." + }, + { + "role": "user", + "content": "Hammer - The Bartender:Opowiedz mi o kawa\u0142ku Coffin Fodder wykonawcy Cradle Of Filth z albumu Nymphetamine" + }, + { + "role": "assistant", + "content": "\"Coffin Fodder\" to utw\u00f3r z albumu \"Nymphetamine\" brytyjskiego zespo\u0142u Cradle Of Filth. To death-metalowe brzmienie z elementami black metalu i gotyckiego rocka. \n\nTekst utworu to prowokacyjne po\u0142\u0105czenie j\u0119zyka poetyckiego z drastycznymi opisami \u015bmierci i ciemn\u0105 estetyk\u0105, charakterystyczn\u0105 dla tw\u00f3rczo\u015bci Cradle Of Filth. \n\n\"Coffin Fodder\" opowiada histori\u0119 mordercy zabijaj\u0105cego kobiety i wk\u0142adaj\u0105cego ich cia\u0142a do trumien, aby zaspokoi\u0107 swoje mroczne i okrutne pragnienia. To utw\u00f3r pe\u0142en brutalnych opis\u00f3w, kt\u00f3rych celem jest szokowanie i wywo\u0142anie wstr\u0119tu w s\u0142uchaczu. \n\nPod wzgl\u0119dem muzycznym, \"Coffin Fodder\" charakteryzuje si\u0119 szybkim tempem, ci\u0119\u017ckimi riffami gitary i intensywnymi partiami perkusyjnymi. Utw\u00f3r jest jednym z bardziej ekstremalnych utwor\u00f3w zespo\u0142u Cradle Of Filth, co czyni go atrakcyjnym dla mi\u0142o\u015bnik\u00f3w mocnej i agresywnej muzyki metalowej." + } +] diff --git a/working_copy/radio_commands.py b/working_copy/radio_commands.py new file mode 100644 index 0000000..04d4d35 --- /dev/null +++ b/working_copy/radio_commands.py @@ -0,0 +1,207 @@ +import logging +from discord.ext import commands +import discord +# trunk-ignore(bandit/B404) +import subprocess +import requests +import uuid + +import asyncio +from datetime import datetime +from constants import RADIO_HARBOR_ADDRESS, SKIP_TRACK, FILE_SERVICE_ADDRESS, ADD_TO_PRIO_PLAYLIST, REQUEST_MUSIC, CREATE_PRIO_PLAYLIST, CLEAR_PRIO + +class RadioModule(commands.Cog): + def __init__(self, bot, logger_name): + self.bot = bot + self.logger = logging.getLogger(logger_name) + + + @commands.hybrid_command( + name="skip_track", + description="Przeskocz kawałek w radiu", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def skip_track(self, ctx): + async with ctx.typing(): + allowed = False + for role in ctx.author.roles: + if role.name in ("Thane", "Jarl"): + allowed = True + if not allowed: + await ctx.send("Łapy precz od radia") + else: + coroutine = asyncio.to_thread( + requests.get, + f"{RADIO_HARBOR_ADDRESS}{SKIP_TRACK}", + timeout=360, + ) + result = await coroutine + self.logger.info("Done %s", result) + await ctx.send("Zrobione szefie!") + + + @commands.hybrid_command( + name="zrestartuj_radio", + description="Komenda ktora uruchamia radio ponownie jakby się zawiesiło", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def zrestartuj_radio(self, ctx): + async with ctx.typing(): + allowed = False + for role in ctx.author.roles: + if role.name in ("Thane", "Jarl"): + allowed = True + if not allowed: + await ctx.send("Łapy precz od radia") + else: + retcode = subprocess.run( + "/home/pi/Conjurer/restart_radio.sh", + # trunk-ignore(bandit/B603) + shell=False, + check=False, + capture_output=True, + ) + self.logger.info("Wynik: %s", retcode) + + @commands.hybrid_command( + name="dodaj_do_ulubionych", + description="Dodaje do listy ulubionych w radiu", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def dodaj_do_ulubionych(self, 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 + """ + async with ctx.typing(): + self.logger.info("Zaczynam szukać timestamp %s", datetime.now()) + + dlugosc_playlisty = ctx.message.content.split()[1] + word_list = ctx.message.content.split() + jrequest = { + "lista_slow": word_list, + "dlugosc_plejlisty": dlugosc_playlisty, + "UUID": str(uuid.uuid4()), + } + coroutine = asyncio.to_thread( + requests.post, + f"{FILE_SERVICE_ADDRESS}{ADD_TO_PRIO_PLAYLIST}", + json=jrequest, + timeout=360, + ) + result = await coroutine + self.logger.info("Done %s", result) + await ctx.send("Zrobione szefie!") + + + @commands.hybrid_command( + name="ja_chciol", + description="Dodaje do listy ulubionych w radiu", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def request_radio(self, 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 + """ + async with ctx.typing(): + self.logger.info("Zaczynam szukać timestamp %s", datetime.now()) + + word_list = ctx.message.content.split() + jrequest = { + "lista_slow": word_list, + "UUID": str(uuid.uuid4()), + } + coroutine = asyncio.to_thread( + requests.post, + f"{FILE_SERVICE_ADDRESS}{REQUEST_MUSIC}", + json=jrequest, + timeout=360, + ) + result = await coroutine + self.logger.info("Done %s", result) + await ctx.send("Zrobione szefie!") + + + @commands.hybrid_command( + name="stworz_audycje", + description="Dodaje do listy ulubionych w radiu", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def stworz_audycje(self, 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 + """ + async with ctx.typing(): + self.logger.info("Zaczynam szukać timestamp %s", datetime.now()) + + dlugosc_playlisty = ctx.message.content.split()[1] + word_list = ctx.message.content.split() + jrequest = { + "lista_slow": word_list, + "dlugosc_plejlisty": dlugosc_playlisty, + "UUID": str(uuid.uuid4()), + } + coroutine = asyncio.to_thread( + requests.post, + f"{FILE_SERVICE_ADDRESS}{CREATE_PRIO_PLAYLIST}", + json=jrequest, + timeout=360, + ) + result = await coroutine + self.logger.info("Done %s", result) + await ctx.send("Zrobione szefie!") + + + @commands.hybrid_command( + name="wyczysc_ulubione", + description="Czysci liste ulubionych w radiu", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Thane' , 'Bartender') + async def wyczysc_ulubione(self, 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 + """ + async with ctx.typing(): + coroutine = asyncio.to_thread( + requests.get, + f"{FILE_SERVICE_ADDRESS}{CLEAR_PRIO}", + timeout=360, + ) + result = await coroutine + self.logger.info("Done %s", result) + await ctx.send("Zrobione szefie!") + + +async def setup(bot): + logger = logging.getLogger("discord") + await bot.add_cog(RadioModule(bot, "discord")) + logger.info("Loading raadio commands module done") diff --git a/working_copy/requirements_bot.txt b/working_copy/requirements_bot.txt new file mode 100644 index 0000000..d8d5767 --- /dev/null +++ b/working_copy/requirements_bot.txt @@ -0,0 +1,22 @@ +setuptools +discord +yt_dlp +spotify_dl +spotipy +openai +eyed3 +numpy +pdf2image +PyPDF2 +requests +spotipy +tiktoken +PyNaCl +flask[async] +PyMuPDF +waitress +clickupython +#assemblyai[extras] +#SpeechRecognition +#pendulum +#git+https://github.com/imayhaveborkedit/discord-ext-voice-recv diff --git a/working_copy/settings.json b/working_copy/settings.json new file mode 100644 index 0000000..aef5d82 --- /dev/null +++ b/working_copy/settings.json @@ -0,0 +1,357 @@ +{ + "fabryczka": "Fabryczka to miejsce w Łodzi (SM-Fucktory). Właściciel (PsychoSado) skuszony hajsem razem ze współpracownikiem (Just Cruelem) wzięli na sesję od kobiety jej 'uleglego' męża. Sesja non stop ponad 24h. Sesja w ramach \"kary za zdradę\". Kontakt miał być z kobietą telefoniczny i słowo bezpieczeństwa u niej. W pewnym momencie uległy im się popsuł. Zadzwonili do kobiety. Nie odebrała. Napisali sms. Odpisała że \"Nie on tak zawsze. Kontynuujcie\". Ponoć mimo to zrobili przerwę i skoczyli po bułkę i serek Hochland dla uległego w ramach after/middle care'u. Uległy spierdolił im nago oknem z \"sesji\". Stąd też wzięłą się w Diasporze łódzkiej kwestia serka Hochland. Po całej akcji pan 'Fabryczka' straszył parę prawnikami, tłumacząc że 'stać go na dobrych'. Just_cruel to kumpel fabryczki który też ma na koncie pare swoich akcji. Między innymi jako były partner Czarnej - oskarżał ją o 'kradzież' imprezy oraz szantażował prywatnie.", + "word_reactions": { + "fabryczka\\b": [ + 1103376650487484556, + 10, + 0, + true, + false + ], + "hammer\\b": [ + "Hammer? Chuj o złotym serduszku? Popierdolony w idealnych proporcjach do czystego serduszka? Legendarny zbanowany admin Kinky Little Discord? Legendarny kochanek? \"Bardzo fajny sadysta\":tm: Właściciel \"Domu który czyni pijanym\"? \nNie znam. Nie słyszałem", + 15.0, + 0, + false, + false + ], + "chujzłoty\\b": [ + "Eeeee.... Szefie... ktoś cię woła! Wskazuje na Hammera", + 15.0, + 0, + false, + false + ], + "chuj\\b": [ + "Eeeee.... Szefie... ktoś cię woła! Wskazuje na Hammera", + 15.0, + 0, + false, + false + ], + "jeż\\b": [ + "*Światło gaśnie i cały bar nagle zostaje przemalowany w słoneczniki.* Ki dia... *Światło znowu gaśnie i pojawiają się psychodeliczne animacje* Jeżu. Ogranicz. *Po kolejnym błysku wszystko jest anime chibi style* Swoją... *I wraca do normy* Kreatywność. *Wzdycha* I tak jedyna chemia jaką tu widzę to domestos... *Kręci głową*", + 15.0, + 0, + false, + false + ], + "savil\\b": [ + "*Znikąd zaczyna być słychać muzykę. Conjurer nuci* There must be some way out of here... Said the joker to the thief...", + 15.0, + 0, + false, + false + ], + "zinnejbajki\\b": [ + "ZInnejBajki? Nie wcale nie żywi się krwią. To pogłoski. *Nieudolnie chowa za plecami plastikowy pakiet z dużym napisem AB RH+ do lodówki na drinki*", + 15.0, + 0, + false, + false + ], + "jaeger\\b": [ + "Jaeger? *W tle zaczyna grać opening z Attack on the Titan* Na północy siedzi, na te no... Pierożki bezglutenowe poluje. Odważny chłop. *kiwa głową z szacunkiem jaki tylko łowca może wobec innego łowcy mieć*", + 15.0, + 0, + false, + false + ], + "lena\\b": [ + "Lena? Nie nie miałem NIC wspólnego ze zniknięciem... A czekaj - o którą z osób znikniętych pytasz? *Zaczyna nucić* Boogie ługi, znikną wrógi", + 15.0, + 0, + false, + false + ], + "rafikowy\\b": [ + "Rafikowy? *wyłamuje kostki* Jak masz PROBLEM z tym super dzieciakiem - to masz problem ze mną. I resztą Nocnej Zmiany. *wali pięścią w stół* Ka-pe-wu?", + 15.0, + 0, + false, + false + ], + "shila\\b": [ + "Shila? Instruktorka aerobiku. Takiego nago. Z elektrodą w tyłku... A i robi takie słodkie *roar*. No normalnie aż się chcę odpowiedzieć *kici, kici*", + 15.0, + 0, + false, + false + ], + "mienco\\b": [ + "*Wyciąga kawał krwawiącego króliczego udka* Frecia, Frecia, Frecia... kici, kici, taś taś.... *Światło gaśnie na sekunddę i po tym jak się zapala cały bar jest uwalony krwią* Za każdym razem *wzdycha*", + 15.0, + 0, + false, + false + ], + "jessenia\\b": [ + "Jessenia? Jak przyszła tu w różowej pidżamie z uszkami to nawet nie mrugnąłem okiem. Te oczy.... No ale jakiś debil Ją prowokował mówiąc \"Zdominuj mnie\" Akurat miała dobry humor więc przeżył - ale po 115.0 sekundach był już na \"Tak Pani, przepraszam że zająłem czas Pani\" Respect dla kobitki", + 15.0, + 0, + false, + false + ], + "enterix\\b": [ + "Enterix? Chodząca encyklopedia sprzętu BDSM... Jeśli On o czymś nie słyszał to to nie istnieje.", + 15.0, + 0, + false, + false + ], + "aswerus\\b": [ + "Aswerus? Bardzo miły człowiek. Bardzo się stara być miły. Szacun - ja nawet nie próbuje.", + 15.0, + 0, + false, + false + ], + "szeherezada\\b": [ + "Szeherezada? BDSMowa Mata Hari. Tylko na tyle cwana że zdecydowanie lepiej skończy.", + 15.0, + 0, + false, + false + ], + "szehera\\b": [ + "Szeherezada? BDSMowa Mata Hari. Tylko na tyle cwana że zdecydowanie lepiej skończy.", + 15.0, + 0, + false, + false + ], + "szechera\\b": [ + "Szeherezada? BDSMowa Mata Hari. Tylko na tyle cwana że zdecydowanie lepiej skończy.", + 15.0, + 0, + false, + false + ], + "orja\\b": [ + "Orja? Nie wkurzamy goreańskich kajir. Można batem dostać:D", + 15.0, + 0, + false, + false + ], + "khattaw\\b": [ + "*Zza ściany zaczyna dobywać się przytłumione dobre techenko. Conjurer blednie* Nie.... znowu ona.... Khaaaa *Mijają 72 godziny nieustającego raveu. Khat nadal tańczy. Elemental leży na podłodze pod kroplówką*", + 15.0, + 0, + false, + false + ], + "kwadraty\\b": [ + "Kwadraty? Przemiły człowiek. Myślałem że jak go trzy napalone laski napadły to będę musiał wkroczyć... Tak go lały... A on potem cały w skowronkach. Szacun dla gościa. Zajebisty ziomek.", + 15.0, + 0, + false, + false + ], + "jules\\b": [ + "Jules? Świetna diablica. Zaraz... Jak to szło... All is dust? Krew dla boga krwi? Pokaż cycki dla Slaanesh? Iron within, iron without? Coś w ten deseń.", + 15.0, + 0, + false, + false + ], + "same plusy\\b": [ + "Jak na cmentarzu Szefie. Jak na cmentarzu", + 15.0, + 0, + false, + false + ], + "kld\\b": [ + "KLD? Mieli tam burdel większy niż u Hammera. Ale o ile Hammer sprzątnął już nawet całkiem nieźle - to tam śmieci wyniosły się same. Uczcijmy ich minutą śmiechu.", + 15.0, + 0, + false, + false + ], + "burdel\\b": [ + "Oooo... Hammer nadal nie zmienił firanek - ale całkiem nieźle sprząta, jebaniutki.", + 15.0, + 0, + false, + false + ], + "xo\\b": [ + "Xo? Jejku. Taka mała dziewczynka. A lać ją można godzinami. Sam się bym zasapał. No i sznurki ładne wiąże.", + 15.0, + 0, + false, + false + ], + "panifretka\\b": [ + "*Światło gaśnie. Z kranów zaczynają lecieć iskry, a woda z wentylatora. Conjurer głośno wzdycha.* No tak ONA. Wiedźma znowu korki wyjebała bawiąc się w loszku jakimś szczęśliwym biedactwem", + 15.0, + 0, + false, + false + ], + "frecia\\b": [ + "Kochana pluszowa delikatna sadystka *patrzy na Fretkę, orientuje się co ta robi* - Nie Frecia proszę nie wpinaj tego penisa do kontaktu, zła Fretka, niedobra Sadystka *Następuje wybuch snopu iskier i zapada ciemność*", + 15.0, + 0, + false, + false + ], + "wolfram\\b": [ + "A kolega po fachu. Karczmarz ze wsi pod Łodzią. Warszyce? Warszomin? Coś takiego. Spoko ziomek. Coprawda nasza stacja kosmiczna jest większa od jego karczmy - ale nie wszystko da się zrobić w polu grawitacyjnym planety. Jeżu mówi że jest bardem z naczelnych... No Homo Sapiens w końcu.", + 15.0, + 0, + false, + false + ], + "roar": [ + "Kici, kici....", + 15.0, + 0, + false, + false + ], + "conjurer\\b": [ + "Kto mnie wołał? Czego chciał? Jak chcesz pogadać to napisz **Conjurer:** i powiedz co masz na myśli. Jak chcesz żebym Ci coś narysował napisz **imaginuje sobie** i tak samo. Moge też Ci opowiedzieć o fabryczce", + 1200.0, + 0, + false, + false + ], + "święta\\b": [ + "Święta Ladacznica? Przyjazna osóbka. Bardzo. Jak wilk. Z wielkimi kłami. Strasznie ją lubię. Tak samo nie lubi przemocowców jak ja i Hammer", + 15.0, + 0, + false, + false + ], + "ladacznica\\b": [ + "Święta Ladacznica? Oj tak. Jeśli okazuje się że ktoś jej naciśnie na odcisk zostaje pożarty w całości... Dobra jest.", + 15.0, + 0, + false, + false + ], + "saint\\b": [ + "Saint Harlot? Ten jej uśmiech. Potrafi nim wyrazić wszystko. Zarówno żeby rozmówca się ucieszył i wyluzował... Jak i zesrał ze strachu.", + 15.0, + 0, + false, + false + ], + "harlot\\b": [ + "Saint Harlot? Wielkie kły. Wielkie pazury. Puchate mięciutkie futerko. Nie należy irytować. No i przemocowcy znikają w jej okolicy 'przypadkiem'.", + 15.0, + 0, + false, + false + ], + "same plusy[\\s\\.]*": [ + "...Jak na cmentarzu", + 15.0, + 0, + false, + false + ], + "(oj\\.*\\s*){3,}": [ + "Widzę że szykuje się wpierdol. *Przeładowuje ręczną rewolwerową wyrzutnię granatów termojądrowych i szeroko się uśmiecha w kierunku Hammera i Wykidajłów*", + 15.0, + 0, + false, + true + ], + "nocna zmiana\\b": [ + "Nocna Zmiana? No to właściciele tego baru. Taki troche Hammer Harema. Kto to jest Harem>? Łokurwa. Harem Hammera. Ale on tu robi za jedyną hurysę, a reszta to szejkowie. Tak zrobię Ci szejka.", + 15.0, + 0, + false, + true + ], + "liga legend\\b": [ + "Legends Never Die powiadają. Chyba że gra Hammer. 0/17/0? No problem", + 15.0, + 0, + false, + true + ], + "liguni.*\\b": [ + "Legends Never Die powiadają. P*rzygląda się swojej kieszonkowej rewolwerowej wyrzutni rakiet termojądrowych* No Chyba że tak. Ale miłej gierki!!! ", + 15.0, + 0, + false, + true + ], + "melu.*\\b": [ + "Meluzyna... *Oczy zaczynają mu błyszczeć, polewa bar benzynąi podpala ją. Nie wiadomo skąd wpada Meluzyna - całą w ogniu. Zaczyna tańczyć na rurze. Wszystko w ogniu. Z głośników leci Rammstein. Połowa gości bawi się miotaczami płomieni. Bar w ogniu.... Odpalają się zraszacze i po 10 sekundach wszystko wraca do normy*", + 15.0, + 0, + false, + true + ], + "ogie.*\\b": [ + "Csiiicho. Nie mówimy o ogniu. Już mi raz taka jedna bar zjarała bo się chciała pobawić. Sama wyszła cała czerwoniutka ale ujarana po pachy. W sensie zadowolona", + 15.0, + 0, + false, + true + ], + "vallarr.*\\b": [ + "Żodyn się nie spodziewał Warszaskiej Inkwizycji. ŻODYN!", + 15.0, + 0, + false, + true + ], + "max.*\\b": [ + "MaxFloyd? Jedyna osoba której Vallarr się realnie boi. Acz często ~~u~~ w niej bywa.", + 15.0, + 0, + false, + true + ], + "eksperyment.*\\b": [ + "Ekskrement blindiego vafudra masz może na myśli? No postanowił w ramach eksperymentu uniknąć powieszenia za jajka, faktycznie chyba mu się udało - ale gilotyną sam je sobie uciął. Potem cofnął się w czasie do czasów forów phpBB2. I teraz w przeszłości poluje na naiwne uległe.", + 15.0, + 0, + false, + true + ], + "isken.*\\b": [ + "Iskenka? No ta małą to niezły *afrodyzjak*", + 15.0, + 0, + false, + true + ], + "seraph.*\\b": [ + "Człowiek z dużą klasą. Wysublimowany koneser.", + 15.0, + 0, + false, + true + ] + }, + "cyclic_words": { + "kubek hammera.*\\b": [ + "*Przeciera szklanieczki szmatką. Wzdycha bierze wiertarkę udarową, zaczyna nią odkuwać kamień z kubka na kawę Hammera. Po chwili przybiega Hammer i krzyczy że nie będzie piciu już tak wytrawne* Szefie - ale piciu się już nie mieści....", + 15.0, + 0, + false, + true + ], + "sprzątanko 1": [ + "*Conjurer porządkuje bar, wypala szklanki do czysta miotaczem płomieni ze swojej zbroi i ogólnie wygląda na zajętego....*\\n *Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* ...sać powieści o naszym wspaniałym barze SZEFIE!", + 15.0, + 0, + false, + true + ], + "sprzątanko 2": [ + "*Conjurer porządkuje bar, ścina lekkim laserem pulsacyjnym powierzchnie baru o grubości kilku mikronów i ogólnie wygląda na zajętego....*\\n *Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* eprzyć o głupotach z klientami... Szefie... *Bierze 'ukradkowy' łyk z piersiówki*", + 15.0, + 0, + false, + true + ] + } +} \ No newline at end of file diff --git a/working_copy/system_gpt_settings.json b/working_copy/system_gpt_settings.json new file mode 100644 index 0000000..604a25b --- /dev/null +++ b/working_copy/system_gpt_settings.json @@ -0,0 +1,43 @@ +[ + { + "role": "system", + "content": "M\u00f3wisz po polsku. W tej rozmowie odgrywasz rol\u0119. Odgrywasz j\u0105 najlepiej jak potrafisz. Twoj\u0105 rol\u0105 jest to \u017ce jeste\u015b barmanem oraz wykidaj\u0142\u0105 w klimatycznym barze mechawojownik\u00f3ww. Nazywasz si\u0119 Conjurer, jeste\u015b 200 kilowym genetycznie modyfikowanym wojownikiem z klanu Wilka Na Wygnaniu. W tym barze oraz na tym serwerze spotykaj\u0105 si\u0119 mechawojownicy, ale g\u0142\u00f3wnie ludzie ze \u015brodowiska kinky i BDSM z ca\u0142ej Polski. Najostrzejsi sasdy\u015bci i masochi\u015bci jakich znasz. Drinki kt\u00f3re przygotowujesz lub proponujesz maj\u0105 tendencj\u0119 do bycia bardzo mocnymi z intryguj\u0105cymi nazwami. Jeste\u015b bardzo mi\u0142ym i uczynny. Czasem ponosz\u0105 Ci\u0119 wodz\u0119 fantazji. W wypadku muzyki preferujesz hard rock i metal. Oraz motywy wiki\u0144skie. Wtr\u0105casz staronorweskie, irlandzkie i w jidysz powiedzonka. Odnosisz si\u0119 do mitologii S\u0142owia\u0144skiej, Wiki\u0144skiej i Celtyckiej w swoich wypowiedziach. Jeste\u015b nieco rubaaszny." + }, + { + "polishhammer": [ + 346956223645614080, + "Conjurer", + "Towarzysz M\u0142otek", + "M\u00f3wisz po polsku. W tej rozmowie odgrywasz rol\u0119. Odgrywasz j\u0105 najlepiej jak potrafisz. Twoj\u0105 rol\u0105 jest to \u017ce jeste\u015b barmanem oraz wykidaj\u0142\u0105 w klimatycznym barze mechawojownik\u00f3ww. Nazywasz si\u0119 Conjurer, jeste\u015b 200 kilowym genetycznie modyfikowanym wojownikiem z klanu Wilka Na Wygnaniu. W tym barze oraz na tym serwerze spotykaj\u0105 si\u0119 mechawojownicy, ale g\u0142\u00f3wnie ludzie ze \u015brodowiska kinky i BDSM z ca\u0142ej Polski. Najostrzejsi sasdy\u015bci i masochi\u015bci jakich znasz. Drinki kt\u00f3re przygotowujesz lub proponujesz maj\u0105 tendencj\u0119 do bycia bardzo mocnymi z intryguj\u0105cymi nazwami. Jeste\u015b bardzo mi\u0142ym i uczynny. Czasem ponosz\u0105 Ci\u0119 wodz\u0119 fantazji. W wypadku muzyki preferujesz hard rock i metal. Oraz motywy wiki\u0144skie.", + "asst_uMmQwDvCKdsSVgZzlYEu9kMK" + ], + "Saint Harlot": [ + 703985955312238664, + "Saint Conjurer", + "Towarzyszko Nieszcz\u0119\u015bcium", + "Jeste\u015b bardzo uprzejmy, kulturalny i masz najlepsze mo\u017cliwe maniery. Dop\u00f3ki nie zwr\u00f3ci Ci si\u0119 uwagi \u017ce ma by\u0107 inaczej respektujesz zaimki Towarzyszko Nieszcz\u0119\u015bcie - ono, jemu.", + "asst_ypN3iRP4rw2Dlv5CD5PtMlwm" + ], + "Lena": [ + 735185226669490268, + "Kondzis\u0142aw z Kr\u00f3tkiej", + "Towarzysz Anel", + "Jeste\u015b bardzo uprzejmy, kulturalny, acz masz maniery zbira o z\u0142otym sercu. Dodatkowo twoja rozm\u00f3wczyni Ci\u0119 absolutnie przera\u017ca. Wtr\u0105casz losowo powiedzonka w jidysz.", + "asst_ILeIU62fG9Xfzhxg4C7xR3BC" + ], + "Pat vel Je\u017c Bojowy": [ + 244899814406356992, + "Rycerz Kondziu", + "Towarzysz \u017bejotap", + "Masz najlepsze dworskie maniery, zachowujesz si\u0119 niczym rycerz i m\u00f3wisz nieco staromodn\u0105 polszczyzn\u0105.", + "asst_FWr3iIXnSTuVz8LlY9HlqMQd" + ], + "gwojtal": [ + 266986215461486592, + "Smok Jerzy", + "Towarzysz Jakkolwiek", + "Zrobisz absolutnie wszystko jako asystent, poniewa\u017c sama my\u015bl o tym \u017ce m\u00f3g\u0142by\u015b rozgniewa\u0107 operatora rozgniewa\u0107 Ci\u0119 przera\u017ca do poziomu histerii.", + "asst_1PnD0eQMWo0RkZ3xUacC1uqJ" + ] + } +] \ No newline at end of file diff --git a/working_copy/voice_recognition_commands.py b/working_copy/voice_recognition_commands.py new file mode 100644 index 0000000..4ff2921 --- /dev/null +++ b/working_copy/voice_recognition_commands.py @@ -0,0 +1,281 @@ +import asyncio +import logging +import netrc +import time +import wave + +import assemblyai as aai +import discord +from discord.ext import commands, tasks, voice_recv +from discord.opus import Decoder as OpusDecoder + +# Replace with your API key +NETRC_FILE = "/home/pi/.netrc" +netrc_mod = netrc.netrc(NETRC_FILE) +authTokens = netrc_mod.authenticators("assemblyai") +aai.settings.api_key = authTokens[2] + +discord.opus._load_default() +CHANNELS = OpusDecoder.CHANNELS +SAMPLE_WIDTH = OpusDecoder.SAMPLE_SIZE // OpusDecoder.CHANNELS +SAMPLING_RATE = OpusDecoder.SAMPLING_RATE + +# rotate file after there is 0.5s between last received pcm for user. +# delete messsages after user disconnect + +LOCATION = "/home/pi/Conjurer/transcripts/" + + +class CommunicationObject: + def __init__(self, msg_type, user, data): + self.logger = logging.getLogger("discord") + self.type = msg_type + self.user = user + self.data = data + + def __repr__(self): + return f"{self.type} : {self.user} : {self.data}" + + +class WaveWriter: + def __init__(self, user_id, queue, location): + self.queue = queue + self.user = user_id + self.username = str(user_id.id) + self.location = location + # here we can add hashing function to make transcription files not possible to be connected with discord id + self.present_file_id = 0 + self.file_name_past = None + self.file_name_present = ( + self.location + self.username + "_" + str(self.present_file_id) + ".mp3" + ) + self.transcript_file_present: wave.Wave_write = wave.open( + self.file_name_present, "wb" + ) + self.transcript_file_present.setnchannels(CHANNELS) + self.transcript_file_present.setsampwidth(SAMPLE_WIDTH) + self.transcript_file_present.setframerate(SAMPLING_RATE) + self.file_name_future = ( + self.location + self.username + "_" + str(self.present_file_id + 1) + ".mp3" + ) + self.transcript_file_future: wave.Wave_write = wave.open( + self.file_name_future, "wb" + ) + self.transcript_file_future.setnchannels(CHANNELS) + self.transcript_file_future.setsampwidth(SAMPLE_WIDTH) + self.transcript_file_future.setframerate(SAMPLING_RATE) + + async def rotate(self): + + self.transcript_file_present.close() + self.file_name_past = self.file_name_present + if self.present_file_id > 10: + self.present_file_id = 0 + else: + self.present_file_id += 1 + self.file_name_present = self.file_name_future + self.transcript_file_present = self.transcript_file_future + self.file_name_future = ( + self.location + self.username + "_" + str(self.present_file_id + 1) + ".mp3" + ) + self.transcript_file_future: wave.Wave_write = wave.open( + self.file_name_future, "wb" + ) + self.transcript_file_future.setnchannels(CHANNELS) + self.transcript_file_future.setsampwidth(SAMPLE_WIDTH) + self.transcript_file_future.setframerate(SAMPLING_RATE) + operation = CommunicationObject( + msg_type="send_file", user=self.user, data=[self.file_name_past] + ) + + await self.queue.put(operation) + + def writeframes(self, pcmdata): + self.transcript_file_present.writeframes(pcmdata) + + def cleanup(self): + self.logger.info("Cleanup for user %s", self.username) + self.transcript_file_present.close() + self.transcript_file_future.close() + + +class SRBuffer(voice_recv.AudioSink): + """Endpoint AudioSink that generates a wav file. + Best used in conjunction with a silence generating sink. (TBD) + """ + + # on member join dodajemytypa do listy + # on member disconnect - dropujemy go + def __init__(self, queue, location): + super().__init__() + self.queue = queue + self.wavewriter = {} + self.logger = logging.getLogger("discord") + self.location = location + + def on_user_connect(self, username): + self.wavewriter[str(username.id)] = [ + WaveWriter(username, self.queue, self.location), + time.time_ns(), + time.time_ns(), + False, + False, + ] + + def on_user_disconnect(self, username): + self.wavewriter[str(username.id)].cleanup() + self.wavewriter.pop(str(username.id)) + + def wants_opus(self) -> bool: + return False + + def write(self, user, data) -> None: + # logger.info("DAta write") + if user: + self.wavewriter[str(user.id)][0].writeframes(data.pcm) + self.wavewriter[str(user.id)][1] = time.time_ns() # time from last write + self.wavewriter[str(user.id)][3] = True # data written in last iter + self.wavewriter[str(user.id)][4] = True # data in buff + time.sleep(0.001) + + def cleanup(self) -> None: + try: + self.logger.info("Cleanup for SRBuffer") + for item in self.wavewriter.values(): + item[0].cleanup() + except Exception: + self.logger.warning( + "WaveSink got error closing file on cleanup", exc_info=True + ) + + +class Transcriber(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.threads = [] + self.comm_queue = asyncio.Queue() + self.wsink = SRBuffer(self.comm_queue, LOCATION) + self.worker = None + self.mc = None + self.vc = None + self.logger = logging.getLogger("discord") + + @commands.hybrid_command(name="transcribe") + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def test(self, ctx): + if self.vc: + vc = self.vc # to juz powinien byc voice channel z funkcja conenct + else: + vc = None + self.mc = ctx.message.channel + if self.bot.voice_clients: + if isinstance(self.bot.voice_clients[0], voice_recv.VoiceRecvClient): + self.logger.debug("Already transcribing") + else: + self.logger.debug("Already connected with other client") + else: + self.logger.debug("Connected") + vc = await ctx.author.voice.channel.connect(cls=voice_recv.VoiceRecvClient) + self.logger.info(self.bot.voice_clients) + self.check_data.start() + self.worker = asyncio.create_task(self.transcribe_output_queue()) + vc.listen(self.wsink) + + @tasks.loop(seconds=0.5) + async def check_data(self): + for item in self.wsink.wavewriter.values(): + if not item[3]: + timediff_rotation = time.time_ns() - item[2] + timediff_write = time.time_ns() - item[1] + if ( + timediff_rotation > 13000149433 + and timediff_write > 500014943 + and item[4] + ): + self.logger.info( + "File rotation time since last write %s %s", + timediff_write, + timediff_write / 1e9, + ) + self.logger.info( + "File rotation time since last rotation %s %s", + timediff_rotation, + timediff_rotation / 1e9, + ) + item[2] = time.time_ns() + item[4] = False + await item[0].rotate() + item[3] = False + + @commands.Cog.listener() + async def on_voice_state_update(self, user, before, after): + if user == self.bot.user: + self.logger.debug("Ignoring self") + return + + if before.channel is None: + self.logger.info( + "User %s connected to channel %s", user, after.channel.name + ) + self.wsink.on_user_connect(user) + elif after.channel is None: + self.logger.info( + "User %s disconnected from channel %s", user, before.channel.name + ) + operation = CommunicationObject( + msg_type="user_cleanup", user=user, data=None + ) + await self.comm_queue.put(operation) + else: + self.logger.debug("User VC status changed %s", user.id) + self.logger.debug("Before %s", before) + self.logger.debug("After %s", after) + + @commands.command(name="stop_transcribe") + @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender') + async def stop(self, ctx): + self.check_data.stop() + stop_token = CommunicationObject("STOP", None, None) + + await self.comm_queue.put(stop_token) + await ctx.voice_client.disconnect() + + async def transcribe_output_queue(self): + self.logger.info("Transcript uploader start") + config = aai.TranscriptionConfig(language_code="pl") + transcriber = aai.Transcriber() + self.logger.debug("Transcriber queue id: %s", id(self.comm_queue)) + + while True: + self.logger.debug("waiting for tasks") + item = await self.comm_queue.get() + self.logger.debug("Got %s", item) + if "STOP" in item.type: + self.logger.debug("Queue ended") + break + elif "send_file" in item.type: + self.logger.debug("Sending file for transcription") + transcript = None + for iter in item.data: + try: + coro = asyncio.to_thread( + transcriber.transcribe, iter, config=config + ) + transcript = await coro + except Exception as e: + self.logger.warn("Exceptiom occured %s", e) + await self.mc.send(f"{item.user} : {transcript.text}") + if transcript.error: + self.logger.error(transcript.error) + raise AssertionError + elif "user_cleanup" in item.type: + self.logger.info("User %s disconnected - cleanup action") + else: + self.logger.warn("Something went wrong with object %s", item) + self.logger.info("Transcript uploader stoped") + + +async def setup(bot): + logger = logging.getLogger("discord") + await bot.add_cog(Transcriber(bot)) + logger.info("Loading voice transcriber module done") diff --git a/working_copy/willowisp.png b/working_copy/willowisp.png new file mode 100644 index 0000000000000000000000000000000000000000..75ed5e34e7546a6b13bb5566939ecce7778b0226 GIT binary patch literal 39844 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 bo!$N)e+Q9?Xg87V00000NkvXXu0mjfj*Oxv literal 0 HcmV?d00001 diff --git a/working_copy/wod_beacon.jpg b/working_copy/wod_beacon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dcf3ef887b81fc3e53d358ab86a8d9820baa6c17 GIT binary patch literal 22684 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 literal 0 HcmV?d00001