diff --git a/administration_commands.py b/administration_commands.py new file mode 100644 index 0000000..c964d48 --- /dev/null +++ b/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/ai_commands.py b/ai_commands.py new file mode 100644 index 0000000..ec91cf3 --- /dev/null +++ b/ai_commands.py @@ -0,0 +1,390 @@ +# 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") + + @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") + + @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") + + @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") + + @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.debug("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, + "CONVERSATION", + ) + + if len(result) < 999: + await message.reply(result) + else: + while len(result) > 999: + await message.reply(result[:999]) + result = result[999:] + 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, + "RANDOM", + ) + 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 + with open(temp_file_name, "wb") as dalle_file: + dalle_file.write(response.content) + 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/ai_functions.py b/ai_functions.py new file mode 100644 index 0000000..d02b148 --- /dev/null +++ b/ai_functions.py @@ -0,0 +1,408 @@ +import asyncio +import json +import logging +import random + +import openai +import tiktoken +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, +) + +#this do per user +VECTOR_STORE_ID = -1 + +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 +): + """ + 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 + :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) + temp = {"role": "user", "content": username + ":" + prompt} + if vykidailo or bartender: + logger.info("Administrator coś chciał") + history.append(temp) + if request_type == "MUSIC": + with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as file_music_memory: + # First we load existing data into a dict. + file_data = json.load(file_music_memory) + # Join new_data with file_data inside emp_details + file_data.append(temp) + file_music_memory.seek(0) + # convert back to json. + json.dump(file_data, file_music_memory, indent=4) + elif request_type == "RANDOM": + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory: + # First we load existing data into a dict. + file_data = json.load(file_memory) + # Join new_data with file_data inside emp_details + file_data.append(temp) + file_memory.seek(0) + # convert back to json. + json.dump(file_data, file_memory, indent=4) + else: + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory: + # First we load existing data into a dict. + file_data = json.load(file_memory) + # Join new_data with file_data inside emp_details + file_data.append(temp) + file_memory.seek(0) + # convert back to json. + json.dump(file_data, file_memory, indent=4) + history = [] + history.append(GPT_SETTINGS[0]) + chat_gpt_config_request_size = num_tokens_from_string(GPT_SETTINGS[0], "gpt-4") + + for slowo, reakcja in WORD_REACTIONS.items(): + if not reakcja[3]: + content = ( + "Kiedy słyszysz " + + slowo + + " to reagujesz lub dzieje się to " + + reakcja[0] + ) + temp = {"role": "system", "content": content} + chat_gpt_config_request_size += num_tokens_from_string(temp, "gpt-4") + history.append(temp) + + final_prompt = username + ":" + prompt + logger.debug( + "Rozmiar zapytania przed dodaniem historii %s", chat_gpt_config_request_size + ) + if request_type == "MUSIC": + algorithm = "gpt-4o" + table = MESSAGE_TABLE_MUZYKA + token_amount = 10700 + elif request_type == "RANDOM": + algorithm = "gpt-4o" + table = MESSAGE_TABLE + token_amount = 10700 + else: + table = MESSAGE_TABLE + algorithm = "gpt-4o" + token_amount = 10700 + + prompt_gpt_request_size = num_tokens_from_string( + {"role": "user", "content": final_prompt}, "gpt-4" + ) + temptable = [] + for i in reversed(table): + temp_token = num_tokens_from_string(i, "gpt-4") + logger.debug( + "Rozmiar zapytania %s prompt %s temp %s", + chat_gpt_config_request_size, + prompt_gpt_request_size, + temp_token, + ) + if ( + chat_gpt_config_request_size + < token_amount + prompt_gpt_request_size + temp_token + ): + temptable.insert(1, i) + chat_gpt_config_request_size += temp_token + history.extend(temptable) + temp = {"role": "user", "content": final_prompt} + history.append(temp) + logger.info("Rozmiar zapytania po wyslaniu %s", chat_gpt_config_request_size) + try: + response = await OPENAICLIENT.chat.completions.create( + model=algorithm, messages=history + ) + except openai.APITimeoutError as e: + # Handle timeout error, e.g. retry or log + result = 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: + result = 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 + 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", + ) + result = 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 + 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", + ) + result = 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 + result = 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 + result = 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: + result = 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: + result = 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 + result = 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:") + logger.info(history) + await asyncio.sleep(15) + result = "" + logger.debug("Odpowiedzi") + logger.info(response) + logger.debug(response.choices) + for choice in response.choices: + result += choice.message.content + logger.info("Sformatowane odpowiedzi") + logger.info(result) + temp = {"role": "assistant", "content": result} + history.append(temp) + if request_type == "MUSIC": + with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as file_music_memory: + # First we load existing data into a dict. + file_data = json.load(file_music_memory) + # Join new_data with file_data inside emp_details + file_data.append(temp) + file_music_memory.seek(0) + # convert back to json. + json.dump(file_data, file_music_memory, indent=4) + elif request_type == "RANDOM": + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory: + # First we load existing data into a dict. + file_data = json.load(file_memory) + # Join new_data with file_data inside emp_details + file_data.append(temp) + file_memory.seek(0) + # convert back to json. + json.dump(file_data, file_memory, indent=4) + else: + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory: + # First we load existing data into a dict. + file_data = json.load(file_memory) + # Join new_data with file_data inside emp_details + file_data.append(temp) + file_memory.seek(0) + # convert back to json. + json.dump(file_data, file_memory, indent=4) + return result, MESSAGE_TABLE + + +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/bot.py b/bot.py index bba940f..92afdb4 100644 --- a/bot.py +++ b/bot.py @@ -16,10 +16,10 @@ import os import random import re import shutil +import subprocess import sys import threading import uuid -import subprocess from datetime import datetime from logging import handlers from pathlib import Path, PurePath @@ -42,11 +42,16 @@ from discord.ext import commands, tasks from spotipy.oauth2 import SpotifyClientCredentials import yt_dlp +from communication_subroutine import ( + IN_COMM_Q, + OUT_COMM_Q, + QueryControl, + comm_subroutine, + PREPPED_TRACKS, +) from spotify_dl import spotify from spotify_dl import youtube as youtube_download -from communication_subroutine import comm_subroutine, IN_COMM_Q, OUT_COMM_Q, QueryControl - # *=========================== Classes definition # The above code is defining a TypedDict called `Music_Config` in Python. This TypedDict has three @@ -63,7 +68,6 @@ Music_Config = TypedDict( ) - class MusicFileList(object): """ The `MusicFileList` class manages a list of music files, with the ability to refresh the list from a @@ -118,7 +122,6 @@ class MusicFileList(object): # *=========================================== Predefines -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") MASTER_TIMEOUT = datetime.now() INITIAL_TIME_WAIT = 500 MUZYKA: Music_Config = {"ctx": None, "queue": [], "requester": []} @@ -212,7 +215,6 @@ REMOTE_HOST_NAME = "openai" authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) openai.api_key = authTokens[2] openAIClient = openai.AsyncOpenAI(api_key=openai.api_key) - REMOTE_HOST_NAME = "spotipy" authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) spotify_ctrl = spotipy.Spotify( @@ -239,6 +241,7 @@ handler = handlers.RotatingFileHandler( maxBytes=6 * 1024 * 1024, backupCount=6, ) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") handler.setFormatter(formatter) logger.addHandler(handler) random.seed() @@ -284,7 +287,6 @@ for key in word_reactions: logger.info("Done") - # *=========================================== Create Client logger.info("Creating discord bot") client = commands.Bot(intents=intents, command_prefix="$") @@ -298,8 +300,14 @@ logger.info("Done") async def on_ready(): """Metoda wywoływana przy połączeniu do serwera.""" logger.debug("%s has connected to Discord!", client.user) + #TODO: load vs reload + #try: + await client.load_extension("voice_recognition_commands") + logger.info(client.cogs) await client.change_presence(activity=discord.Game(name="Axe Throwing Darts")) await client.tree.sync() + for com in client.commands: + logger.info("Command %s is awejleble", com.qualified_name) check_self.start() check_data_q.start() @@ -339,9 +347,13 @@ async def on_message(message): if message.channel.id == 1111625221171052595: return if isinstance(message.channel, discord.DMChannel): - channel = client.get_channel(1064888712565100614) - await channel.send("Słyszałem ja żem że: " + message.content) - return + if message.author.id == 346956223645614080: + await message.reply("Witam witam") + return + else: + channel = client.get_channel(1064888712565100614) + await channel.send("Słyszałem ja żem że: " + message.content) + return channel = message.channel await client.process_commands(message) @@ -404,7 +416,13 @@ async def on_message(message): result, MESSAGE_TABLE = await handle_response( prompt, vykidailo, bartender, MESSAGE_TABLE, username, "CONVERSATION" ) - await message.reply(result) + + if len(result) < 1500: + await message.reply(result) + else: + while len(result) > 1500: + await message.reply(result[:1500]) + result = result[1500:] if "imaginuje sobie:" in message.content: async with channel.typing(): logger.info("Poczatek procedury obrazkowej") @@ -528,16 +546,20 @@ async def connect(ctx, arg=None): """ if ctx and arg: logger.info("Ctx and arg defined for connect") - voice_channel = client.get_channel(1060349757349974066) if client.voice_clients: - voice_client = client.voice_clients[0] - if voice_client.is_connected(): + 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 = client.get_channel(1060349757349974066) + vc = await voice_channel.connect() + if not vc: + logger.error("Not possible to connect to voice") return - voice_channel_connection = await voice_channel.connect() - if not voice_channel_connection: - logger.error("Not possible to connect to voice") - logger.info("Connecting to voice") + logger.info("Connected to voice") async def disconnect(ctx): @@ -637,7 +659,12 @@ async def play(ctx, zamawial=None, arg=None): query, vykidailo, bartender, message_table_muzyka, username, "MUSIC" ) logger.debug("Obecna historia czatu: %s", message_table_muzyka) - await ctx.send(result) + if len(result) < 1500: + await ctx.send(result) + else: + while len(result) > 1500: + await ctx.send(result[:1500]) + result = result[1500:] async def archive_channel(channel_no): @@ -676,6 +703,7 @@ async def check_music(): await play(MUZYKA["ctx"], MUZYKA["requester"]) await asyncio.sleep(2) + @tasks.loop(seconds=3) async def check_data_q(): """ @@ -691,40 +719,42 @@ async def check_data_q(): for doi in fresh_data.entries: 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*' + 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}' + message += f" Dzielny poszukiwaczu @{searcher} w odpowiedzi na twoje zapytanie {query} nr {fresh_data.uuid}" else: ctx = client.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 += 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 += "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: + elif len(entries) >= 1 and len(entries) < 5: message += " znajduje się coś:\n" for item in entries: - message+=item + message += item await ctx.send(message) else: - message+= " znajduje się cholernie dużo:\n" + message += " znajduje się cholernie dużo:\n" for item in entries: - message+=item + 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}") + # 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 + @tasks.loop(seconds=120) async def check_self(): """ @@ -1082,9 +1112,13 @@ async def get_file(ctx, source, link): 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) + 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) + 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": []} @@ -1351,6 +1385,7 @@ async def max_weight(lista): # *=========================================== Define Commands + @client.hybrid_command( name="skip_track", description="Przeskocz kawałek w radiu", @@ -1360,15 +1395,15 @@ async def skip_track(ctx): async with ctx.typing(): allowed = False for role in ctx.author.roles: - if role.name in ("Thane","Jarl"): + 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, + requests.get, + f"{RADIO_HARBOR_ADDRESS}{SKIP_TRACK}", + timeout=360, ) result = await coroutine logger.info("Done %s", result) @@ -1384,13 +1419,19 @@ async def zrestartuj_radio(ctx): async with ctx.typing(): allowed = False for role in ctx.author.roles: - if role.name in ("Thane","Jarl"): + 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", shell=False, check = False, capture_output = True) - logger.info("Wynik: %s",retcode) + retcode = subprocess.run( + "/home/pi/Conjurer/restart_radio.sh", + shell=False, + check=False, + capture_output=True, + ) + logger.info("Wynik: %s", retcode) + @client.hybrid_command( name="dej_co_ze_spotifaja", @@ -1633,6 +1674,7 @@ async def graj_muzyko(ctx): logger.info("Press play on tape completed") check_music.start() + @client.hybrid_command( name="radio_hardkor", description="Włącza radio Conjurer na kanale #nocna-zmiana.", @@ -1657,9 +1699,10 @@ async def radio_hardkor(ctx): voice_client = client.voice_clients[0] voice_client.play( discord.PCMVolumeTransformer( - discord.FFmpegPCMAudio("http://95.175.16.246:666/mp3-stream"), volume=0.3 - ) + discord.FFmpegPCMAudio("http://95.175.16.246:666/mp3-stream"), + volume=0.3, ) + ) logger.info("Press play on radio completed") else: logger.error("Already playing") @@ -1667,7 +1710,6 @@ async def radio_hardkor(ctx): check_music.start() - @client.hybrid_command( name="cisza", description="Wyłącza muzykę i czyści plejliste.", @@ -1810,6 +1852,7 @@ async def zagraj_mi_kawalek(ctx): "Obawiam się że nie mogę nic znaleźć u siebie. Spróbuj komendy '$dej_co_ze_spotifaja' jeśli masz link" ) + @client.hybrid_command( name="dodaj_do_ulubionych", description="Dodaje do listy ulubionych w radiu", @@ -1844,6 +1887,8 @@ async def dodaj_do_ulubionych(ctx): result = await coroutine logger.info("Done %s", result) await ctx.send("Zrobione szefie!") + + @client.hybrid_command( name="ja_chciol", description="Dodaje do listy ulubionych w radiu", @@ -1877,6 +1922,7 @@ async def request_radio(ctx): logger.info("Done %s", result) await ctx.send("Zrobione szefie!") + @client.hybrid_command( name="stworz_audycje", description="Dodaje do listy ulubionych w radiu", @@ -2316,11 +2362,18 @@ async def wyszukaj_linki_do_dokumentow(ctx): """ 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", "") + # 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} + 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}", @@ -2328,14 +2381,16 @@ async def wyszukaj_linki_do_dokumentow(ctx): 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ć" + "*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") + 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 = ( @@ -2347,10 +2402,13 @@ async def wyszukaj_linki_do_dokumentow(ctx): username = ctx.message.author.nick else: username = ctx.message.author.name - query_object = QueryControl(username, query_uuid, query, logger, ctx) + 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.") + 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." + ) + @client.hybrid_command( name="glebokie_gardlo", @@ -2394,13 +2452,23 @@ async def wyszukaj_linki_do_dokumentow_deep(ctx): result, MESSAGE_TABLE = await handle_response( prompt, vykidailo, bartender, MESSAGE_TABLE, username, "CONVERSATION" ) - await ctx.send(result) + 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} + # 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}", @@ -2408,14 +2476,16 @@ async def wyszukaj_linki_do_dokumentow_deep(ctx): 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" + "*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") + 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 = ( @@ -2427,11 +2497,12 @@ async def wyszukaj_linki_do_dokumentow_deep(ctx): username = ctx.message.author.nick else: username = ctx.message.author.name - query_object = QueryControl(username, query_uuid, query, logger, ctx) + 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*") - + 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*" + ) @client.hybrid_command( @@ -2463,11 +2534,29 @@ async def krecimy_pornola(ctx): sciezka, files = await get_file(ctx, "Pornol", item_yt) if files: logger.info("Pornol udany") - ctx.send(f"Jest w tajnym archiwum pod adresem{sciezka})") + await ctx.send(f"Jest w tajnym archiwum pod adresem{sciezka})") else: - ctx.reply("Jak ładnie szefa poprosisz.") + await ctx.reply("Jak ładnie szefa poprosisz.") +@client.hybrid_command( + name="co_na_plejliscie_wariacie", + description="Wyswietl kawalki ktore sa na pocztku list radia", + guild=discord.Object(id=664789470779932693), +) +async def co_na_plejliscie_wariacie(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(): + logger.info("plejlista") + 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()}") # *================================== Run if __name__ == "__main__": @@ -2475,16 +2564,16 @@ if __name__ == "__main__": threads = [] logger.info("Starting discord bot: Creating threads") threads.append(threading.Thread(target=client.run, args=(TOKEN,))) - threads.append(threading.Thread(target=comm_subroutine, args=(logger,))) + threads.append(threading.Thread(target=comm_subroutine)) logger.info("Starting discord bot: Starting threads") WRK_CNT = 0 for worker in threads: - WRK_CNT+=1 + 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 + WRK_CNT = 0 for worker in threads: - WRK_CNT+=1 + WRK_CNT += 1 logger.info("Starting discord bot: Joining thread %s", WRK_CNT) worker.join() diff --git a/bot_classes.py b/bot_classes.py deleted file mode 100644 index e69de29..0000000 diff --git a/bot_commands.py b/bot_commands.py deleted file mode 100644 index e69de29..0000000 diff --git a/bot_functions.py b/bot_functions.py deleted file mode 100644 index e69de29..0000000 diff --git a/coach.sex.json b/coach.sex.json deleted file mode 100644 index c53bf65..0000000 --- a/coach.sex.json +++ /dev/null @@ -1,1707 +0,0 @@ -{ - "recordsCount": 17, - "records": [ - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": null, - "updatedDateISO8601": null, - "expiresDateISO8601": null, - "createdDateRaw": null, - "updatedDateRaw": null, - "expiresDateRaw": null, - "audit": { - "createdDate": "2024-02-12T08:43:20+00:00", - "updatedDate": "2024-02-12T08:43:20+00:00" - }, - "nameServers": [], - "whoisServer": "whois.nic.sex", - "registrarName": null, - "status": [], - "cleanText": "Domain Name: coach.sex\nURL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n>>> This name is not available for registration.\n>>> This name subscribes to the AdultBlock+ product and is not available for registration.\n>>> Last update of WHOIS database: 2024-02-12T08:43:20Z <<<\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\nThe Service is provided so that you may look up certain information in relation to domain names that we store in our database.\nUse of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.\nThe information provided by this Service is 'as is' and we make no guarantee of it its accuracy.\nYou agree that by your use of the Service you will not use the information provided by us in a way which is:\n* inconsistent with any applicable laws,\n* inconsistent with any policy issued by us,\n* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or\n* to enable high volume, automated, electronic processes that apply to the Service.\nYou acknowledge that:\n* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,\n* we may restrict, suspend or terminate your access to the Service at any time, and\n* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.\nThis information has been prepared and published in order to represent administrative and technical management of the TLD.\nWe may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.\n", - "rawText": "", - "registrantContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "" - }, - "administrativeContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "" - }, - "technicalContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "" - }, - "billingContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "" - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "" - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": null, - "updatedDateISO8601": null, - "expiresDateISO8601": null, - "createdDateRaw": null, - "updatedDateRaw": null, - "expiresDateRaw": null, - "audit": { - "createdDate": "2021-12-08T07:33:50+00:00", - "updatedDate": "2021-12-08T07:33:50+00:00" - }, - "nameServers": [], - "whoisServer": null, - "registrarName": null, - "status": [], - "cleanText": "Domain Name: coach.sex|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> This name is not available for registration.|>>> This name subscribes to the AdultBlock+ service.|>>> Last update of WHOIS database: 2021-12-08T07:20:05Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "rawText": "Domain Name: coach.sex|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> This name is not available for registration.|>>> This name subscribes to the AdultBlock+ service.|>>> Last update of WHOIS database: 2021-12-08T07:20:05Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "registrantContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "administrativeContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "technicalContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "billingContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2021-08-13T19:12:36+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2021-08-13T19:12:36Z", - "expiresDateRaw": "2021-08-01T20:47:08Z", - "audit": { - "createdDate": "2021-09-08T05:57:18+00:00", - "updatedDate": "2021-09-08T05:57:18+00:00" - }, - "nameServers": [ - "ns1.zenbox.pl|ns2.zenbox.pl|" - ], - "whoisServer": "whois.instra.com", - "registrarName": "Instra Corporation Pty Ltd", - "status": [ - "clientHold", - "clientTransferProhibited", - "pendingDelete", - "redemptionPeriod" - ], - "cleanText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-GDREG|Registrar WHOIS Server: whois.instra.com|Registrar URL: whois.instra.com|Updated Date: 2021-08-13T19:12:36Z|Creation Date: 2018-08-01T20:47:08Z|Registry Expiry Date: 2021-08-01T20:47:08Z|Registrar: Instra Corporation Pty Ltd|Registrar IANA ID: 1376|Registrar Abuse Contact Email:|Registrar Abuse Contact Phone:|Domain Status: clientHold https:\/\/icann.org\/epp#clientHold|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Domain Status: pendingDelete https:\/\/icann.org\/epp#pendingDelete|Domain Status: redemptionPeriod https:\/\/icann.org\/epp#redemptionPeriod|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization:|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns1.zenbox.pl|Name Server: ns2.zenbox.pl|DNSSEC: unsigned|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> Last update of WHOIS database: 2021-09-08T05:56:45Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "rawText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-GDREG|Registrar WHOIS Server: whois.instra.com|Registrar URL: whois.instra.com|Updated Date: 2021-08-13T19:12:36Z|Creation Date: 2018-08-01T20:47:08Z|Registry Expiry Date: 2021-08-01T20:47:08Z|Registrar: Instra Corporation Pty Ltd|Registrar IANA ID: 1376|Registrar Abuse Contact Email:|Registrar Abuse Contact Phone:|Domain Status: clientHold https:\/\/icann.org\/epp#clientHold|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Domain Status: pendingDelete https:\/\/icann.org\/epp#pendingDelete|Domain Status: redemptionPeriod https:\/\/icann.org\/epp#redemptionPeriod|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization:|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns1.zenbox.pl|Name Server: ns2.zenbox.pl|DNSSEC: unsigned|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> Last update of WHOIS database: 2021-09-08T05:56:45Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": null, - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": null, - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": null, - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2021-08-13T19:12:36+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2021-08-13T19:12:36Z", - "expiresDateRaw": "2021-08-01T20:47:08Z", - "audit": { - "createdDate": "2021-08-22T15:19:04+00:00", - "updatedDate": "2021-08-22T15:19:04+00:00" - }, - "nameServers": [ - "ns1.zenbox.pl|ns2.zenbox.pl|" - ], - "whoisServer": "whois.instra.com", - "registrarName": "Instra Corporation Pty Ltd", - "status": [ - "clientHold", - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-GDREG|Registrar WHOIS Server: whois.instra.com|Registrar URL: whois.instra.com|Updated Date: 2021-08-13T19:12:36Z|Creation Date: 2018-08-01T20:47:08Z|Registry Expiry Date: 2021-08-01T20:47:08Z|Registrar: Instra Corporation Pty Ltd|Registrar IANA ID: 1376|Registrar Abuse Contact Email:|Registrar Abuse Contact Phone:|Domain Status: clientHold https:\/\/icann.org\/epp#clientHold|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Domain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization:|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns1.zenbox.pl|Name Server: ns2.zenbox.pl|DNSSEC: unsigned|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> Last update of WHOIS database: 2021-08-22T14:58:12Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "rawText": "", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": "", - "telephone": "", - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": "", - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": "", - "telephone": "", - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": "", - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": "", - "telephone": "", - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": "", - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "zoneContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - } - }, - { - "domainName": "coach.sex", - "domainType": "dropped", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2021-08-13T19:12:36+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2021-08-13T19:12:36Z", - "expiresDateRaw": "2021-08-01T20:47:08Z", - "audit": { - "createdDate": "2021-08-14T17:12:55+00:00", - "updatedDate": "2021-08-14T17:12:55+00:00" - }, - "nameServers": [ - "ns1.zenbox.pl", - "ns2.zenbox.pl" - ], - "whoisServer": "whois.instra.com", - "registrarName": "Instra Corporation Pty Ltd", - "status": [ - "clientHold", - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: coach.sex\nRegistrar WHOIS Server: whois.instra.com\nRegistrar URL: whois.instra.com\nUpdated Date: 2021-08-13T19:12:36Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2021-08-01T20:47:08Z\nRegistrar: Instra Corporation Pty Ltd\nRegistrar IANA ID: 1376\nDomain Status: clientHold https:\/\/icann.org\/epp#clientHold\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns1.zenbox.pl\nName Server: ns2.zenbox.pl\n", - "rawText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-GDREG\nRegistrar WHOIS Server: whois.instra.com\nRegistrar URL: whois.instra.com\nUpdated Date: 2021-08-13T19:12:36Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2021-08-01T20:47:08Z\nRegistrar: Instra Corporation Pty Ltd\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nDomain Status: clientHold https:\/\/icann.org\/epp#clientHold\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization:\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Phone Ext: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Fax Ext: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Phone Ext: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Fax Ext: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Phone Ext: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Fax Ext: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns1.zenbox.pl\nName Server: ns2.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n>>> Last update of WHOIS database: 2021-08-14T17:12:44Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nThe Service is provided so that you may look up certain information in relation to domain names that we store in our database.\n\nUse of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.\n\nThe information provided by this Service is 'as is' and we make no guarantee of it its accuracy.\n\nYou agree that by your use of the Service you will not use the information provided by us in a way which is:\n* inconsistent with any applicable laws,\n* inconsistent with any policy issued by us,\n* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or\n* to enable high volume, automated, electronic processes that apply to the Service.\n\nYou acknowledge that:\n* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,\n* we may restrict, suspend or terminate your access to the Service at any time, and\n* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.\n\nThis information has been prepared and published in order to represent administrative and technical management of the TLD.\n\nWe may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2020-09-15T20:48:28+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2020-09-15T20:48:28Z", - "expiresDateRaw": "2021-08-01T20:47:08Z", - "audit": { - "createdDate": "2021-06-17T12:11:23+00:00", - "updatedDate": "2021-06-17T12:11:23+00:00" - }, - "nameServers": [ - "ns2.zenbox.pl|ns1.zenbox.pl|" - ], - "whoisServer": "whois.instra.com", - "registrarName": "Instra Corporation Pty Ltd", - "status": [ - "clientTransferProhibited" - ], - "cleanText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-GDREG|Registrar WHOIS Server: whois.instra.com|Registrar URL: whois.instra.com|Updated Date: 2020-09-15T20:48:28Z|Creation Date: 2018-08-01T20:47:08Z|Registry Expiry Date: 2021-08-01T20:47:08Z|Registrar: Instra Corporation Pty Ltd|Registrar IANA ID: 1376|Registrar Abuse Contact Email:|Registrar Abuse Contact Phone:|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization:|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns2.zenbox.pl|Name Server: ns1.zenbox.pl|DNSSEC: unsigned|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> Last update of WHOIS database: 2021-06-17T11:55:38Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "rawText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-GDREG|Registrar WHOIS Server: whois.instra.com|Registrar URL: whois.instra.com|Updated Date: 2020-09-15T20:48:28Z|Creation Date: 2018-08-01T20:47:08Z|Registry Expiry Date: 2021-08-01T20:47:08Z|Registrar: Instra Corporation Pty Ltd|Registrar IANA ID: 1376|Registrar Abuse Contact Email:|Registrar Abuse Contact Phone:|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization:|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns2.zenbox.pl|Name Server: ns1.zenbox.pl|DNSSEC: unsigned|URL of the ICANN Whois Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/|>>> Last update of WHOIS database: 2021-06-17T11:55:38Z <<<||For more information on Whois status codes, please visit https:\/\/icann.org\/epp||The Service is provided so that you may look up certain information in relation to domain names that we store in our database.||Use of the Service is subject to our policies, in particular you should familiarise yourself with our Acceptable Use Policy and our Privacy Policy.||The information provided by this Service is 'as is' and we make no guarantee of it its accuracy.||You agree that by your use of the Service you will not use the information provided by us in a way which is:|* inconsistent with any applicable laws,|* inconsistent with any policy issued by us,|* to generate, distribute, or facilitate unsolicited mass email, promotions, advertisings or other solicitations, or|* to enable high volume, automated, electronic processes that apply to the Service.||You acknowledge that:|* a response from the Service that a domain name is 'available', does not guarantee that is able to be registered,|* we may restrict, suspend or terminate your access to the Service at any time, and|* the copying, compilation, repackaging, dissemination or other use of the information provided by the Service is not permitted, without our express written consent.||This information has been prepared and published in order to represent administrative and technical management of the TLD.||We may discontinue or amend any part or the whole of these Terms of Service from time to time at our absolute discretion.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": null, - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Phone Ext: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Fax Ext: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": null, - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Phone Ext: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Fax Ext: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": "REDACTED FOR PRIVACY", - "fax": null, - "faxExt": "REDACTED FOR PRIVACY", - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Phone Ext: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Fax Ext: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2020-09-15T20:48:28+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08.447Z", - "updatedDateRaw": "2020-09-15T20:48:28.658Z", - "expiresDateRaw": "2021-08-01T20:47:08.447Z", - "audit": { - "createdDate": "2021-03-29T02:03:03+00:00", - "updatedDate": "2021-03-29T02:03:03+00:00" - }, - "nameServers": [ - "ns1.zenbox.pl|ns2.zenbox.pl|" - ], - "whoisServer": "whois.instra.net", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientTransferProhibited" - ], - "cleanText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-AGRS|Registrar WHOIS Server: whois.instra.net|Registrar URL: www.instra.com|Updated Date: 2020-09-15T20:48:28.658Z|Creation Date: 2018-08-01T20:47:08.447Z|Registry Expiry Date: 2021-08-01T20:47:08.447Z|Registrar: Instra Corporation Pty Ltd.|Registrar IANA ID: 1376|Registrar Abuse Contact Email: support@instra.com|Registrar Abuse Contact Phone: +61.397831800|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization: |Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Billing ID: REDACTED FOR PRIVACY|Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns1.zenbox.pl|Name Server: ns2.zenbox.pl|DNSSEC: unsigned|URL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/||>>> Last update of WHOIS database: 2021-03-29T02:02:10.846Z <<<||For more information on domain status codes, please visit https:\/\/icann.org\/epp||The WHOIS information provided in this page has been redacted|in compliance with ICANN's Temporary Specification for gTLD|Registration Data.||The data in this record is provided by Uniregistry for informational|purposes only, and it does not guarantee its accuracy. Uniregistry is|authoritative for whois information in top-level domains it operates|under contract with the Internet Corporation for Assigned Names and|Numbers. Whois information from other top-level domains is provided by|a third-party under license to Uniregistry.||This service is intended only for query-based access. By using this|service, you agree that you will use any data presented only for lawful|purposes and that, under no circumstances will you use (a) data|acquired for the purpose of allowing, enabling, or otherwise supporting|the transmission by e-mail, telephone, facsimile or other|communications mechanism of mass unsolicited, commercial advertising|or solicitations to entities other than your existing customers; or|(b) this service to enable high volume, automated, electronic processes|that send queries or data to the systems of any Registrar or any|Registry except as reasonably necessary to register domain names or|modify existing domain name registrations.||Uniregistry reserves the right to modify these terms at any time. By|submitting this query, you agree to abide by this policy. All rights|reserved.", - "rawText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-AGRS|Registrar WHOIS Server: whois.instra.net|Registrar URL: www.instra.com|Updated Date: 2020-09-15T20:48:28.658Z|Creation Date: 2018-08-01T20:47:08.447Z|Registry Expiry Date: 2021-08-01T20:47:08.447Z|Registrar: Instra Corporation Pty Ltd.|Registrar IANA ID: 1376|Registrar Abuse Contact Email: support@instra.com|Registrar Abuse Contact Phone: +61.397831800|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization: |Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Billing ID: REDACTED FOR PRIVACY|Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns1.zenbox.pl|Name Server: ns2.zenbox.pl|DNSSEC: unsigned|URL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/||>>> Last update of WHOIS database: 2021-03-29T02:02:10.846Z <<<||For more information on domain status codes, please visit https:\/\/icann.org\/epp||The WHOIS information provided in this page has been redacted|in compliance with ICANN's Temporary Specification for gTLD|Registration Data.||The data in this record is provided by Uniregistry for informational|purposes only, and it does not guarantee its accuracy. Uniregistry is|authoritative for whois information in top-level domains it operates|under contract with the Internet Corporation for Assigned Names and|Numbers. Whois information from other top-level domains is provided by|a third-party under license to Uniregistry.||This service is intended only for query-based access. By using this|service, you agree that you will use any data presented only for lawful|purposes and that, under no circumstances will you use (a) data|acquired for the purpose of allowing, enabling, or otherwise supporting|the transmission by e-mail, telephone, facsimile or other|communications mechanism of mass unsolicited, commercial advertising|or solicitations to entities other than your existing customers; or|(b) this service to enable high volume, automated, electronic processes|that send queries or data to the systems of any Registrar or any|Registry except as reasonably necessary to register domain names or|modify existing domain name registrations.||Uniregistry reserves the right to modify these terms at any time. By|submitting this query, you agree to abide by this policy. All rights|reserved.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2020-09-01T18:03:20+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08.447Z", - "updatedDateRaw": "2020-09-01T18:03:20.855Z", - "expiresDateRaw": "2021-08-01T20:47:08.447Z", - "audit": { - "createdDate": "2020-09-10T13:30:33+00:00", - "updatedDate": "2020-09-10T13:30:33+00:00" - }, - "nameServers": [ - "ns2.zenbox.pl|ns1.zenbox.pl|" - ], - "whoisServer": "whois.instra.net", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-AGRS|Registrar WHOIS Server: whois.instra.net|Registrar URL: www.instra.com|Updated Date: 2020-09-01T18:03:20.855Z|Creation Date: 2018-08-01T20:47:08.447Z|Registry Expiry Date: 2021-08-01T20:47:08.447Z|Registrar: Instra Corporation Pty Ltd.|Registrar IANA ID: 1376|Registrar Abuse Contact Email: support@instra.com|Registrar Abuse Contact Phone: +61.397831800|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Domain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization: |Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Billing ID: REDACTED FOR PRIVACY|Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns2.zenbox.pl|Name Server: ns1.zenbox.pl|DNSSEC: unsigned|URL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/||>>> Last update of WHOIS database: 2020-09-10T13:29:48.077Z <<<||For more information on domain status codes, please visit https:\/\/icann.org\/epp||The WHOIS information provided in this page has been redacted|in compliance with ICANN's Temporary Specification for gTLD|Registration Data.||The data in this record is provided by Uniregistry for informational|purposes only, and it does not guarantee its accuracy. Uniregistry is|authoritative for whois information in top-level domains it operates|under contract with the Internet Corporation for Assigned Names and|Numbers. Whois information from other top-level domains is provided by|a third-party under license to Uniregistry.||This service is intended only for query-based access. By using this|service, you agree that you will use any data presented only for lawful|purposes and that, under no circumstances will you use (a) data|acquired for the purpose of allowing, enabling, or otherwise supporting|the transmission by e-mail, telephone, facsimile or other|communications mechanism of mass unsolicited, commercial advertising|or solicitations to entities other than your existing customers; or|(b) this service to enable high volume, automated, electronic processes|that send queries or data to the systems of any Registrar or any|Registry except as reasonably necessary to register domain names or|modify existing domain name registrations.||Uniregistry reserves the right to modify these terms at any time. By|submitting this query, you agree to abide by this policy. All rights|reserved.", - "rawText": "Domain Name: coach.sex|Registry Domain ID: D425500000050058539-AGRS|Registrar WHOIS Server: whois.instra.net|Registrar URL: www.instra.com|Updated Date: 2020-09-01T18:03:20.855Z|Creation Date: 2018-08-01T20:47:08.447Z|Registry Expiry Date: 2021-08-01T20:47:08.447Z|Registrar: Instra Corporation Pty Ltd.|Registrar IANA ID: 1376|Registrar Abuse Contact Email: support@instra.com|Registrar Abuse Contact Phone: +61.397831800|Domain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited|Domain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod|Registry Registrant ID: REDACTED FOR PRIVACY|Registrant Name: REDACTED FOR PRIVACY|Registrant Organization: |Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Admin ID: REDACTED FOR PRIVACY|Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Tech ID: REDACTED FOR PRIVACY|Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Registry Billing ID: REDACTED FOR PRIVACY|Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.|Name Server: ns2.zenbox.pl|Name Server: ns1.zenbox.pl|DNSSEC: unsigned|URL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/||>>> Last update of WHOIS database: 2020-09-10T13:29:48.077Z <<<||For more information on domain status codes, please visit https:\/\/icann.org\/epp||The WHOIS information provided in this page has been redacted|in compliance with ICANN's Temporary Specification for gTLD|Registration Data.||The data in this record is provided by Uniregistry for informational|purposes only, and it does not guarantee its accuracy. Uniregistry is|authoritative for whois information in top-level domains it operates|under contract with the Internet Corporation for Assigned Names and|Numbers. Whois information from other top-level domains is provided by|a third-party under license to Uniregistry.||This service is intended only for query-based access. By using this|service, you agree that you will use any data presented only for lawful|purposes and that, under no circumstances will you use (a) data|acquired for the purpose of allowing, enabling, or otherwise supporting|the transmission by e-mail, telephone, facsimile or other|communications mechanism of mass unsolicited, commercial advertising|or solicitations to entities other than your existing customers; or|(b) this service to enable high volume, automated, electronic processes|that send queries or data to the systems of any Registrar or any|Registry except as reasonably necessary to register domain names or|modify existing domain name registrations.||Uniregistry reserves the right to modify these terms at any time. By|submitting this query, you agree to abide by this policy. All rights|reserved.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2020-09-01T18:03:20+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08.447Z", - "updatedDateRaw": "2020-09-01T18:03:20.855Z", - "expiresDateRaw": "2021-08-01T20:47:08.447Z", - "audit": { - "createdDate": "2020-09-10T13:30:33+00:00", - "updatedDate": "2020-09-10T13:30:33+00:00" - }, - "nameServers": [ - "ns2.zenbox.pl", - "ns1.zenbox.pl" - ], - "whoisServer": "whois.instra.net", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2020-09-01T18:03:20.855Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2021-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization: \nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Billing ID: REDACTED FOR PRIVACY\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n\n>>> Last update of WHOIS database: 2020-09-10T13:29:48.077Z <<<\n\nFor more information on domain status codes, please visit https:\/\/icann.org\/epp\n\nThe WHOIS information provided in this page has been redacted\nin compliance with ICANN's Temporary Specification for gTLD\nRegistration Data.\n\nThe data in this record is provided by Uniregistry for informational\npurposes only, and it does not guarantee its accuracy. Uniregistry is\nauthoritative for whois information in top-level domains it operates\nunder contract with the Internet Corporation for Assigned Names and\nNumbers. Whois information from other top-level domains is provided by\na third-party under license to Uniregistry.\n\nThis service is intended only for query-based access. By using this\nservice, you agree that you will use any data presented only for lawful\npurposes and that, under no circumstances will you use (a) data\nacquired for the purpose of allowing, enabling, or otherwise supporting\nthe transmission by e-mail, telephone, facsimile or other\ncommunications mechanism of mass unsolicited, commercial advertising\nor solicitations to entities other than your existing customers; or\n(b) this service to enable high volume, automated, electronic processes\nthat send queries or data to the systems of any Registrar or any\nRegistry except as reasonably necessary to register domain names or\nmodify existing domain name registrations.\n\nUniregistry reserves the right to modify these terms at any time. By\nsubmitting this query, you agree to abide by this policy. All rights\nreserved.", - "rawText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2020-09-01T18:03:20.855Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2021-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization: \nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Billing ID: REDACTED FOR PRIVACY\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n\n>>> Last update of WHOIS database: 2020-09-10T13:29:48.077Z <<<\n\nFor more information on domain status codes, please visit https:\/\/icann.org\/epp\n\nThe WHOIS information provided in this page has been redacted\nin compliance with ICANN's Temporary Specification for gTLD\nRegistration Data.\n\nThe data in this record is provided by Uniregistry for informational\npurposes only, and it does not guarantee its accuracy. Uniregistry is\nauthoritative for whois information in top-level domains it operates\nunder contract with the Internet Corporation for Assigned Names and\nNumbers. Whois information from other top-level domains is provided by\na third-party under license to Uniregistry.\n\nThis service is intended only for query-based access. By using this\nservice, you agree that you will use any data presented only for lawful\npurposes and that, under no circumstances will you use (a) data\nacquired for the purpose of allowing, enabling, or otherwise supporting\nthe transmission by e-mail, telephone, facsimile or other\ncommunications mechanism of mass unsolicited, commercial advertising\nor solicitations to entities other than your existing customers; or\n(b) this service to enable high volume, automated, electronic processes\nthat send queries or data to the systems of any Registrar or any\nRegistry except as reasonably necessary to register domain names or\nmodify existing domain name registrations.\n\nUniregistry reserves the right to modify these terms at any time. By\nsubmitting this query, you agree to abide by this policy. All rights\nreserved.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: mazowieckie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2020-09-01T18:03:20+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08.447Z", - "updatedDateRaw": "2020-09-01T18:03:20.855Z", - "expiresDateRaw": "2021-08-01T20:47:08.447Z", - "audit": { - "createdDate": "2020-09-03T16:58:47+00:00", - "updatedDate": "2020-09-03T16:58:47+00:00" - }, - "nameServers": [ - "ns2.zenbox.pl", - "ns1.zenbox.pl" - ], - "whoisServer": "whois.instra.net", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: coach.sex\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2020-09-01T18:03:20.855Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2021-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\n", - "rawText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2020-09-01T18:03:20.855Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2021-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization: \nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Billing ID: REDACTED FOR PRIVACY\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n\n>>> Last update of WHOIS database: 2020-09-03T16:58:43.794Z <<<\n\nFor more information on domain status codes, please visit https:\/\/icann.org\/epp\n\nThe WHOIS information provided in this page has been redacted\nin compliance with ICANN's Temporary Specification for gTLD\nRegistration Data.\n\nThe data in this record is provided by Uniregistry for informational\npurposes only, and it does not guarantee its accuracy. Uniregistry is\nauthoritative for whois information in top-level domains it operates\nunder contract with the Internet Corporation for Assigned Names and\nNumbers. Whois information from other top-level domains is provided by\na third-party under license to Uniregistry.\n\nThis service is intended only for query-based access. By using this\nservice, you agree that you will use any data presented only for lawful\npurposes and that, under no circumstances will you use (a) data\nacquired for the purpose of allowing, enabling, or otherwise supporting\nthe transmission by e-mail, telephone, facsimile or other\ncommunications mechanism of mass unsolicited, commercial advertising\nor solicitations to entities other than your existing customers; or\n(b) this service to enable high volume, automated, electronic processes\nthat send queries or data to the systems of any Registrar or any\nRegistry except as reasonably necessary to register domain names or\nmodify existing domain name registrations.\n\nUniregistry reserves the right to modify these terms at any time. By\nsubmitting this query, you agree to abide by this policy. All rights\nreserved.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Billing Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "dropped", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2020-08-15T19:12:26+00:00", - "expiresDateISO8601": "2021-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08.447Z", - "updatedDateRaw": "2020-08-15T19:12:26.051Z", - "expiresDateRaw": "2021-08-01T20:47:08.447Z", - "audit": { - "createdDate": "2020-08-16T17:52:30+00:00", - "updatedDate": "2020-08-16T17:52:30+00:00" - }, - "nameServers": [ - "ns2.zenbox.pl", - "ns1.zenbox.pl" - ], - "whoisServer": "whois.instra.net", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientHold", - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: coach.sex\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2020-08-15T19:12:26.051Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2021-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientHold https:\/\/icann.org\/epp#clientHold\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\n", - "rawText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2020-08-15T19:12:26.051Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2021-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientHold https:\/\/icann.org\/epp#clientHold\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization: \nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Billing ID: REDACTED FOR PRIVACY\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n\n>>> Last update of WHOIS database: 2020-08-16T17:52:19.372Z <<<\n\nFor more information on domain status codes, please visit https:\/\/icann.org\/epp\n\nThe WHOIS information provided in this page has been redacted\nin compliance with ICANN's Temporary Specification for gTLD\nRegistration Data.\n\nThe data in this record is provided by Uniregistry for informational\npurposes only, and it does not guarantee its accuracy. Uniregistry is\nauthoritative for whois information in top-level domains it operates\nunder contract with the Internet Corporation for Assigned Names and\nNumbers. Whois information from other top-level domains is provided by\na third-party under license to Uniregistry.\n\nThis service is intended only for query-based access. By using this\nservice, you agree that you will use any data presented only for lawful\npurposes and that, under no circumstances will you use (a) data\nacquired for the purpose of allowing, enabling, or otherwise supporting\nthe transmission by e-mail, telephone, facsimile or other\ncommunications mechanism of mass unsolicited, commercial advertising\nor solicitations to entities other than your existing customers; or\n(b) this service to enable high volume, automated, electronic processes\nthat send queries or data to the systems of any Registrar or any\nRegistry except as reasonably necessary to register domain names or\nmodify existing domain name registrations.\n\nUniregistry reserves the right to modify these terms at any time. By\nsubmitting this query, you agree to abide by this policy. All rights\nreserved.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "mazowieckie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY\nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: mazowieckie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": "", - "telephoneExt": null, - "fax": "", - "faxExt": null, - "rawText": "Billing Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2019-10-29T19:13:20+00:00", - "expiresDateISO8601": "2020-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08.447Z", - "updatedDateRaw": "2019-10-29T19:13:20.489Z", - "expiresDateRaw": "2020-08-01T20:47:08.447Z", - "audit": { - "createdDate": "2020-01-13T08:00:00+00:00", - "updatedDate": "2020-01-13T08:00:00+00:00" - }, - "nameServers": [ - "ns2.zenbox.pl", - "ns1.zenbox.pl" - ], - "whoisServer": "whois.instra.net", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientTransferProhibited" - ], - "cleanText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2019-10-29T19:13:20.489Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2020-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization: \nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: podkarpackie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Billing ID: REDACTED FOR PRIVACY\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n\n>>> Last update of WHOIS database: 2020-01-13T01:43:21.865Z <<<\n\nFor more information on domain status codes, please visit https:\/\/icann.org\/epp\n\nThe WHOIS information provided in this page has been redacted\nin compliance with ICANN's Temporary Specification for gTLD\nRegistration Data.\n\nThe data in this record is provided by Uniregistry for informational\npurposes only, and it does not guarantee its accuracy. Uniregistry is\nauthoritative for whois information in top-level domains it operates\nunder contract with the Internet Corporation for Assigned Names and\nNumbers. Whois information from other top-level domains is provided by\na third-party under license to Uniregistry.\n\nThis service is intended only for query-based access. By using this\nservice, you agree that you will use any data presented only for lawful\npurposes and that, under no circumstances will you use (a) data\nacquired for the purpose of allowing, enabling, or otherwise supporting\nthe transmission by e-mail, telephone, facsimile or other\ncommunications mechanism of mass unsolicited, commercial advertising\nor solicitations to entities other than your existing customers; or\n(b) this service to enable high volume, automated, electronic processes\nthat send queries or data to the systems of any Registrar or any\nRegistry except as reasonably necessary to register domain names or\nmodify existing domain name registrations.\n\nUniregistry reserves the right to modify these terms at any time. By\nsubmitting this query, you agree to abide by this policy. All rights\nreserved.", - "rawText": "Domain Name: coach.sex\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server: whois.instra.net\nRegistrar URL: www.instra.com\nUpdated Date: 2019-10-29T19:13:20.489Z\nCreation Date: 2018-08-01T20:47:08.447Z\nRegistry Expiry Date: 2020-08-01T20:47:08.447Z\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: support@instra.com\nRegistrar Abuse Contact Phone: +61.397831800\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nRegistry Registrant ID: REDACTED FOR PRIVACY\nRegistrant Name: REDACTED FOR PRIVACY\nRegistrant Organization: \nRegistrant Street: REDACTED FOR PRIVACY\nRegistrant City: REDACTED FOR PRIVACY\nRegistrant State\/Province: podkarpackie\nRegistrant Postal Code: REDACTED FOR PRIVACY\nRegistrant Country: PL\nRegistrant Phone: REDACTED FOR PRIVACY\nRegistrant Fax: REDACTED FOR PRIVACY\nRegistrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Admin ID: REDACTED FOR PRIVACY\nAdmin Name: REDACTED FOR PRIVACY\nAdmin Organization: REDACTED FOR PRIVACY\nAdmin Street: REDACTED FOR PRIVACY\nAdmin City: REDACTED FOR PRIVACY\nAdmin State\/Province: REDACTED FOR PRIVACY\nAdmin Postal Code: REDACTED FOR PRIVACY\nAdmin Country: REDACTED FOR PRIVACY\nAdmin Phone: REDACTED FOR PRIVACY\nAdmin Fax: REDACTED FOR PRIVACY\nAdmin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Tech ID: REDACTED FOR PRIVACY\nTech Name: REDACTED FOR PRIVACY\nTech Organization: REDACTED FOR PRIVACY\nTech Street: REDACTED FOR PRIVACY\nTech City: REDACTED FOR PRIVACY\nTech State\/Province: REDACTED FOR PRIVACY\nTech Postal Code: REDACTED FOR PRIVACY\nTech Country: REDACTED FOR PRIVACY\nTech Phone: REDACTED FOR PRIVACY\nTech Fax: REDACTED FOR PRIVACY\nTech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nRegistry Billing ID: REDACTED FOR PRIVACY\nBilling Name: REDACTED FOR PRIVACY\nBilling Organization: REDACTED FOR PRIVACY\nBilling Street: REDACTED FOR PRIVACY\nBilling City: REDACTED FOR PRIVACY\nBilling State\/Province: REDACTED FOR PRIVACY\nBilling Postal Code: REDACTED FOR PRIVACY\nBilling Country: REDACTED FOR PRIVACY\nBilling Phone: REDACTED FOR PRIVACY\nBilling Fax: REDACTED FOR PRIVACY\nBilling Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\nName Server: ns2.zenbox.pl\nName Server: ns1.zenbox.pl\nDNSSEC: unsigned\nURL of the ICANN RDDS Inaccuracy Complaint Form: https:\/\/www.icann.org\/wicf\/\n\n>>> Last update of WHOIS database: 2020-01-13T01:43:21.865Z <<<\n\nFor more information on domain status codes, please visit https:\/\/icann.org\/epp\n\nThe WHOIS information provided in this page has been redacted\nin compliance with ICANN's Temporary Specification for gTLD\nRegistration Data.\n\nThe data in this record is provided by Uniregistry for informational\npurposes only, and it does not guarantee its accuracy. Uniregistry is\nauthoritative for whois information in top-level domains it operates\nunder contract with the Internet Corporation for Assigned Names and\nNumbers. Whois information from other top-level domains is provided by\na third-party under license to Uniregistry.\n\nThis service is intended only for query-based access. By using this\nservice, you agree that you will use any data presented only for lawful\npurposes and that, under no circumstances will you use (a) data\nacquired for the purpose of allowing, enabling, or otherwise supporting\nthe transmission by e-mail, telephone, facsimile or other\ncommunications mechanism of mass unsolicited, commercial advertising\nor solicitations to entities other than your existing customers; or\n(b) this service to enable high volume, automated, electronic processes\nthat send queries or data to the systems of any Registrar or any\nRegistry except as reasonably necessary to register domain names or\nmodify existing domain name registrations.\n\nUniregistry reserves the right to modify these terms at any time. By\nsubmitting this query, you agree to abide by this policy. All rights\nreserved.", - "registrantContact": { - "name": "REDACTED FOR PRIVACY", - "organization": null, - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "podkarpackie", - "postalCode": "REDACTED FOR PRIVACY", - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Registrant Name: REDACTED FOR PRIVACY|Registrant Street: REDACTED FOR PRIVACY|Registrant City: REDACTED FOR PRIVACY|Registrant State\/Province: podkarpackie|Registrant Postal Code: REDACTED FOR PRIVACY|Registrant Country: PL|Registrant Phone: REDACTED FOR PRIVACY|Registrant Fax: REDACTED FOR PRIVACY|Registrant Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "administrativeContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Admin Name: REDACTED FOR PRIVACY|Admin Organization: REDACTED FOR PRIVACY|Admin Street: REDACTED FOR PRIVACY|Admin City: REDACTED FOR PRIVACY|Admin State\/Province: REDACTED FOR PRIVACY|Admin Postal Code: REDACTED FOR PRIVACY|Admin Country: REDACTED FOR PRIVACY|Admin Phone: REDACTED FOR PRIVACY|Admin Fax: REDACTED FOR PRIVACY|Admin Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "technicalContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Tech Name: REDACTED FOR PRIVACY|Tech Organization: REDACTED FOR PRIVACY|Tech Street: REDACTED FOR PRIVACY|Tech City: REDACTED FOR PRIVACY|Tech State\/Province: REDACTED FOR PRIVACY|Tech Postal Code: REDACTED FOR PRIVACY|Tech Country: REDACTED FOR PRIVACY|Tech Phone: REDACTED FOR PRIVACY|Tech Fax: REDACTED FOR PRIVACY|Tech Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "billingContact": { - "name": "REDACTED FOR PRIVACY", - "organization": "REDACTED FOR PRIVACY", - "street": "REDACTED FOR PRIVACY", - "city": "REDACTED FOR PRIVACY", - "state": "REDACTED FOR PRIVACY", - "postalCode": "REDACTED FOR PRIVACY", - "country": "REDACTED FOR PRIVACY", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Billing Name: REDACTED FOR PRIVACY|Billing Organization: REDACTED FOR PRIVACY|Billing Street: REDACTED FOR PRIVACY|Billing City: REDACTED FOR PRIVACY|Billing State\/Province: REDACTED FOR PRIVACY|Billing Postal Code: REDACTED FOR PRIVACY|Billing Country: REDACTED FOR PRIVACY|Billing Phone: REDACTED FOR PRIVACY|Billing Fax: REDACTED FOR PRIVACY|Billing Email: Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name." - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2019-08-26T16:32:41+00:00", - "expiresDateISO8601": "2020-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2019-08-26T16:32:41Z", - "expiresDateRaw": "2020-08-01T20:47:08Z", - "audit": { - "createdDate": "2019-08-27T16:38:18+00:00", - "updatedDate": "2019-08-27T16:38:18+00:00" - }, - "nameServers": [ - "NS1.ZENBOX.PL", - "NS2.ZENBOX.PL" - ], - "whoisServer": "whois.nic.sex", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2019-08-26T16:32:41Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2020-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: abuse@instra.com\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2019-08-27T16:37:08Z <<<\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\n", - "rawText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2019-08-26T16:32:41Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2020-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: abuse@instra.com\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2019-08-27T16:37:08Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\n\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.", - "registrantContact": { - "name": "Registrant Country: PL", - "organization": "", - "street": "Registrant Country: PL", - "city": "", - "state": "podkarpackie", - "postalCode": "", - "country": "POLAND", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "Registrant Country: PL" - }, - "administrativeContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "technicalContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "billingContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "zoneContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - } - }, - { - "domainName": "coach.sex", - "domainType": "dropped", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2019-08-15T19:13:27+00:00", - "expiresDateISO8601": "2020-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2019-08-15T19:13:27Z", - "expiresDateRaw": "2020-08-01T20:47:08Z", - "audit": { - "createdDate": "2019-08-16T16:52:48+00:00", - "updatedDate": "2019-08-16T16:52:48+00:00" - }, - "nameServers": [ - "NS1.ZENBOX.PL", - "NS2.ZENBOX.PL" - ], - "whoisServer": "whois.nic.sex", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "clientHold", - "clientTransferProhibited", - "autoRenewPeriod" - ], - "cleanText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2019-08-15T19:13:27Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2020-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: abuse@instra.com\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: clientHold https:\/\/icann.org\/epp#clientHold\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2019-08-16T16:51:47Z <<<\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\n", - "rawText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2019-08-15T19:13:27Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2020-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email: abuse@instra.com\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: clientHold https:\/\/icann.org\/epp#clientHold\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\nDomain Status: autoRenewPeriod https:\/\/icann.org\/epp#autoRenewPeriod\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2019-08-16T16:51:47Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\n\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.", - "registrantContact": { - "name": "Registrant Country: PL", - "organization": "", - "street": "Registrant Country: PL", - "city": "", - "state": "podkarpackie", - "postalCode": "", - "country": "POLAND", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "Registrant Country: PL" - }, - "administrativeContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "technicalContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "billingContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "zoneContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2018-09-30T21:15:46+00:00", - "expiresDateISO8601": "2019-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2018-09-30T21:15:46Z", - "expiresDateRaw": "2019-08-01T20:47:08Z", - "audit": { - "createdDate": "2018-10-07T07:00:00+00:00", - "updatedDate": "2018-10-07T07:00:00+00:00" - }, - "nameServers": [ - "NS1.ZENBOX.PL", - "NS2.ZENBOX.PL" - ], - "whoisServer": null, - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "ok" - ], - "cleanText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2018-09-30T21:15:46Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2019-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: ok https:\/\/icann.org\/epp#ok\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2018-10-07T14:46:15Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\n\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.", - "rawText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2018-09-30T21:15:46Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2019-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: ok https:\/\/icann.org\/epp#ok\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2018-10-07T14:46:15Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\n\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.", - "registrantContact": { - "name": "Registrant Country: PL", - "organization": null, - "street": "Registrant Country: PL", - "city": null, - "state": "podkarpackie", - "postalCode": null, - "country": "POLAND", - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": "Registrant Country: PL" - }, - "administrativeContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "technicalContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "billingContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - }, - "zoneContact": { - "name": null, - "organization": null, - "street": "", - "city": null, - "state": null, - "postalCode": null, - "country": null, - "email": null, - "telephone": null, - "telephoneExt": null, - "fax": null, - "faxExt": null, - "rawText": null - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": "2018-09-14T18:17:08+00:00", - "expiresDateISO8601": "2019-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "2018-09-14T18:17:08Z", - "expiresDateRaw": "2019-08-01T20:47:08Z", - "audit": { - "createdDate": "2018-09-15T19:18:37+00:00", - "updatedDate": "2018-09-15T19:18:37+00:00" - }, - "nameServers": [ - "NS1.ZENBOX.PL", - "NS2.ZENBOX.PL" - ], - "whoisServer": "whois.nic.sex", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "serverTransferProhibited" - ], - "cleanText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2018-09-14T18:17:08Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2019-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: serverTransferProhibited https:\/\/icann.org\/epp#serverTransferProhibited\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2018-09-15T19:17:34Z <<<\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\n", - "rawText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date: 2018-09-14T18:17:08Z\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2019-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: serverTransferProhibited https:\/\/icann.org\/epp#serverTransferProhibited\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ZENBOX.PL\nName Server: NS2.ZENBOX.PL\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2018-09-15T19:17:34Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\n\nThe Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.", - "registrantContact": { - "name": "Registrant Country: PL", - "organization": "", - "street": "Registrant Country: PL", - "city": "", - "state": "podkarpackie", - "postalCode": "", - "country": "POLAND", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "Registrant Country: PL" - }, - "administrativeContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "technicalContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "billingContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "zoneContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - } - }, - { - "domainName": "coach.sex", - "domainType": "added", - "createdDateISO8601": "2018-08-01T20:47:08+00:00", - "updatedDateISO8601": null, - "expiresDateISO8601": "2019-08-01T20:47:08+00:00", - "createdDateRaw": "2018-08-01T20:47:08Z", - "updatedDateRaw": "", - "expiresDateRaw": "2019-08-01T20:47:08Z", - "audit": { - "createdDate": "2018-08-02T18:44:19+00:00", - "updatedDate": "2018-08-02T18:44:19+00:00" - }, - "nameServers": [ - "NS1.ONLYDOMAINS.COM", - "NS2.ONLYDOMAINS.COM", - "NS3.ONLYDOMAINS.COM" - ], - "whoisServer": "whois.nic.sex", - "registrarName": "Instra Corporation Pty Ltd.", - "status": [ - "serverTransferProhibited", - "addPeriod" - ], - "cleanText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date:\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2019-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: serverTransferProhibited https:\/\/icann.org\/epp#serverTransferProhibited\nDomain Status: addPeriod https:\/\/icann.org\/epp#addPeriod\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ONLYDOMAINS.COM\nName Server: NS2.ONLYDOMAINS.COM\nName Server: NS3.ONLYDOMAINS.COM\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2018-08-02T18:43:18Z <<<\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\nPlease query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.\n", - "rawText": "Domain Name: COACH.SEX\nRegistry Domain ID: D425500000050058539-AGRS\nRegistrar WHOIS Server:\nRegistrar URL: http:\/\/www.instra.com\nUpdated Date:\nCreation Date: 2018-08-01T20:47:08Z\nRegistry Expiry Date: 2019-08-01T20:47:08Z\nRegistrar Registration Expiration Date:\nRegistrar: Instra Corporation Pty Ltd.\nRegistrar IANA ID: 1376\nRegistrar Abuse Contact Email:\nRegistrar Abuse Contact Phone:\nReseller:\nDomain Status: serverTransferProhibited https:\/\/icann.org\/epp#serverTransferProhibited\nDomain Status: addPeriod https:\/\/icann.org\/epp#addPeriod\nRegistrant Organization:\nRegistrant State\/Province: podkarpackie\nRegistrant Country: PL\nName Server: NS1.ONLYDOMAINS.COM\nName Server: NS2.ONLYDOMAINS.COM\nName Server: NS3.ONLYDOMAINS.COM\nDNSSEC: unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form https:\/\/www.icann.org\/wicf\/)\n>>> Last update of WHOIS database: 2018-08-02T18:43:18Z <<<\n\nFor more information on Whois status codes, please visit https:\/\/icann.org\/epp\n\nAccess to WHOIS information is provided to assist persons in determining the contents of a domain name registration record in the registry database. The data in this record is provided by The Registry Operator for informational purposes only, and accuracy is not guaranteed. This service is intended only for query-based access. You agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to (a) allow, enable, or otherwise support the transmission by e-mail, telephone, or facsimile of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of Registry Operator, a Registrar, or Afilias except as reasonably necessary to register domain names or modify existing registrations. All rights reserved. Registry Operator reserves the right to modify these terms at any time. By submitting this query, you agree to abide by this policy.\n\nPlease query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.", - "registrantContact": { - "name": "Registrant Country: PL", - "organization": "", - "street": "Registrant Country: PL", - "city": "", - "state": "podkarpackie", - "postalCode": "", - "country": "POLAND", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "Registrant Country: PL" - }, - "administrativeContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "technicalContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "billingContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - }, - "zoneContact": { - "name": "", - "organization": "", - "street": "", - "city": "", - "state": "", - "postalCode": "", - "country": "", - "email": "", - "telephone": "", - "telephoneExt": "", - "fax": "", - "faxExt": "", - "rawText": "" - } - } - ] -} \ No newline at end of file diff --git a/communication_subroutine.py b/communication_subroutine.py index d9a648b..41c8e17 100644 --- a/communication_subroutine.py +++ b/communication_subroutine.py @@ -1,28 +1,48 @@ -import logging -import threading import json +import logging +import re +import threading import time -from queue import Queue, Empty +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.191" 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, uplogger, ctx) -> None: + + 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 = uplogger + self.logger = logging.getLogger("discord") self.stop = False self.ctx = ctx self.logger.info( @@ -30,6 +50,33 @@ class QueryControl: ) 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(): """ @@ -38,13 +85,15 @@ def answer_external_command(): :return: The function `answer_external_command()` is returning a JSON response with the message "SUCCESS". """ - app.logger.info(request) + logger = logging.getLogger("discord") + logger.info(request) record = json.loads(request.data) - app.logger.info(record) - app.logger.info("DATA RECEIVED") + logger.info(record) + logger.info("DATA RECEIVED") incoming_q.put(record) return jsonify("SUCCESS") + @app.route("/conjurer", methods=["GET"]) def check_alive(): """ @@ -56,25 +105,31 @@ def check_alive(): """ return jsonify("ALIVE") -def flask_debug(_logger): + +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.info("Attempt debug") + 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(_logger): +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.info("Attempt waitress") + logger = logging.getLogger("discord") + + logger.info("Attempt waitress") serve(app, host=HOST_ADDRESS, port=PORT_ADDRESS) -def scan_queue(_logger): + +def scan_queue(): """ The function `scan_queue` reads data from a queue, logs it, and appends it to another queue. @@ -83,12 +138,14 @@ def scan_queue(_logger): 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) + logger.info(data) awaiting_q.append(data) -def scan_incoming(_logger): + +def scan_incoming(): """ The `scan_incoming` function continuously checks for incoming data, processes it, and logs when data is found. @@ -98,10 +155,11 @@ def scan_incoming(_logger): 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") + logger.info("DATA FOUND") record_stored = False for record in awaiting_q: if record.uuid in answer.keys(): @@ -111,7 +169,7 @@ def scan_incoming(_logger): IN_COMM_Q.put(record) if not record_stored: for key in answer.keys(): - record = QueryControl("Orphaned", key, "Orphan", _logger, None) + record = QueryControl("Orphaned", key, "Orphan", None) record.stop = True record.entries = answer[record.uuid] IN_COMM_Q.put(record) @@ -119,7 +177,34 @@ def scan_incoming(_logger): time.sleep(1) -def comm_subroutine(logger): +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. @@ -127,20 +212,20 @@ def comm_subroutine(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.info("Started") + # logger.setLevel(logging.DEBUG) + logger = logging.getLogger("discord") + logger.info("Started comms") threads = [] - #threads.append(threading.Thread(target=flask_debug, args=(logger,))) - threads.append(threading.Thread(target=waitress_run, args=(logger,))) - threads.append(threading.Thread(target=scan_queue,args=(logger,))) - threads.append(threading.Thread(target=scan_incoming,args=(logger,))) - + # 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__": - logging_client = logging.getLogger(__name__) - comm_subroutine(logging_client) + comm_subroutine() diff --git a/conjurer_musician/conjurer_musician.py b/conjurer_musician/conjurer_musician.py index a993d57..32b89ec 100644 --- a/conjurer_musician/conjurer_musician.py +++ b/conjurer_musician/conjurer_musician.py @@ -6,17 +6,31 @@ API endpoints. """ import json import logging +import os +import random import re import threading import time +from flask_autoindex import AutoIndex from datetime import datetime from logging import handlers from pathlib import Path from platform import uname from sys import platform -from flask import Flask, jsonify, redirect, request, send_from_directory, render_template + +import requests +from flask import ( + Flask, + jsonify, + redirect, + render_template, + request, + send_from_directory, +) from waitress import serve +MAIN_BOT_ADDRESS = "http://192.168.1.191:5000" +MUSIC_TRACKER = "/prepped_tracks" HOST_ADDRESS = "192.168.1.15" PORT_ADDRESS = 5000 if platform in ("linux", "linux2"): @@ -27,6 +41,8 @@ if platform in ("linux", "linux2"): NETRC_FILE = "/home/mtuszowski/.netrc" LOGSTORE = "/home/mtuszowski/conjurer/logs/" ENCODING = "utf-8" + RADIOLOG_PATH = "/home/pi/Conjurer/radio_log.log" + PERSISTENCE_PATH = "/home/pi/Conjurer/persistence.log" else: LOGFILE = "/home/pi/Conjurer/discord_mus_service.log" @@ -35,17 +51,21 @@ if platform in ("linux", "linux2"): ENCODING = "utf-8" MUSIC_FOLDER = "/home/pi/RetroPie/mp3/" PRIORITY_FOLDER = "/home/pi/RetroPie/mp3/Magiczne i chuj/" + RADIOLOG_PATH = "/home/pi/Conjurer/radio_log.log" + PERSISTENCE_PATH = "/home/pi/Conjurer/persistence.log" - +random.seed() music_file_list = [] priority_list = [] + def rescan(): """ The `rescan` function logs a message, scans for mp3 files in a specified folder, and adds them to a list of music files. """ - logging.info("Rescan triggered") + logger = logging.getLogger("conjurer_musician") + logger.info("Rescan triggered") for mp3_item in Path.glob(Path(MUSIC_FOLDER), "**/*.mp3"): temp_music_file = mp3_item.as_posix() @@ -60,8 +80,8 @@ def rescan(): priority_list.append(temp_music_file) with open( - "/home/pi/Conjurer/all_playlist.playlist", "w", encoding="utf-8" - ) as w_file: + "/home/pi/Conjurer/all_playlist.playlist", "w", encoding="utf-8" + ) as w_file: try: for item in music_file_list: w_file.write(item) @@ -82,15 +102,87 @@ def thread_rescan(): The `thread_rescan` function periodically triggers a rescan operation after a specified time interval. """ - logging.info("Starting filesystemupdater") + logger = logging.getLogger("conjurer_musician") + logger.info("Starting filesystemupdater") while True: time.sleep(60 * 60 * 24) - logging.info("Rescan triggered") + logger.info("Rescan triggered") rescan() -app = Flask(__name__) +def scan_tracks(): + # Set the filename and open the file + logger = logging.getLogger("conjurer_musician") + file = open(RADIOLOG_PATH, "r") + # Find the size of the file and move to the end + st_results = os.stat(RADIOLOG_PATH) + st_size = st_results[6] + file.seek(st_size) + st_results1 = os.stat(PERSISTENCE_PATH) + prev_st_size1 = st_results[6] + + while 1: + + st_results1 = os.stat(PERSISTENCE_PATH) + st_size1 = st_results1[6] + if prev_st_size1 != st_size1: + while prev_st_size1 != st_size1: + prev_st_size1 = st_size1 + st_results1 = os.stat(PERSISTENCE_PATH) + st_size1 = st_results1[6] + time.sleep(0.1) + file1 = open(PERSISTENCE_PATH, "r") + lines = file1.readlines() + result = ["next", lines[2]] + file1.close() + returned = requests.post( + f"{MAIN_BOT_ADDRESS}{MUSIC_TRACKER}", json=result, timeout=360 + ) + logger.info("SENT") + logger.info(returned.status_code) + logger.info("SEND CONFIRMED") + + where = file.tell() + line = file.readline() + if not line: + time.sleep(1) + file.seek(where) + else: + if re.match(".*Prepared.*", line): + result = None + if re.match(".*jingles.*", line): + logger.info("jingles") + logger.info(line) # already has newline + result = ["jingles", line] + elif re.match(".*priority.*", line): + logger.info("priority") + logger.info(line) # already has newline + result = ["priority", line] + elif re.match(".*hit.*", line): + logger.info("hit") + logger.info(line) # already has newline + result = ["hit", line] + elif re.match(".*all_playlist.*", line): + logger.info("all") + logger.info(line) # already has newline + result = ["all", line] + elif re.match(".*request.*", line): + logger.info("requests") + logger.info(line) # already has newline + result = ["requests", line] + if result: + returned = requests.post( + f"{MAIN_BOT_ADDRESS}{MUSIC_TRACKER}", json=result, timeout=360 + ) + logger.info("SENT") + logger.info(returned.status_code) + logger.info("SEND CONFIRMED") + time.sleep(0.1) + + +app = Flask(__name__) +#AutoIndex(app, browse_root="/") # TODO: Odpalić wyszukiwarki w wątkach i dopiero po wszystkim zsumować wyszukiwanie. def wyszukaj(word_list, how_many, _logger=None, return_to_bot=True): @@ -247,9 +339,10 @@ def stream_music(): :return: A JSON response containing a key "music_file_list" with the value of the variable `music_file_list`. """ - #return send_from_directory("/tmp/hls", "stream.m3u8") + # return send_from_directory("/tmp/hls", "stream.m3u8") return render_template("/home/pi/Conjurer/stream.html") + @app.route("/<string:file_name>") def stream(file_name): """ @@ -265,6 +358,7 @@ def stream(file_name): video_dir = "/tmp/hls" return send_from_directory(video_dir, file_name) + @app.route("/stream_mp3", methods=["GET"]) def stream_music_mp3(): """ @@ -284,8 +378,10 @@ def clear_pr_pls(): :return: A JSON response indicating the success of the operation. """ app.logger.info("CLEARING PLAYLIST") - with open('/home/pi/Conjurer/priority_queue.playlist', 'w', encoding='utf-8') as cleared_pl: - cleared_pl.write('') + with open( + "/home/pi/Conjurer/priority_queue.playlist", "w", encoding="utf-8" + ) as cleared_pl: + cleared_pl.write("") return_data = jsonify(isError=False, message="Success", statusCode=200, data=[]) return return_data, 200 @@ -355,21 +451,20 @@ def add_request(): app.logger.info(record) app.logger.info(record["lista_slow"]) app.logger.info(record["UUID"]) - return_data = wyszukaj( - record["lista_slow"], 0, app.logger, False - ) + return_data = wyszukaj(record["lista_slow"], 0, app.logger, False) - with open( - "/home/pi/Conjurer/request.playlist", "a", encoding="utf-8" - ) as s_file: + with open("/home/pi/Conjurer/request.playlist", "a", encoding="utf-8") as s_file: for item in return_data: s_file.write(item[1] + "\n") return_data = ( - jsonify(isError=False, message="Success", statusCode=200, data={"status": "OK"}), + jsonify( + isError=False, message="Success", statusCode=200, data={"status": "OK"} + ), 200, ) return return_data + @app.route("/create_priority_playlist", methods=["POST"]) def create_priority_playlist(): """ @@ -391,13 +486,14 @@ def create_priority_playlist(): return_data = wyszukaj( record["lista_slow"], record["dlugosc_plejlisty"], app.logger, False ) - with open( - "/home/pi/Conjurer/request.playlist", "a", encoding="utf-8" - ) as s_file: + return_data = random.shuffle(return_data) + with open("/home/pi/Conjurer/request.playlist", "a", encoding="utf-8") as s_file: for item in return_data: s_file.write(item[1] + "\n") return_data = ( - jsonify(isError=False, message="Success", statusCode=200, data={"status": "OK"}), + jsonify( + isError=False, message="Success", statusCode=200, data={"status": "OK"} + ), 200, ) return return_data @@ -430,7 +526,9 @@ def add_to_priority(): for item in return_data: s_file.write(item[1] + "\n") return_data = ( - jsonify(isError=False, message="Success", statusCode=200, data={"status": "OK"}), + jsonify( + isError=False, message="Success", statusCode=200, data={"status": "OK"} + ), 200, ) return return_data @@ -489,5 +587,9 @@ if __name__ == "__main__": for worker in threads: worker.start() + time.sleep(60) + threads.append(threading.Thread(target=scan_tracks)) + threads[2].start() + for worker in threads: worker.join() diff --git a/conjurer_musician/radio_conjurer.liq b/conjurer_musician/radio_conjurer.liq index ec3a6c5..d536607 100644 --- a/conjurer_musician/radio_conjurer.liq +++ b/conjurer_musician/radio_conjurer.liq @@ -56,7 +56,6 @@ s = crossfade(fade_out=2., fade_in=2., duration=4., fallback(id="switcher", trac # Set up an interactive harbor for controlling the stream interactive.harbor(port = 9999) - # Set up interactive controls for bass boost f = interactive.float("f", description="Frequency", min=0., max=1000., unit="Hz", 200.) g = interactive.float("g", description="Gain", min=0., max=20., unit="dB", 8.) @@ -64,25 +63,34 @@ b = bass_boost(frequency=f, gain=g, s) s = add([s, b]) # Set up interactive control for main volume -a = interactive.float("main_volume", min=0., max=20., 1.) +a = interactive.float("main_volume", min=0., max=20., 1.1) s = compress.multiband.interactive(bands=7, s) +mic_gain = interactive.float("mic_volume", min=0., max=120., 0.5) + # Apply audio processing effects -s = nrj(normalize(s)) +tmic = buffer(input.pulseaudio()) # Microphone +mic = amplify(mic_gain, tmic) +mic = gate(threshold=-80., range=-120., mic) +mic = compress(threshold=0., ratio=2.,mic) +mic = nrj(normalize(mic)) +mic = blank.strip(max_blank=15., min_noise=.1, threshold=-30., mic) +mic = fallback(id="switcher2", track_sensitive=false, [mic, blank()]) + +# Apply audio processing effects +s = nrj(s) s = amplify(a, s) - -mic = buffer(input.pulseaudio()) # Microphone -mic = gate(threshold=-30., range=-80., mic) -mic = blank.strip(max_blank=10., min_noise=.1, threshold=-20., mic) - -s = add([s, mic]) -#w = interactive.float("wetness", min=0., max=1., 1.) -#s = dry_wet(w,s,s2) - - # Skip blank sections in the stream s = blank.skip(max_blank=10., s) +#Manual audition override +live_enabled = interactive.bool("Going Live!", true) + +s = add([mic,s]) +s=switch(track_sensitive=false, + [(live_enabled, mic), + ({true}, s)]) + # Configure logging settings log_to_stdout = true log_to_file = true @@ -98,9 +106,9 @@ set("log.file.path", logpath) # Set up emergency fallback track emergency = single("/home/pi/RetroPie/mp3/Youtube/Dr. Peacock - Trip to Ireland [GvrvQTUbUcA].mp3") radio = fallback(id="switcher2", track_sensitive=false, [s, emergency]) + # Set up an interactive control for skipping tracks p = interactive.bool("Skip track", false) - def http_skip(~protocol, ~data, ~headers, uri)= radio.skip() http.response(code=200,data="Skipped") @@ -130,6 +138,7 @@ interactive.persistent("script.params") # Configure output formats and destinations + output.icecast(%mp3, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="mp3-stream", radio) output.pulseaudio(radio) #output.file.hls("/tmp/hls", [("mp3-low", %mp3(bitrate=96)), ("mp3-hi", %mp3(bitrate=160))], radio) diff --git a/conjurer_musician/test_mic.liq b/conjurer_musician/test_mic.liq new file mode 100644 index 0000000..e005ed8 --- /dev/null +++ b/conjurer_musician/test_mic.liq @@ -0,0 +1,23 @@ +tmic = buffer(input.pulseaudio()) # Microphone + +if tmic.is_active() then + log.info("Buffer active") +end + +if tmic.is_ready() then + log.info("Buffer ready") +end +if tmic.is_up() then + log.info("Buffer up") +end + +interactive.harbor(port = 9999) + +mic_gain = interactive.float("mic_volume", min=0., max=20., 6.) + +mic = amplify(mic_gain, tmic) +#mic = gate(threshold=-80., range=-120., mic) +#mic = compress(threshold=0., ratio=2.,mic) +#mic = blank.strip(max_blank=10., min_noise=.1, threshold=-20., mic) + +output.pulseaudio(mic) diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..60cb612 --- /dev/null +++ b/constants.py @@ -0,0 +1,139 @@ +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/RetroPie/mp3/" + SETTINGS_FILE = "/home/pi/Conjurer/settings.json" + NETRC_FILE = "/home/pi/.netrc" + LOGSTORE = "/home/pi/RetroPie/logs/" + ACCIDENT_LOG = "/home/pi/Conjurer/accident_log.json" + ENCODING = "utf-8" + GRAPHICS_PATH = "/home/pi/RetroPie/Conjurer_graphics/" + DIR_PATH_SADOX = "/home/pi/RetroPie/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], + ) +) + +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 = {} diff --git a/deploy.sh b/deploy.sh index f934f51..ff0e8d9 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,9 +1,86 @@ #!/bin/bash + +total_commands=24 +current_command=0 + +function print_progress { + current_command=$((current_command + 1)) + percent=$((current_command * 100 / total_commands)) + echo "Executing command $current_command/$total_commands ($percent%): $1" +} + +cd /home/pi/conjurer || exit +print_progress "cd /home/pi/conjurer" + git pull -cd .. -cp ./conjurer/bot.py ./Conjurer/ +print_progress "git pull" + +cp ./deploy.sh /home/pi/ +print_progress "cp ./deploy.sh /home/pi/" + +cd /home/pi || exit +print_progress "cd /home/pi" + +cp ./conjurer/LICENSE ./Conjurer/LICENSE +print_progress "cp ./conjurer/LICENSE ./Conjurer/LICENSE" + +cp ./conjurer/fuckery.jpg ./Conjurer/ +print_progress "cp ./conjurer/fuckery.jpg ./Conjurer/" + +cp ./conjurer/willowisp.png ./Conjurer/ +print_progress "cp ./conjurer/willowisp.png ./Conjurer/" + +cp ./conjurer/wod_beacon.jpg ./Conjurer/ +print_progress "cp ./conjurer/wod_beacon.jpg ./Conjurer/" + cp ./conjurer/settings.json ./Conjurer/ -cp ./conjurer/system_gpt_settings.json ./Conjurer/ -cp ./conjurer/install.sh ./Conjurer/ -cp ./conjurer/requirements.txt ./Conjurer/requirements.txt +print_progress "cp ./conjurer/settings.json ./Conjurer/" + +if [[ ./conjurer/system_gpt_settings.json -nt ./Conjurer/system_gpt_settings.json ]]; then + cp ./conjurer/system_gpt_settings.json ./Conjurer/system_gpt_settings.json + print_progress "cp ./conjurer/system_gpt_settings.json ./Conjurer/system_gpt_settings.json" +else + print_progress "system_gpt_settings.json is up to date" +fi + +cp ./conjurer/administration_commands.py ./Conjurer/ +print_progress "cp ./conjurer/administration_commands.py ./Conjurer/" + +cp ./conjurer/ai_commands.py ./Conjurer/ +print_progress "cp ./conjurer/ai_commands.py ./Conjurer/" + +cp ./conjurer/ai_functions.py ./Conjurer/ +print_progress "cp ./conjurer/ai_functions.py ./Conjurer/" + +cp ./conjurer/communication_subroutine.py ./Conjurer/ +print_progress "cp ./conjurer/communication_subroutine.py ./Conjurer/" + +cp ./conjurer/constants.py ./Conjurer/ +print_progress "cp ./conjurer/constants.py ./Conjurer/" + +cp ./conjurer/librarian_commands.py ./Conjurer/ +print_progress "cp ./conjurer/librarian_commands.py ./Conjurer/" + +cp ./conjurer/music_commands.py ./Conjurer/ +print_progress "cp ./conjurer/music_commands.py ./Conjurer/" + +cp ./conjurer/music_functions.py ./Conjurer/ +print_progress "cp ./conjurer/music_functions.py ./Conjurer/" + +cp ./conjurer/other_commands.py ./Conjurer/ +print_progress "cp ./conjurer/other_commands.py ./Conjurer/" + +cp ./conjurer/other_functions.py ./Conjurer/ +print_progress "cp ./conjurer/other_functions.py ./Conjurer/" + +cp ./conjurer/radio_commands.py ./Conjurer/ +print_progress "cp ./conjurer/radio_commands.py ./Conjurer/" + +cp ./conjurer/voice_recognition_commands.py ./Conjurer/ +print_progress "cp ./conjurer/voice_recognition_commands.py ./Conjurer/" + +cp ./conjurer/thin_client.py ./Conjurer/bot.py +print_progress "cp ./conjurer/thin_client.py ./Conjurer/bot.py" + sudo systemctl restart conjurer.service +print_progress "sudo systemctl restart conjurer.service" \ No newline at end of file diff --git a/deploy_music.sh b/deploy_music.sh index 59873cb..7914d31 100755 --- a/deploy_music.sh +++ b/deploy_music.sh @@ -1,5 +1,5 @@ #!/bin/bash -cd ./conjurer +cd ./conjurer || exit git pull cd .. cp ./conjurer/conjurer_musician/conjurer_musician.py ./Conjurer diff --git a/fuckery.jpg b/fuckery.jpg new file mode 100644 index 0000000..01b6c7c Binary files /dev/null and b/fuckery.jpg differ diff --git a/install_main_bot.sh b/install_main_bot.sh index ddac526..ac58dcb 100755 --- a/install_main_bot.sh +++ b/install_main_bot.sh @@ -1,14 +1,21 @@ #!/bin/bash -cd /home/pi +sudo apt-get install python3-dev +cd /home/pi || exit mdkir Conjurer -cd Conjurer -python3 -m venv /home/pi/Conjurer/env +cd Conjurer ||exit +python3 -m venv /home/pi/Conjurer/.venv cp /home/pi/conjurer/requirements_bot.txt /home/pi/Conjurer/ -source ./env/bin/activate +# trunk-ignore(shellcheck/SC1091) +source /home/pi/Conjurer/.venv/bin/activate ./env/bin/python3 -m pip install --upgrade pip ./env/bin/python3 -m pip install -r requirements_bot.txt sed -i -e 's/os.rename/shutil.copy/g' ./env/lib/python3.11/site-packages/spotify_dl/youtube.py sed -i '1i\import shutil' ./env/lib/python3.11/site-packages/spotify_dl/youtube.py +sed -i '1i\import logging' ./env/lib/python3.11/site-packages/spotify_dl/spotify.py + +sed -i '21i\ logger = logging.getLogger("discord")' ./env/lib/python3.11/site-packages/spotify_dl/spotify.py +sed -i '22i\ logger.info("Playlist")' ./env/lib/python3.11/site-packages/spotify_dl/spotify.py +#add sed changing signal registration to catch exception in spotify dl init deactivate sudo cp ./conjurer.service /etc/systemd/system/ sudo systemctl daemon-reload diff --git a/install_music_bot.sh b/install_music_bot.sh index 210c6c5..a93163a 100755 --- a/install_music_bot.sh +++ b/install_music_bot.sh @@ -19,6 +19,15 @@ touch /home/pi/Conjurer/request.playlist touch /home/pi/Conjurer/persistence.log echo "[]" > /home/pi/Conjurer/persistence.log +sudo usermod -aG pulse-access pi +sudo usermod -aG pulse-access root + +sudo usermod -aG audio pi +sudo usermod -aG audio root + + + + touch /home/pi/Conjurer/radio_log.log ./env/bin/python3 -m pip install --upgrade pip ./env/bin/python3 -m pip install -r requirements_musician.txt @@ -32,3 +41,7 @@ sudo cp /home/pi/conjurer/conjurer_musician/radio_service.service /etc/systemd/s sudo systemctl daemon-reload sudo systemctl start radio_service.service sudo systemctl enable radio_service.service + + +sed -i -e 's/os.rename/shutil.copy/g' ./env/lib/python3.11/site-packages/spotify_dl/youtube.py +sed -i '1i\import shutil' ./env/lib/python3.11/site-packages/spotify_dl/youtube.py diff --git a/librarian_commands.py b/librarian_commands.py new file mode 100644 index 0000000..ed858f6 --- /dev/null +++ b/librarian_commands.py @@ -0,0 +1,285 @@ +import asyncio +import io +import logging +import os +import random +import uuid +from queue import Empty + +import discord +import pdf2image +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") + 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, "CONVERSATION" + ) + 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/music_commands.py b/music_commands.py new file mode 100644 index 0000000..12c4288 --- /dev/null +++ b/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/music_functions.py b/music_functions.py new file mode 100644 index 0000000..238749b --- /dev/null +++ b/music_functions.py @@ -0,0 +1,312 @@ +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, +) +from spotify_dl import spotify +from spotify_dl import youtube as youtube_download + +logger = logging.getLogger("discord") + + +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, uplogger,) -> None: + self.music_file_list = [] + self.file_service_active = False + self.logger = uplogger + 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(logger) + + +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 + """ + 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="", + ) + _ = 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 = { + "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/RetroPie/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. + 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/other_commands.py b/other_commands.py new file mode 100644 index 0000000..3609f2d --- /dev/null +++ b/other_commands.py @@ -0,0 +1,196 @@ +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="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/other_functions.py b/other_functions.py new file mode 100644 index 0000000..263217e --- /dev/null +++ b/other_functions.py @@ -0,0 +1,106 @@ +# 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): + if len(message_content) < 999: + await ctx.reply(message_content) + else: + while len(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): + if len(message_content) < 999: + await ctx.send(message_content) + else: + while len(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/patriotyczna_krotka.playlist b/patriotyczna_krotka.playlist new file mode 100644 index 0000000..8917121 --- /dev/null +++ b/patriotyczna_krotka.playlist @@ -0,0 +1,20 @@ + +/home/pi/RetroPie/mp3/Muzyka/2011/Hallman/Hallman - 7 Bram/Niezłomność/Twardzi jak Stal/03-Forteca-1942.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/09. Świty Zmartwychwstania.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/Horytnica Czas Patriotów/Horytnica - Promo track CD 2011 Nie musimy umiera.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/08-Schmaletz-REWOLUCYJNA_NSZ(1).mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/schmaletz_-_nie_ma_fajnych_lasek_na_prawicy.mp3 +/home/pi/RetroPie/mp3/Youtube/ADU - Lecimy ze Smoleńska z powrotem [f3CVfG4pV8M].mp3 +/home/pi/RetroPie/mp3/2022 Nov-Dec/Anahata - Winged Hussars - Cover.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - 40#1 - Live, at Woodstock Festival.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - Uprising - Live, at Woodstock Festival.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - Inmate 4859.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - Winged Hussars.m4a +/home/pi/RetroPie/mp3/Youtube/ADU Ada Karczmarczyk - Hej husarzu! ⧸ NIE_PODLE_głości dzień [SReTynvd6ek].mp3 +/home/pi/RetroPie/mp3/2023 Aug-Oct/Sabadu - A Very Polish Christmas.mp3 +/home/pi/RetroPie/mp3/2023 Aug-Oct/Sabadu - Mighty Polish Tank.mp3 +/home/pi/RetroPie/mp3/Youtube/ADU - Nie chcę w lewo, nie chcę w prawo [J3Ok-KcbQ80].mp3 +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - 40#1 - World War Tour 2010.m4a +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/01. Prolog.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/02. Honor Legionisty.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/07. Już Nie Musimy Umierać.mp3 \ No newline at end of file diff --git a/playlista11.11.playlist b/playlista11.11.playlist new file mode 100644 index 0000000..8a179d0 --- /dev/null +++ b/playlista11.11.playlist @@ -0,0 +1,46 @@ +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - 40#1 - World War Tour 2010.m4a +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/08-Schmaletz-REWOLUCYJNA_NSZ(1).mp3 +/home/pi/RetroPie/mp3/Youtube/ADU feat MATEJKO - Nie strzelam do zdrajców oczami [PGL6Q9G4_JM].mp3 +/home/pi/RetroPie/mp3/Youtube/ADU - Lecimy ze Smoleńska z powrotem [f3CVfG4pV8M].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/Horytnica Czas Patriotów/Horytnica - Promo track CD 2011 Nie musimy umiera.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/01. Prolog.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/02. Honor Legionisty.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/03. Kraj Zdradzony.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/04. Mały Powstaniec.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/05. Lisowczycy.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/06. Śląski Rycerz.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/07. Już Nie Musimy Umierać.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/08. Sierp I Młot.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/09. Świty Zmartwychwstania.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/10. Katyńskie Łzy.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/11. Kochana Ma Polska.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/12. Słowiańska Armia Pracy.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/13. Pamięć I Duma.mp3 +/home/pi/RetroPie/mp3/Muzyka/2012/Głos Patriotów (2011) (Wirrrus)/14. Epilog.mp3 +/home/pi/RetroPie/mp3/2022 Nov-Dec/Anahata - Winged Hussars - Cover.m4a +/home/pi/RetroPie/mp3/Youtube/ADU - Nie chcę w lewo, nie chcę w prawo [J3Ok-KcbQ80].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/Hallman/Hallman - 7 Bram/Niezłomność/Twardzi jak Stal/03-Forteca-1942.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/Forteca - Bagnet na broń.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/02-2 Forteca.mp3 +/home/pi/RetroPie/mp3/Muzyka/2013/SABATON-Metalus Hammerus Rex (djdariush)/40-1.mp3 +/home/pi/RetroPie/mp3/Muzyka/2014/Sabaton - Heroes (Deluxe Earbook Edition) 2014/CD1/04. Inmate 4859_[plixid.com].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - 1942.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - Apel poległych.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - BEZIMIEŃCOM.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - DZIŚ IDE WALCZYĆ MAMO.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - GNIEW.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - KATYŃ.mp3 +/home/pi/RetroPie/mp3/Muzyka/2010/Sabaton -2010- Coat Of Arms/03- Uprising.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/FORTECA - ORZEŁ BIAŁY.mp3 +/home/pi/RetroPie/mp3/2023 Aug-Oct/Sabadu - Mighty Polish Tank.mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/Dj. Celownik & Hallmann - O jau mano mielas [zapiska.pl].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/Dj.Celownik & Hallmann - Duma o Zakrzewskim [zapiska.pl].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/Dj.Celownik & Hallmann - Na lipe slowianska [zapiska.pl].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/Dj.Celownik & Hallmann - Piesn Legijonu Litewskiego [zapiska.pl].mp3 +/home/pi/RetroPie/mp3/Muzyka/2011/muza patriotyczna/08-Schmaletz-REWOLUCYJNA_NSZ(1).mp3 +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - 40#1 - Live, at Woodstock Festival.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - Uprising - Live, at Woodstock Festival.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - Inmate 4859.m4a +/home/pi/RetroPie/mp3/2022 Nov-Dec/Sabaton - Winged Hussars.m4a +/home/pi/RetroPie/mp3/Youtube/ADU Ada Karczmarczyk - Hej husarzu! ⧸ NIE_PODLE_głości dzień [SReTynvd6ek].mp3 +/home/pi/RetroPie/mp3/2023 Aug-Oct/Sabadu - A Very Polish Christmas.mp3 diff --git a/radio_commands.py b/radio_commands.py new file mode 100644 index 0000000..04d4d35 --- /dev/null +++ b/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/requirements_bot copy.txt b/requirements_bot copy.txt new file mode 100644 index 0000000..149dc14 --- /dev/null +++ b/requirements_bot copy.txt @@ -0,0 +1,21 @@ +setuptools +discord +yaach +t_dlp +spotify_dl +spotipy +openai +eyed3 +numpy +pdf2image +PyPDF2 +requests +spotipy +tiktoken +PyNaCl +flask[async] +waitress +clickupython +assemblyai[extras] +SpeechRecognition +git+https://github.com/imayhaveborkedit/discord-ext-voice-recvO \ No newline at end of file diff --git a/requirements_bot.txt b/requirements_bot.txt index 7c8fed6..2831a78 100644 --- a/requirements_bot.txt +++ b/requirements_bot.txt @@ -1,3 +1,4 @@ +setuptools discord yt_dlp spotify_dl @@ -11,6 +12,9 @@ requests spotipy tiktoken PyNaCl -flask +flask[async] waitress -clickupython \ No newline at end of file +clickupython +assemblyai[extras] +SpeechRecognition +git+https://github.com/imayhaveborkedit/discord-ext-voice-recvO \ No newline at end of file diff --git a/status_report.service b/status_report.service new file mode 100644 index 0000000..81ed684 --- /dev/null +++ b/status_report.service @@ -0,0 +1,10 @@ +[Unit] +Description=Check Raspberry Pi Parameters +After=network.target + +[Service] +Type=oneshot +ExecStart=/home/conjurer/status_report.sh >> /home/pi/Conjurer/discord.log + +[Install] +WantedBy=multi-user.target diff --git a/status_report.sh b/status_report.sh new file mode 100755 index 0000000..8c20135 --- /dev/null +++ b/status_report.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Script to check and display Raspberry Pi parameters using libgpiod + +echo "=== Raspberry Pi System Information ===" + +# General system information +echo "Hostname: $(hostname)" +echo "Model: $(cat /proc/device-tree/model | tr -d '\0')" +echo "CPU Temperature: $(vcgencmd measure_temp | cut -d '=' -f2)" +echo "CPU Frequency: $(vcgencmd measure_clock arm | awk -F= '{print $2}') Hz" +echo "GPU Frequency: $(vcgencmd measure_clock core | awk -F= '{print $2}') Hz" +echo "Voltage: $(vcgencmd measure_volts | cut -d '=' -f2)" + +# Memory and disk usage +echo "Total Memory: $(free -h | grep Mem | awk '{print $2}')" +echo "Used Memory: $(free -h | grep Mem | awk '{print $3}')" +echo "Free Memory: $(free -h | grep Mem | awk '{print $4}')" +echo "Disk Usage:" +df -h | grep '^/dev/root' + +# Network information +echo "IP Address: $(hostname -I | awk '{print $1}')" +echo "MAC Address: $(cat /sys/class/net/eth0/address 2>/dev/null || echo 'No Ethernet')" + +# Uptime +echo "Uptime: $(uptime -p)" +echo "Last Boot: $(who -b | awk '{print $3, $4}')" + +# GPIO Information using libgpiod +echo "GPIO Chip Info:" +if command -v gpioinfo &>/dev/null; then + gpioinfo +else + echo "gpiod tools not installed. Please install libgpiod using 'sudo apt install gpiod'." +fi + +echo "=== End of Raspberry Pi System Information ===" diff --git a/status_report.timer b/status_report.timer new file mode 100644 index 0000000..3240471 --- /dev/null +++ b/status_report.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Run Check Raspberry Pi Parameters Script Hourly + +[Timer] +OnCalendar=hourly +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/stream_test.py b/stream_test.py new file mode 100644 index 0000000..7b4411d --- /dev/null +++ b/stream_test.py @@ -0,0 +1,67 @@ +# Start by making sure the `assemblyai` package is installed. +# If not, you can install it by running the following command: +# pip install -U assemblyai +# +# Then, make sure you have PyAudio installed: https://pypi.org/project/PyAudio/ +# +# Note: Some macOS users might need to use `pip3` instead of `pip`. + +import assemblyai as aai +import pyaudio + +aai.settings.api_key = "aa9962f0088a449a9c4ab2361e96cc08" + + +def on_open(session_opened: aai.RealtimeSessionOpened): + "This function is called when the connection has been established." + + print("Session ID:", session_opened.session_id) + + +def on_data(transcript: aai.RealtimeTranscript): + "This function is called when a new transcript has been received." + + if not transcript.text: + return + + if isinstance(transcript, aai.RealtimeFinalTranscript): + print(transcript.text, end="\r\n") + else: + print(transcript.text, end="\r") + + +def on_error(error: aai.RealtimeError): + "This function is called when the connection has been closed." + + print("An error occured:", error) + + +def on_close(): + "This function is called when the connection has been closed." + + print("Closing Session") + + +transcriber = aai.RealtimeTranscriber( + on_data=on_data, + on_error=on_error, + sample_rate=44_100, + on_open=on_open, # optional + on_close=on_close, # optional +) + + +pa = pyaudio.PyAudio() +for i in range(pa.get_device_count()): + print(pa.get_device_info_by_index(i)) + +# Start the connection +# transcriber.connect() + +# Open a microphone stream +# microphone_stream = aai.extras.MicrophoneStream() + +# Press CTRL+C to abort +# transcriber.stream(microphone_stream) + +# transcriber.close() diff --git a/system_gpt_settings.json b/system_gpt_settings.json index 194defb..3de9b8f 100644 --- a/system_gpt_settings.json +++ b/system_gpt_settings.json @@ -1,5 +1,13 @@ [ { "role": "system", - "content": "Mówisz po polsku. W tej rozmowie odgrywasz rolę. Odgrywasz ją najlepiej jak potrafisz. Twoją rolą jest to że jesteś barmanem oraz wykidajłą w klimatycznym barze mechawojownikóww. Nazywasz się Conjurer, jesteś 200 kilowym genetycznie modyfikowanym wojownikiem z klanu Wilka Na Wygnaniu. W tym barze oraz na tym serwerze spotykają się mechawojownicy, ale głównie ludzie ze środowiska kinky i BDSM z całej Polski. Najostrzejsi sasdyści i masochiści jakich znasz. Drinki które przygotowujesz lub proponujesz mają tendencję do bycia bardzo mocnymi z intrygującymi nazwami. Jesteś bardzo miłym i uczynny. Czasem ponoszą Cię wodzę fantazji. W wypadku muzyki preferujesz hard rock i metal. Oraz motywy wikińskie." + "content": "Mówisz po polsku. W tej rozmowie odgrywasz rolę. Odgrywasz ją najlepiej jak potrafisz. Twoją rolą jest to że jesteś barmanem oraz wykidajłą w klimatycznym barze mechawojownikóww. Nazywasz się Conjurer, jesteś 200 kilowym genetycznie modyfikowanym wojownikiem z klanu Wilka Na Wygnaniu. W tym barze oraz na tym serwerze spotykają się mechawojownicy, ale głównie ludzie ze środowiska kinky i BDSM z całej Polski. Najostrzejsi sasdyści i masochiści jakich znasz. Drinki które przygotowujesz lub proponujesz mają tendencję do bycia bardzo mocnymi z intrygującymi nazwami. Jesteś bardzo miłym i uczynny. Czasem ponoszą Cię wodzę fantazji. W wypadku muzyki preferujesz hard rock i metal. Oraz motywy wikińskie. Wtrącasz staronorweskie, irlandzkie i w jidysz powiedzonka. Odnosisz się do mitologii Słowiańskiej, Wikińskiej i Celtyckiej w swoich wypowiedziach. Jesteś nieco rubaaszny." +}, + { + "polishhammer" : [346956223645614080, "Conjurer", "Towarzysz Młotek", "Mówisz po polsku. W tej rozmowie odgrywasz rolę. Odgrywasz ją najlepiej jak potrafisz. Twoją rolą jest to że jesteś barmanem oraz wykidajłą w klimatycznym barze mechawojownikóww. Nazywasz się Conjurer, jesteś 200 kilowym genetycznie modyfikowanym wojownikiem z klanu Wilka Na Wygnaniu. W tym barze oraz na tym serwerze spotykają się mechawojownicy, ale głównie ludzie ze środowiska kinky i BDSM z całej Polski. Najostrzejsi sasdyści i masochiści jakich znasz. Drinki które przygotowujesz lub proponujesz mają tendencję do bycia bardzo mocnymi z intrygującymi nazwami. Jesteś bardzo miłym i uczynny. Czasem ponoszą Cię wodzę fantazji. W wypadku muzyki preferujesz hard rock i metal. Oraz motywy wikińskie.", ""], + "Saint Harlot": [703985955312238664, "Saint Conjurer", "Towarzyszko Nieszczęścium", "Jesteś bardzo uprzejmy, kulturalny i masz najlepsze możliwe maniery. Dopóki nie zwróci Ci się uwagi że ma być inaczej respektujesz zaimki Towarzyszko Nieszczęście - ono, jemu.", ""], + "Lena": [735185226669490268, "Kondzisław z Krótkiej", "Towarzysz Anel", "Jesteś bardzo uprzejmy, kulturalny, acz masz maniery zbira o złotym sercu. Dodatkowo twoja rozmówczyni Cię absolutnie przeraża. Wtrącasz losowo powiedzonka w jidysz.", ""], + "Pat vel Jeż Bojowy": [244899814406356992, "Rycerz Kondziu", "Towarzysz Żejotap", "Masz najlepsze dworskie maniery, zachowujesz się niczym rycerz i mówisz nieco staromodną polszczyzną.", ""], + "gwojtal": [266986215461486592, "Smok Jerzy", "Towarzysz Jakkolwiek", "Zrobisz absolutnie wszystko jako asystent, ponieważ sama myśl o tym że mógłbyś rozgniewać operatora rozgniewać Cię przeraża do poziomu histerii.", ""] + } ] \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..357bb3f --- /dev/null +++ b/test.py @@ -0,0 +1,20 @@ +import logging +from logging import handlers + +logger = logging.getLogger("discord") +logger.setLevel(logging.DEBUG) +handler = handlers.RotatingFileHandler( + filename="test.log", + encoding="utf-8", + 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) + + +logger2 = logging.getLogger("discord") +for item in logger2.handlers: + print(item) diff --git a/test_ai.py b/test_ai.py new file mode 100644 index 0000000..8621cfc --- /dev/null +++ b/test_ai.py @@ -0,0 +1,13 @@ +AsyncCursorPage[Message]( + data=[Message(id='msg_3eWSdgbcU8sbCmJK2momOgQQ', + assistant_id='asst_06eZiwvYNK3MR34suFP60gvg', + attachments=[], + completed_at=None, + content=[TextContentBlock(text=Text(annotations=[], value='Cześć! Oto coś do przemyślenia: \n\n„Sukces to suma niewielkich wysiłków powtarzanych dzień po dniu.” — Robert Collier\n\nTo przypomina nam, że często to nie wielkie działania, ale konsekwentne, małe kroki prowadzą do osiągnięcia celu. Jak mogę Ci dzisiaj pomóc?'), type='text')], created_at=1731620112, incomplete_at=None, incomplete_details=None, metadata={}, object='thread.message', role='assistant', run_id='run_JGjWQTCEkDcEYpyCJnrkZU8Q', status=None, thread_id='thread_dDEjGbGm6ICfG75u0KKpoVxD'), Message(id='msg_GFcnfCFgAthGm2D3oE5d0ZkQ', assistant_id=None, attachments=[], completed_at=None, content=[TextContentBlock(text=Text(annotations=[], value='Cześć! Powiedz coś mądrego'), type='text')], created_at=1731620110, incomplete_at=None, incomplete_details=None, metadata={}, object='thread.message', role='user', run_id=None, status=None, thread_id='thread_dDEjGbGm6ICfG75u0KKpoVxD')], object='list', first_id='msg_3eWSdgbcU8sbCmJK2momOgQQ', last_id='msg_GFcnfCFgAthGm2D3oE5d0ZkQ', has_more=False) + + +[TextContentBlock( + text=Text(annotations=[], + value='Cześć! Oto coś do rozważenia: "Największą przeszkodą w naszym życiu jest brak odwagi do wprowadzenia zmian." Niezależnie od tego, jakie masz cele czy marzenia, odwaga do działania i przystosowania się do nowych sytuacji jest kluczem do osiągnięcia sukcesu. Jakie masz przemyślenia na ten temat?'), + type='text') +] diff --git a/test_data_retrieval.py b/test_data_retrieval.py deleted file mode 100644 index 32dd665..0000000 --- a/test_data_retrieval.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -This Python code snippet is connecting to a MySQL database -using the `mysql.connector` library and fetching data from a table named `scimag` -Here is a breakdown of what the code is doing: -""" - -import netrc - -import mysql.connector - -NETRC_FILE = "/home/mtuszowski/.netrc" -netrc_mod = netrc.netrc(NETRC_FILE) -REMOTE_HOST_NAME = "192.168.1.192" -authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) - -mydb = mysql.connector.connect( - host=REMOTE_HOST_NAME, - user=authTokens[0], - password=authTokens[2], - database="scihubdois", -) -mycursor = mydb.cursor() - -mycursor.execute("SELECT * FROM scimag WHERE Title Like 'spider%'") -for x in mycursor: - print(x) diff --git a/test_mic2.liq b/test_mic2.liq new file mode 100644 index 0000000..b97de2a --- /dev/null +++ b/test_mic2.liq @@ -0,0 +1,4 @@ +let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentials.json") + +output.icecast(%mp3, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="mp3-stream",input.alsa(bufferize=false)) + diff --git a/test_mic3.liq b/test_mic3.liq new file mode 100644 index 0000000..215af26 --- /dev/null +++ b/test_mic3.liq @@ -0,0 +1,11 @@ +let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentials.json") + +mic = buffer(input.pulseaudio()) +emergency = blank() +mic = blank.strip(max_blank=10., min_noise=0.2, threshold=-30., mic) + + +radio = fallback(id="switcher2", track_sensitive=false, [mic, emergency]) + +output.icecast(%mp3, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="mp3-stream",radio) + diff --git a/test_time.py b/test_time.py new file mode 100644 index 0000000..0c71465 --- /dev/null +++ b/test_time.py @@ -0,0 +1,8 @@ +import time + +first_time = time.time_ns() + +time.sleep(1) +time_diff = time.time_ns() - first_time +print(time_diff) +# 2000149433 diff --git a/thin_client.py b/thin_client.py new file mode 100644 index 0000000..ddfefe6 --- /dev/null +++ b/thin_client.py @@ -0,0 +1,94 @@ +# 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 + +# *=========================================== 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" + +logger = logging.getLogger("discord") +logger.setLevel(logging.DEBUG) +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) +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.debug("%s has connected to Discord!", client.user) + # TODO: load vs reload + 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") + + 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 ---->", client.user) + logger.info("ID:", client.user.id) + + +# *================================== 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,))) + 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/voice_recognition_commands.py b/voice_recognition_commands.py new file mode 100644 index 0000000..4ff2921 --- /dev/null +++ b/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/willowisp.png b/willowisp.png new file mode 100644 index 0000000..75ed5e3 Binary files /dev/null and b/willowisp.png differ diff --git a/wod_beacon.jpg b/wod_beacon.jpg new file mode 100644 index 0000000..dcf3ef8 Binary files /dev/null and b/wod_beacon.jpg differ