Skip to content

feat: add submission logic #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
*
!.gitignore
!src/
!README.md
!requirements.txt
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
discord.py
python-dotenv
aiofiles
1 change: 1 addition & 0 deletions src/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DISCORD_TOKEN= # Your Discord bot token here
135 changes: 135 additions & 0 deletions src/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import re
import discord
from utils import update_message, create_initial_message, extract_sections

async def on_ready(client):
print(f'{client.user} has connected to Discord!')

async def on_member_join(client, member, channel_ids):
acolhimento_channel = client.get_channel(channel_ids['acolhimento'])
if acolhimento_channel:
await acolhimento_channel.send(f'Bem vindo ao Discord do CoderDojo Braga, {member.mention}!\nPara começares, envia uma mensagem para este canal e diz-nos olá! Diz-nos também o teu primeiro e último nome, se és ninja, guardião ou voluntário, para sabermos quem és e te darmos acesso ao resto do Discord!')

async def handle_reaction(client, payload, add, react_roles, channel_ids, message_ids):
emoji = payload.emoji.name
if emoji in react_roles:
await handle_reaction_roles(client, payload, add, react_roles)

if add and payload.channel_id == channel_ids['submissions']:
if emoji == "✅":
await update_participants_message(client, payload, channel_ids, message_ids)
elif emoji == "❌":
await create_support_thread(client, payload, channel_ids)

async def create_support_thread(client, payload, channel_ids):
support_channel = client.get_channel(channel_ids['support'])
if support_channel:
try:
message = await client.get_channel(payload.channel_id).fetch_message(payload.message_id)
if message:
# Extract the challenge information and mention
correct_pattern = re.match(r'^(Scratch|Python)\s-\sDesafio\s(\d+)\s(<@\d{17,19}>)', message.content)
if correct_pattern:
challenge_info = f"{correct_pattern.group(1)} - Desafio {correct_pattern.group(2)} {correct_pattern.group(3)}"
support_message = await support_channel.send(content=challenge_info)
thread = await support_message.create_thread(name=f"Ajuda para {message.author.display_name}")
await thread.send(f"Olá, parece que precisas de ajuda com o teu desafio. Por favor, descreve a tua dúvida aqui.")
print(f"Support thread created for {message.author.display_name}.")
else:
print(f"Message content does not match the expected pattern.")
else:
print(f"Message with ID {payload.message_id} not found.")
except discord.errors.NotFound:
print(f"Message with ID {payload.message_id} not found.")
except discord.errors.HTTPException as e:
print(f"Failed to create thread: {e}")

async def handle_reaction_roles(client, payload, add, react_roles):
guild = client.get_guild(payload.guild_id)
emoji = payload.emoji.name

if emoji in react_roles:
role_id = int(react_roles[emoji])
role = guild.get_role(role_id)
member = await guild.fetch_member(payload.user_id)
if role and member:
if add:
await member.add_roles(role)
print(f'Role {role.name} added to {member.display_name}.')
else:
await member.remove_roles(role)
print(f'Role {role.name} removed from {member.display_name}.')

async def create_or_fetch_participants_message(client, participants_channel, message_ids):
if message_ids['participants'] is None:
initial_message = create_initial_message()
new_message = await participants_channel.send(initial_message)
message_ids['participants'] = new_message.id
print(f'Initial participants message created with ID: {new_message.id}')
return new_message

try:
return await participants_channel.fetch_message(message_ids['participants'])
except discord.errors.NotFound:
initial_message = create_initial_message()
new_message = await participants_channel.send(initial_message)
message_ids['participants'] = new_message.id
print(f'Recreated participants message with ID: {new_message.id}')
return new_message

async def update_participants_message(client, payload, channel_ids, message_ids):
participants_channel = client.get_channel(channel_ids['participants'])
participants_message = await create_or_fetch_participants_message(client, participants_channel, message_ids)

channel = client.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
correct_pattern = re.match(r'^(Scratch|Python)\s-\sDesafio\s(\d+)\s(<@\d{17,19}>)', message.content)
if correct_pattern:
language = correct_pattern.group(1)
level = int(correct_pattern.group(2))
participant = correct_pattern.group(3)

scratch_level_1, scratch_level_2, scratch_level_3, python_level_1, python_level_2 = extract_sections(participants_message.content)
if (language == "Scratch" and level in [1, 2, 3] and participant not in locals()[f'scratch_level_{level}']) or (language == "Python" and level in [1, 2] and participant not in locals()[f'python_level_{level}']):
updated_message = update_message(participants_message.content, participant, language, level)
await participants_message.edit(content=updated_message)
print(f'Participants message updated with new content: {updated_message}')

async def handle_dm(client, message, guild_id, channel_ids):
guild = client.get_guild(guild_id)
author = guild.get_member(message.author.id)

if author is None:
return

if discord.utils.get(author.roles, name='Ninjas'):
correct_pattern = re.match(r'^([sS]cratch|[Pp]ython)\s*-\s*Desafio\s(\d+)\s*\n(.*)', message.content)
if correct_pattern:
language = (correct_pattern.group(1))[0].upper() + (correct_pattern.group(1))[1:]
level = int(correct_pattern.group(2))
message_content = correct_pattern.group(3)
if (language == "Scratch" and level in [1, 2, 3]) or (language == "Python" and level in [1, 2]):
submissions_channel = client.get_channel(channel_ids['submissions'])
await submissions_channel.send(f"{language} - Desafio {level} <@{author.id}> {message_content}")
await message.author.send("A tua submissão foi recebida com sucesso e será validada em breve.")
else:
await message.author.send("O nível do desafio está fora dos níveis disponíveis. Por favor, verifica e tenta novamente.")
else:
await message.author.send("O formato da mensagem está incorreto. Por favor, segue o formato: ```Linguagem - Desafio Nível\nConteúdo```")

async def handle_public_message(client, message, guild_id, channel_ids):
guild = client.get_guild(guild_id)
if re.match(r'[Oo]l[aá].*', message.content) and message.channel.id == channel_ids['acolhimento']:
await message.add_reaction('👋')
await message.add_reaction('❤️')

if discord.utils.get(message.author.roles, name='Mentores'):
correct_pattern = re.match(r'\*[sS]ubir a <@&(\d+)>(\s*<@\d+>)+', message.content)
if correct_pattern:
cinturao = correct_pattern.group(1)
role = guild.get_role(int(cinturao))
user_ids = re.findall(r'<@(\d+)>', message.content)
for id in user_ids:
member = await guild.fetch_member(int(id))
if member and role:
await member.add_roles(role)
137 changes: 53 additions & 84 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# bot.py
import os

import discord
import re
from utils import update_message
from datetime import datetime
from dotenv import load_dotenv
from events import on_ready, on_member_join, handle_reaction, handle_dm, handle_public_message
from utils import schedule_challenges

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
Expand All @@ -21,14 +21,17 @@
channel_ids = {
'acolhimento' : 763200168743927868,
'submissions' : 1285597566188392529,
'introduction' : 1285598108524347413, # canal de introducao dos desafios
'introduction' : 1285598108524347413,
'languages' : 1285619669268566117,
'participants' : 1285221109406502962,
'scratch-challenges' : 1285220596942115002,
'python-challenges' : 1285220641028440074,
'support' : 1285223260572745761
}

message_ids = {
'languages' : 1286373895959740491,
'participants' : 1286373895078936640
'participants' : None
}

react_roles = {
Expand All @@ -38,93 +41,59 @@

class MyClient(discord.Client):
async def on_ready(self):
print(f'{client.user} has connected to Discord!')

async def on_member_join(self, member):
print("Member has joined")
acolhimento_channel = self.get_channel(channel_ids['acolhimento'])

if acolhimento_channel:
await acolhimento_channel.send(f'Bem vindo ao Discord do CoderDojo Braga, {member.mention}!\nPara começares, envia uma mensagem para este canal e diz-nos olá! Coloca também o teu nickname como o teu primeiro e último nome para sabermos quem és e te darmos acesso ao resto do Discord!')

async def handle_reaction_roles(self, payload, add):
guild = self.get_guild(payload.guild_id)
emoji = payload.emoji.name

if emoji in react_roles:
role_id = int(react_roles[emoji])
role = guild.get_role(role_id)
member = await guild.fetch_member(payload.user_id)
if role and member:
if add:
await member.add_roles(role)
print(f'Role {role.name} added to {member.display_name}.')
else:
await member.remove_roles(role)
print(f'Role {role.name} removed from {member.display_name}.')

async def on_raw_reaction_add(self, payload):
emoji = payload.emoji.name
if emoji in react_roles.keys():
await self.handle_reaction_roles(payload, add=True)

if payload.channel_id == channel_ids['submissions'] and emoji == "✅":
channel = self.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
await on_ready(self)
participants_channel = self.get_channel(channel_ids['participants'])
async for message in participants_channel.history(limit=1):
if message.author == self.user:
correct_pattern = re.match(r'^(Scratch|Python)\s(<@\d{17,19}>)', message.content)
if correct_pattern:
language = correct_pattern.group(1)
participant = correct_pattern.group(2)
message_ids['participants'] = message.id
print(f'Loaded participants message ID: {message.id}')
break

participants_channel = self.get_channel(channel_ids['participants'])
participants_message = await participants_channel.fetch_message(message_ids['participants'])
introduction_channel = self.get_channel(channel_ids['introduction'])
async for message in introduction_channel.history(limit=1):
if message.author == self.user:
break
else:
introduction_message = (
"👋 Bem vindos aos desafios do discord do CoderDojo Braga!\n\n"
"🙋‍♂️ Para participar, basta escolherem a(s) linguagen(s) dos desafios que pretendem fazer, no canal <#1285619669268566117>.\n\n"
"📣 Os novos desafios irão surgir nos canais <#1285220596942115002> e <#1285220641028440074>.\n\n"
"📮 Para submeter as respostas, enviem uma mensagem ao <@1285612642945339434>, o nosso bot dos desafios.\n"
"A única regra para ser aceite, é que a mensagem deve começar com a linha:\n"
"```Scratch - Desafio x```"
"para os desafios de Scratch.\n\n"
"Ou uma linha:\n"
"```Python - Desafio x```"
"para os desafios de Python.\n\n"
"O x representa o nível do desafio atual.\n\n"
"🏆 Os nomes dos participantes que tiverem concluído cada desafio vão aparecer no canal <#1285221109406502962>\n\n"
"🆘 Para pedir ajuda com alguma dúvida relativa aos desafios, podem escrever no canal <#1285223260572745761>, e chamar um dos nossos mentores ajudantes, mencionando <@&1285597424060207178>!\n\n"
"🥳 Esperamos que se divirtam muito com estes desafios complementares, e vemo-nos nas sessões!"
)
await introduction_channel.send(introduction_message)
print('Introduction message sent.')

schedule_time = datetime(2024, 11, 17, 11, 00)
now = datetime.now()
if now < schedule_time:
self.loop.create_task(schedule_challenges(self, channel_ids, schedule_time, message_ids))
else:
print("Scheduled time has already passed, skipping scheduling.")

updated_message = update_message(participants_message.content, participant, language)

await participants_message.edit(content=updated_message)
async def on_member_join(self, member):
await on_member_join(self, member, channel_ids)

async def on_raw_reaction_add(self, payload):
await handle_reaction(self, payload, add=True, react_roles=react_roles, channel_ids=channel_ids, message_ids=message_ids)

async def on_raw_reaction_remove(self, payload):
emoji = payload.emoji.name
if emoji in react_roles.keys():
await self.handle_reaction_roles(payload, add=False)

await handle_reaction(self, payload, add=False, react_roles=react_roles, channel_ids=channel_ids, message_ids=message_ids)

async def on_message(self, message):
guild = self.get_guild(guild_id)
if isinstance(message.channel, discord.DMChannel):
author = guild.get_member(message.author.id)

if author is None:
return

if discord.utils.get(author.roles, name='Ninjas'):
correct_pattern = re.match(r'^([sS]cratch|[Pp]ython)\s*-\s*Desafio\s\d+\s*\n(.*)', message.content)
if correct_pattern:
language = (correct_pattern.group(1))[0].upper() + (correct_pattern.group(1))[1:]
message_content = correct_pattern.group(2)
submissions_channel = self.get_channel(channel_ids['submissions'])
await submissions_channel.send(f"{language} <@{author.id}> {message_content}")
else:
return

await handle_dm(self, message, guild_id, channel_ids)
else:
if re.match(r'[Oo]l[aá].*', message.content) and message.channel.id == channel_ids['acolhimento']:
await(message.add_reaction('👋'))
await message.add_reaction('❤️')

if discord.utils.get(message.author.roles, name='Mentores'):
correct_pattern = re.match(r'\*[sS]ubir a <@&(\d+)>(\s*<@\d+>)+', message.content)
if correct_pattern:
cinturao = correct_pattern.group(1)
role = guild.get_role(int(cinturao))
user_ids = re.findall(r'<@(\d+)>', message.content)
for id in user_ids:
member = await guild.fetch_member(int(id))
if member and role:
await member.add_roles(role)


await handle_public_message(self, message, guild_id, channel_ids)

client = MyClient(intents=intents)
client.run(TOKEN)
client.run(TOKEN)
22 changes: 22 additions & 0 deletions src/python/1.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Desafio - Python Nível 1
### Ativo entre: 17/11/2024 e 29/11/2024

## 💡 Ideia:

Cria um sistema de estradas onde existe um semáforo para controlar o trânsito. Usa a tua criatividade!

## ✅ Regras:

1. Uma estrada com um semáforo, controlando o fluxo de carros.
2. O semáforo alterna entre **verde** e **vermelho**.
3. Quando o semáforo está **verde**, os carros passam, quando está **vermelho**, eles param.
4. O sistema exibe o estado atual do semáforo e os carros na estrada.

## 🧩 Dicas de Resolução

- Cria uma variável com o número total de carros à espera.
- Cria uma lista com as cores do semáforo.
- Importa a biblioteca time e cria uma lista com os tempo de espera de cada cor do semáforo.
- Cria uma função para atualizar para a próxima cor do semáforo.
- Cria uma função para movimentar os carros, ou seja, decrementar da variável número total de carros à espera.
- Cria um ciclo infinito que utiliza as funções criadas e o time.sleep() em conjunto com a lista dos tempos de espera para teres o teu sistema a funcionar!
Loading