perefouras/fouras.py

245 lines
9.6 KiB
Python

from discord.ext import tasks
from datetime import datetime, timedelta, timezone
from dotenv import load_dotenv
import discord
import os
import asyncio
import random
import re
import json
import time
intents = discord.Intents.default()
intents.members = True
intents.presences = True
intents.guilds = True
intents.messages = True
intents.message_content = True
client = discord.Client(intents=intents)
load_dotenv()
token = os.getenv('DISCORD_TOKEN', "NO_TOKEN")
client.riddles = []
client.answers = []
client.rhyme_keys = {}
client.rhyme_strings = {}
client.cooldown = {}
client.ongoing_riddles = {}
def load_riddles():
with open('riddles.txt', 'r', encoding='utf-8') as r_file:
client.riddles = r_file.read().split('\n\n')
with open('answers.txt', 'r', encoding='utf-8') as a_file:
client.answers = [line.strip() for line in a_file.readlines()]
print('Loaded {0} riddles'.format(len(client.riddles)))
def load_rhymes():
with open('rhymes.txt', 'r', encoding='utf-8') as f:
data = f.read()
keys_start = data.index('[KEYS]') + len('[KEYS]\n')
keys_end = data.index('[RHYMES]')
keys_data = data[keys_start:keys_end].split('\n')
keys = {}
for key_data in keys_data:
if key_data:
k, v = key_data.split(":")
keys[k] = v.split(',')
rhymes_start = data.index('[RHYMES]') + len('[RHYMES]\n')
rhymes_data = data[rhymes_start:].split('\n')
rhymes = {}
for rhyme_data in rhymes_data:
if rhyme_data:
k, v = rhyme_data.split(":")
rhymes[k] = v.split(',')
client.rhyme_keys = keys
client.rhyme_strings = rhymes
def new_riddle(channel, index):
current_riddle = dict(index=index, nbClues=-1, riddle=client.riddles[index].strip(), answer=client.answers[index])
client.ongoing_riddles[channel] = current_riddle
return format_message(current_riddle)
def finish_riddle(channel):
del client.ongoing_riddles[channel]
def clue_string(answer, nbClues):
finalString = "_"
for i in range(len(answer) - 1):
finalString += " _"
random.seed(hash(answer))
nbRevealed = 0
for i in range(nbClues):
id = random.randint(0, len(answer) - 1)
while finalString[id * 2] != '_':
id = random.randint(0, len(answer) - 1)
nbRevealed += 1
finalString = finalString[:id * 2] + answer[id] + finalString[id * 2 + 1:]
if nbRevealed == len(answer):
return finalString
return finalString
def format_message(current_riddle):
nbClues = current_riddle['nbClues']
answer = current_riddle['answer']
formatted_riddle = "> " + current_riddle['riddle'].replace("\n", "\n> ")
formatted_riddle = formatted_riddle.replace("\r", "")
clue = ""
if nbClues > -1:
if nbClues >= len(answer):
clue = clue + "\nNon trouvée, la solution était : `{0}`".format(answer)
else:
clue = clue + "\nIndice : `{0}`".format(clue_string(answer, nbClues))
if 'solver' in current_riddle:
clue = clue + "\n{0} a trouvé la solution, qui était : `{1}`".format(current_riddle['solver'].mention, answer)
if clue:
return "Énigme {0}:\n{1}\n{2}".format(current_riddle['index'] + 1, formatted_riddle, clue)
else:
return "Énigme {0}:\n{1}".format(current_riddle['index'] + 1, formatted_riddle)
def get_last_word(ch:str)->str:
truncated = ch
while True:
if len(truncated) < 2 or truncated[-1].isnumeric():
return ''
if truncated[-1].isalpha() and truncated[-2].isalpha():
break
truncated = truncated[:-1]
return truncated
def poil_auquel(ch:str)->str:
for key in client.rhyme_keys:
if ch.endswith(tuple(client.rhyme_keys[key])):
return random.choice(client.rhyme_strings[key])
return ''
async def channel_name(channel):
if isinstance(channel, discord.DMChannel):
dm_channel = await client.fetch_channel(channel.id)
return '[DM={0}]'.format(dm_channel.recipient.name)
else:
return '[Server={0}] => [Channel={1}]'.format(channel.guild.name, channel.name)
@client.event
async def on_ready():
load_riddles()
load_rhymes()
print('Logged in as {0.user}'.format(client))
@client.event
async def on_message(message):
# don't answer to self
if message.author == client.user and message.channel in client.ongoing_riddles:
current_riddle = client.ongoing_riddles[message.channel]
if not ('message' in current_riddle):
current_riddle['message'] = message
return
if isinstance(message.channel, discord.DMChannel) or isinstance(message.channel, discord.TextChannel) or isinstance(message.channel, discord.Thread):
message_content = message.content.lower()
# command fouras
fouras_match = re.match(r"^fouras\s+(\d+)$", message_content)
if fouras_match:
index = int(fouras_match.group(1)) - 1
if index >= 0 and index < len(client.riddles):
if random.random() <= 0.03:
await message.channel.send("Non")
else:
await message.channel.send(new_riddle(message.channel, index))
else:
await message.channel.send("Numéro d'énigme invalide, merci de saisir un numéro entre 1 et {0}".format(len(client.riddles)))
return
if message_content == 'fouras':
if random.random() <= 0.03:
await message.channel.send("Non")
elif len(client.riddles) > 0:
index = random.randint(0, len(client.riddles) - 1)
await message.channel.send(new_riddle(message.channel, index))
return
# command reload
if message_content == 'reload_riddles':
load_riddles()
await message.channel.send('Loaded {0} riddles'.format(len(client.riddles)))
return
if message_content == 'about fouras':
author_user = await client.fetch_user(151626081458192384)
await message.channel.send(f"Ce bot a été développé par {author_user.mention}\nCode Source : https://gitlab.epicsparrow.com/Anselme/perefouras\nAjouter ce bot à votre serveur : https://discord.com/api/oauth2/authorize?client_id=1110208055171367014&permissions=274877975552&scope=bot")
return
if message_content == 'debug fouras':
dump = {}
cooldowns = {}
for key, value in client.cooldown.items():
cooldowns[channel_name(key)] = value - time.time()
dump['poil_au_cooldown'] = client.cooldown
for key, value in client.ongoing_riddles.items():
dump_channel = value
dump_channel.pop("message", None)
dump_channel['answer'] = '||{0}||'.format(dump_channel['answer'])
dump[channel_name(key)] = dump_channel
await message.author.send(json.dumps(dump, ensure_ascii=False, indent=4))
return
# if current channel has ongoing riddle
if message.channel in client.ongoing_riddles:
current_riddle = client.ongoing_riddles[message.channel]
if 'message' in current_riddle:
answer = current_riddle["answer"]
if answer.lower() in message_content:
current_riddle["solver"] = message.author
await message.channel.send(f"Bravo {message.author.mention} ! La réponse était bien `{answer}`.")
await current_riddle['message'].edit(content=format_message(current_riddle))
finish_riddle(message.channel)
return
if message_content == 'repete' or message_content == 'répète' or message_content == 'repeat':
current_riddle.pop('message')
await message.channel.send(format_message(current_riddle))
return
# Commande /clue : révèle une lettre au hasard de la réponse attendue
if message_content == 'indice' or message_content == 'aide' or message_content == 'connard de fouras':
nbClues = current_riddle["nbClues"] + 1
current_riddle["nbClues"] = nbClues
if nbClues >= len(answer):
reply = "Perdu ! La réponse était : `{0}`".format(answer)
await message.channel.send(reply)
# else:
# reply = "Indice : `{0}`".format(clue_string(answer, nbClues))
await current_riddle['message'].edit(content=format_message(current_riddle))
if nbClues >= len(answer):
finish_riddle(message.channel)
return
last_word = get_last_word(message_content)
if last_word:
poil = poil_auquel(last_word)
cooldown = 0
if message.channel in client.cooldown:
cooldown = client.cooldown[message.channel]
if poil and time.time() - cooldown > 0:
wait_time = random.randint(0, 900)
if bool(random.getrandbits(1)):
wait_time = random.randint(900, 10800)
client.cooldown[message.channel] = time.time() + wait_time
await message.channel.send(poil)
return
# Initialise le client
client.run(token)