Skip to content

Commit 060751a

Browse files
danielsp45nelsonmestevaoruilopesm
authored
Feature - daily updates (#20)
* Add feature private messages * Fix minor error * Updates * Updates v2 * Add feature daily updates * Bug fix * Bug fix * Refactor attribute messages colors * Refactor propposed changes * Add loggins * Documentation snippet * Update bot/cogs/daily_report.py Co-authored-by: Nelson Estevão <[email protected]> * Add documentation * Add SQLAlchemy database * Fix bugs * Fix .env variables * Add log files to gitignore * Fix bug in logs.py * Solve ci problems * Add `GUILD_ID` and `GONGO_CHANNEL_ID` to .env.sample file Co-authored-by: Nelson Estevão <[email protected]> Co-authored-by: Rui Lopes <[email protected]>
1 parent 85a46b2 commit 060751a

12 files changed

+275
-65
lines changed

.env.sample

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
BOT_TOKEN='<Your Token Here>'
2+
GUILD_ID='<Your Guild ID Here>'
3+
GONGO_CHANNEL_ID='<The Channel For The Daily Reports Here>'

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,9 @@ dmypy.json
136136

137137
# Cython debug symbols
138138
cython_debug/
139+
140+
# database
141+
*.db
142+
143+
# log files
144+
*.log

bot/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
A bot for the CoderDojo Braga Discord server. Its goal is to help the Discord
33
admins promote members in a more festive and easy way.
44
"""
5+
56
VERSION = "0.0.1"

bot/client.py

+24-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import logging
12
import os
23

34
import discord
45
from discord.ext import commands
56

67
from bot import VERSION
8+
from bot.cogs.daily_report import DailyReport
79

810
client = commands.Bot(
911
command_prefix=commands.when_mentioned_or("$"),
@@ -12,26 +14,41 @@
1214
case_insensitive=True,
1315
)
1416

17+
# Logging setup
18+
logger = logging.getLogger("discord")
19+
logger.setLevel(logging.INFO)
20+
handler = logging.FileHandler(filename="bot/discord.log", encoding="utf-8", mode="w")
21+
handler.setFormatter(
22+
logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s")
23+
)
24+
logger.addHandler(handler)
25+
1526

1627
@client.event
1728
async def on_ready():
18-
print(f"We have logged in as {client.user}")
29+
"""Client event that run when the program is ready."""
30+
31+
logger.info("The bot was logged in")
32+
DailyReport(client).report.start()
33+
logger.info("The task has been loaded")
1934

2035

2136
@client.command()
22-
async def load(ctx, extension):
23-
client.load_extension(f"bot.cogs.{extension}")
37+
async def load(ctx: discord.ext.commands.Context, extension: str):
38+
"""Command to load an extension."""
2439

40+
client.load_extension(f"bot.cogs.{extension}")
2541
await ctx.send(f"O cog {extension} foi ativado.")
2642

2743

2844
@client.command()
29-
async def unload(ctx, extension):
45+
async def unload(ctx: discord.ext.commands.Context, extension: str):
46+
"""Command to unload an extension."""
47+
3048
client.unload_extension(f"bot.cogs.{extension}")
3149

3250
await ctx.send(f"O cog {extension} foi desativado.")
3351

3452

35-
for filename in os.listdir("bot/cogs"):
36-
if filename.endswith(".py"):
37-
client.load_extension(f"bot.cogs.{filename[:-3]}")
53+
client.load_extension("bot.cogs.belts")
54+
client.load_extension("bot.cogs.daily_report")

bot/cogs/__init__.py

Whitespace-only changes.

bot/cogs/belts.json

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,70 @@
11
{
22
"Branco": {
3-
"objectives": [
3+
"goals": [
44
"- Finalizar um projeto sozinho",
55
"- Apresentar o teu projeto a outros ninjas",
66
"- Saber o nome de 5 ninjas e 2 mentores",
77
"- Completar o Lightbot"
88
],
9-
"color": "FFFFFF"
9+
"color": "e9e9e9"
1010
},
1111
"Amarelo": {
12-
"objectives": [
12+
"goals": [
1313
"- Estar presente em 4 sessões",
1414
"- Fazer um projeto e apresentar à mesa"
1515
],
16-
"color": "F7E129"
16+
"color": "fcd767"
1717
},
1818
"Azul": {
19-
"objectives": [
19+
"goals": [
2020
"- Ajudar como mentor numa sessão",
2121
"- Saber o nome de 7 ninjas e 4 mentores",
2222
"- Conseguir completar o código que os mentores têm preparado para ti",
2323
"- Fazer um site"
2424
],
25-
"color": "296DF7"
25+
"color": "7f9acd"
2626
},
2727
"Verde": {
28-
"objectives": [
28+
"goals": [
2929
"- Apresentar um projeto para o Dojo inteiro",
3030
"- Chegar a 5 Kyu em CodeWars",
3131
"- Missão secreta n.º2",
3232
"- Projeto em raspberry c/ apresentação"
3333
],
34-
"color": "0ACD1B"
34+
"color": "09a777"
3535
},
3636
"Laranja": {
37-
"objectives": [
37+
"goals": [
3838
"- Projeto de Fuler",
3939
"- Com a ajuda de um mentor, montar um computador",
4040
"- Introduzir conceitos de bash",
4141
"- Missão secreta n.º4 (difícil)"
4242
],
43-
"color": "F48005"
43+
"color": "f79520"
4444
},
4545
"Vermelho": {
46-
"objectives": [
46+
"goals": [
4747
"- Ser mentor 1 sessão",
4848
"- Demonstração de conhecimento do paradigma POO",
4949
"- Ter um projeto que use API externa"
5050
],
51-
"color": "F60909"
51+
"color": "ec2027"
5252
},
5353
"Roxo": {
54-
"objectives": [
54+
"goals": [
5555
"- Conceitos básicos g7",
5656
"- Criar uma missão secreta",
5757
"- Ser mentor 2 sessões",
5858
"- Ter conta e saber usar o Slack",
5959
"- Criar um Bot com uma API externa",
6060
"- Última missão secreta"
6161
],
62-
"color": "A709F6"
62+
"color": "6f5977"
6363
},
6464
"Preto": {
65-
"objectives": [
65+
"goals": [
6666
"Não tens, és um guru da programação!"
6767
],
68-
"color": "000000"
68+
"color": "383b3f"
6969
}
7070
}

bot/cogs/belts.py

+80-42
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,76 @@
11
import json
2+
import time
3+
from datetime import date
24
from enum import Enum, unique
35

46
import discord
57
from discord.ext import commands
8+
from sqlalchemy import create_engine
9+
from sqlalchemy.orm import sessionmaker
10+
11+
from bot.cogs.constants import Belts, get_role_from_name, translator_to_emoji
12+
from bot.cogs.logs import AttributionLogs, Base, log_attribution
13+
14+
# sqlalchemy setup
15+
engine = create_engine("sqlite:///daily_logs.db")
16+
17+
Base.metadata.bind = engine
18+
19+
DBSession = sessionmaker(bind=engine)
20+
21+
session = DBSession()
622

723

824
class FileHandler:
25+
"""
26+
This is a class to handle a json file.
27+
28+
Attributes:
29+
file (string): The path to the json file being handled.
30+
"""
31+
932
file = "bot/cogs/belts.json"
1033

11-
def __init__(self, belt):
34+
def __init__(self: str, belt: str):
35+
"""
36+
The constructor for the FileHandler class.
37+
38+
Parameters:
39+
color (int): Color code to be displayed in discord embed.
40+
"""
1241
self.belt = belt
1342
self.msg = self.get_info()[0]
1443
self.color = self.get_info()[1]
1544

16-
def get_info(self):
45+
def get_info(self) -> tuple:
46+
"""
47+
The function to get the info from the belts.json file.
48+
49+
Returns:
50+
msg (string): Variable that contains the message of the respective belt.
51+
color (int): Color code to be displayed in discord embed.
52+
"""
1753
with open(self.file) as json_file:
1854
data = json.load(json_file)
1955
msg = f"Subiste para {self.belt} :clap:\n\nPróximos objetivos:"
2056
color = int(data[self.belt]["color"], 16)
21-
for param in data[self.belt]["objectives"]:
57+
for param in data[self.belt]["goals"]:
2258
msg += "\n" + param
2359

2460
return (msg, color)
2561

2662

27-
translator_to_emoji = {
28-
"Branco": ":white_circle:",
29-
"Amarelo": ":yellow_circle:",
30-
"Azul": ":blue_circle:",
31-
"Verde": ":green_circle:",
32-
"Laranja": ":orange_circle:",
33-
"Vermelho": ":red_circle:",
34-
"Roxo": ":purple_circle:",
35-
"Preto": ":black_circle:",
36-
}
37-
38-
39-
@unique
40-
class Belts(Enum):
41-
Branco = 1
42-
Amarelo = 2
43-
Azul = 3
44-
Verde = 4
45-
Laranja = 5
46-
Vermelho = 6
47-
Roxo = 7
48-
Preto = 8
49-
50-
5163
class Ninja:
52-
def __init__(self, guild, member):
64+
"""This is a class to get information about a specific ninja."""
65+
66+
def __init__(self, guild: discord.Guild, member: discord.Member):
5367
self.guild = guild
5468
self.member = member
5569
self.roles = [role for role in member.roles]
5670

57-
def current_belt(self):
71+
def current_belt(self) -> Belts:
72+
"""This function returns the current belt of the ninja."""
73+
5874
highest_belt = None
5975
for role in self.roles:
6076
for belt in Belts:
@@ -63,34 +79,34 @@ def current_belt(self):
6379

6480
return highest_belt
6581

66-
def next_belt(self):
67-
# Check if the maximum range has been exceeded
82+
def next_belt(self) -> Belts:
83+
"""This function returns the next belt of the ninja."""
84+
6885
value = self.current_belt().value + 1 if self.current_belt().value < 8 else 8
6986

7087
return Belts(value)
7188

72-
@staticmethod
73-
def get_role_from_name(guild, belt):
74-
for role in guild.roles:
75-
if role.name == belt:
76-
return role
77-
7889

7990
class BeltsAttributions(commands.Cog):
80-
def __init__(self, client):
91+
"""This is a class to handle the attribution of belts."""
92+
93+
def __init__(self, client: commands.Bot):
8194
self.client = client
8295

8396
@commands.command(name="promove")
8497
@commands.has_any_role("🛡️ Admin", "🏆 Champion", "🧑‍🏫 Mentores")
85-
async def promove(self, ctx, user, belt):
98+
async def promove(
99+
self, ctx: discord.ext.commands.Context, user: str, belt: str
100+
) -> None:
101+
"""This function promotes a user to the next belt."""
86102

87103
mentions = ctx.message.raw_mentions
88104
guild = ctx.guild
89105
member = guild.get_member(mentions[0])
90106
ninja = Ninja(guild, member)
91107

92108
if belt == "Branco" and ninja.current_belt() == None:
93-
role = Ninja.get_role_from_name(guild, belt)
109+
role = get_role_from_name(guild, belt)
94110

95111
await member.add_roles(guild.get_role(role.id), reason=None, atomic=True)
96112

@@ -109,11 +125,22 @@ async def promove(self, ctx, user, belt):
109125

110126
await user.send(embed=embed)
111127

128+
# Adding the log to the database
129+
new_log = AttributionLogs(
130+
ninja_id=str(member),
131+
mentor_id=str(ctx.author),
132+
belt_attributed=belt,
133+
timestamp=int(time.time()),
134+
)
135+
136+
session.add(new_log)
137+
session.commit()
138+
112139
elif belt == ninja.current_belt().name:
113140
await ctx.reply(f"Esse já é o cinturão do ninja {user}!")
114141

115142
elif belt == ninja.next_belt().name:
116-
role = Ninja.get_role_from_name(guild, belt)
143+
role = get_role_from_name(guild, belt)
117144
await member.add_roles(guild.get_role(role.id), reason=None, atomic=True)
118145

119146
# Public message
@@ -131,9 +158,20 @@ async def promove(self, ctx, user, belt):
131158

132159
await user.send(embed=embed)
133160

161+
# Adding the log to the database
162+
new_log = AttributionLogs(
163+
ninja_id=str(member),
164+
mentor_id=str(ctx.author),
165+
belt_attributed=belt,
166+
timestamp=int(time.time()),
167+
)
168+
169+
session.add(new_log)
170+
session.commit()
171+
134172
elif belt != ninja.next_belt().name:
135173
await ctx.send(f"{user} esse cinturão não é valido de se ser atribuido.")
136174

137175

138-
def setup(client):
176+
def setup(client: commands.Bot) -> None:
139177
client.add_cog(BeltsAttributions(client))

0 commit comments

Comments
 (0)