Skip to content

Commit 83fed45

Browse files
Release v1 (#6)
* Initial commit * Initial commit * Basic implementation for introduction and started the migration for compatibility * Added missing config to base-configs * Completed the introduction feature * Removed unused import * push to transfer modifications * Updated gitignore and requirements * Added setup command * Optimized imports * Fixed circular import * Added memberCog * Fixed an import reference Co-authored-by: DeveloperAnonymous <matthew_quebec@outlook.fr> Co-authored-by: DeveloperAnonymous <40847862+DeveloperAnonymous@users.noreply.github.com>
1 parent bb10abf commit 83fed45

26 files changed

Lines changed: 965 additions & 63 deletions

.gitignore

Lines changed: 5 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,5 @@
1-
# Logs
2-
logs
3-
*.log
4-
npm-debug.log*
5-
yarn-debug.log*
6-
yarn-error.log*
7-
8-
# Runtime data
9-
pids
10-
*.pid
11-
*.seed
12-
*.pid.lock
13-
14-
# Directory for instrumented libs generated by jscoverage/JSCover
15-
lib-cov
16-
17-
# Coverage directory used by tools like istanbul
18-
coverage
19-
20-
# nyc test coverage
21-
.nyc_output
22-
23-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24-
.grunt
25-
26-
# Bower dependency directory (https://bower.io/)
27-
bower_components
28-
29-
# node-waf configuration
30-
.lock-wscript
31-
32-
# Compiled binary addons (https://nodejs.org/api/addons.html)
33-
build/Release
34-
35-
# Dependency directories
36-
node_modules/
37-
jspm_packages/
38-
39-
# TypeScript v1 declaration files
40-
typings/
41-
42-
# Optional npm cache directory
43-
.npm
44-
45-
# Optional eslint cache
46-
.eslintcache
47-
48-
# Optional REPL history
49-
.node_repl_history
50-
51-
# Output of 'npm pack'
52-
*.tgz
53-
54-
# Yarn Integrity file
55-
.yarn-integrity
56-
57-
# dotenv environment variables file
58-
.env
59-
60-
# next.js build output
61-
.next
1+
.vscode
2+
__pycache__
3+
configs.py
4+
.DS_STORE
5+
.idea

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019 ADEPT Informatique
3+
Copyright (c) 2021 ADEPT Informatique
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,21 @@
1-
# adeptbot
1+
# ADEPT Informatique Bot
2+
3+
A discord bot for managing your server
4+
5+
## Getting started
6+
7+
### Requirements
8+
9+
- Python 3.8+
10+
- Dependencies (`pip install -r requirements.txt`)
11+
- [ADEPT-API](https://github.com/ADEPT-Informatique/ADEPT-API)
12+
13+
### Setting up the bot
14+
15+
Rename `base-configs.py` to `configs.py`
16+
17+
Make sure to include the discord token & modify the channels and roles IDs if neccessary
18+
19+
### Run the bot
20+
21+
> `python3 run.py`

base-configs.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Discord Configs
2+
TOKEN = ""
3+
PREFIX = "!"
4+
5+
# Server Configs
6+
ADEPT = 362987473154080778
7+
LOGS_CHANNEL = f"547896873776578563/{ADEPT}"
8+
WELCOME_CHANNEL = f"470775922069471252/{ADEPT}"
9+
10+
# Roles
11+
PROG_ROLE = 363042185295167490
12+
NETWORK_ROLE = 363042228035125266
13+
DECBAC_ROLE = 775759126578855957
14+
TEACHER_ROLE = 775751726480883783
15+
16+
TRUST_ROLE = 434423894238298112
17+
ADMIN_ROLE = 363015309562478593
18+
19+
MUTED_ROLE = 363031112664219648
20+
MORON_ROLE = 559825539993698316
21+
22+
WELCOME_MESSAGE = "Bonjour et bienvenue à l'ADEPT! Pour commencez, nous allez devoir vous posez quelques questions afin de mieux vous connaitre.\n\n{content}"
23+
WELCOME_SERVER ="""
24+
Bienvenue {name} sur le serveur de l'ADEPT Informatique,
25+
26+
veuillez suivre les étapes en message privé pour avoir accès au serveur!
27+
28+
Assurez-vous également de regardez les réglements dans <#471133862442041355> après avoir complété les étapes
29+
"""

bot/botcommands/administration.py

Whitespace-only changes.

bot/botcommands/member.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from bot import util
2+
from discord.ext import commands
3+
4+
from run import AdeptClient
5+
6+
7+
class MemberCog(commands.Cog):
8+
def __init__(self, client: AdeptClient) -> None:
9+
super().__init__()
10+
self.client = client
11+
12+
@commands.command()
13+
async def setup(self, ctx):
14+
result = await self.client.walk_through_welcome(ctx.author)
15+
await util.process_welcome_result(ctx.author, result)

bot/botcommands/moderation.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import discord
2+
import typing
3+
from discord.ext import commands
4+
from discord.ext.commands.context import Context
5+
6+
import configs
7+
from .. import util, tasks
8+
from ..http.services import MuteService, BanService
9+
from ..strikes import Strike
10+
11+
NO_REASON = "No reason specified"
12+
13+
14+
def parse_secs(value:int):
15+
return (value, "seconds" if value > 1 else "second", value)
16+
17+
18+
def parse_mins(value:int):
19+
total_time = value * 60
20+
return (value, "minutes" if value > 1 else "minute", total_time)
21+
22+
23+
def parse_hours(value:int):
24+
total_time = value * 60 * 60
25+
return (value, "hours" if value > 1 else "hour", total_time)
26+
27+
28+
def parse_days(value:int):
29+
total_time = value * 60 * 60 * 24
30+
return (value, "days" if value > 1 else "day", total_time)
31+
32+
33+
def parse_week(value:int):
34+
total_time = value * 60 * 60 * 24 * 7
35+
return (value, "weeks" if value > 1 else "week", total_time)
36+
37+
38+
class CustomTime(commands.Converter):
39+
async def convert(self, _, value):
40+
time_parse = value[-1].lower()
41+
try:
42+
int_value = int(value)
43+
except TypeError:
44+
int_value = int(value[:-1])
45+
return {
46+
's': parse_secs(int_value),
47+
'm': parse_mins(int_value),
48+
'h': parse_hours(int_value),
49+
'd': parse_days(int_value),
50+
'w': parse_week(int_value)
51+
}.get(time_parse, parse_secs(int_value))
52+
53+
54+
class Moderation(commands.Cog):
55+
def __init__(self, bot: commands.Bot) -> None:
56+
self.mute_service = MuteService(bot.loop)
57+
self.ban_service = BanService(bot.loop)
58+
59+
async def cog_check(self, ctx: Context) -> bool:
60+
return any([role in (configs.ADMIN_ROLE, configs.TRUST_ROLE) for role in ctx.author.roles])
61+
62+
@commands.command()
63+
@commands.has_any_role(configs.ADMIN_ROLE, configs.TRUST_ROLE)
64+
async def warn(self, ctx: Context, member: discord.Member, *, reason:str=NO_REASON):
65+
"""
66+
USAGE EXAMPLES:
67+
!warn @DeveloperAnonymous Is a noob
68+
"""
69+
70+
warn_embed = discord.Embed(title=f"New case | Warning | {member}", color=15066368)
71+
warn_embed.add_field(name="User", value=member.mention)
72+
warn_embed.add_field(name="Moderator", value=ctx.author.mention)
73+
warn_embed.add_field(name="Reason", value=reason, inline=False)
74+
75+
try:
76+
await member.send("You have been warned in %s: %s" % (ctx.guild.name, reason))
77+
except(discord.errors.HTTPException, discord.errors.Forbidden):
78+
util.logger.warn("Failed to notify warn")
79+
80+
await util.strike(member.id, str(Strike.WARN), reason)
81+
await util.say(configs.LOGS, embed=warn_embed)
82+
await util.react_to(ctx.message, u"\u2705")
83+
84+
@commands.command()
85+
@commands.has_any_role(configs.ADMIN_ROLE, configs.TRUST_ROLE)
86+
async def mute(self, ctx: Context, member: discord.Member, length:typing.Optional[CustomTime]=-1, *, reason:str=NO_REASON):
87+
"""
88+
USAGE EXAMPLES:
89+
!mute @DeveloperAnonymous Is a noob
90+
!mute @DeveloperAnonymous 15 Is a noob
91+
!mute @DeveloperAnonymous 15s Is a noob
92+
!mute @DeveloperAnonymous 15m Is a noob
93+
!mute @DeveloperAnonymous 15h Is a noob
94+
!mute @DeveloperAnonymous 15d Is a noob
95+
!mute @DeveloperAnonymous 15w Is a noob
96+
97+
NOTE:
98+
s = second(s)
99+
m = minute(s)
100+
h = hour(s)
101+
d = day(s)
102+
w = week(s)
103+
"""
104+
if await util.has_role(member, ctx.guild.get_role(configs.MUTED_ROLE)):
105+
return await util.exception(ctx.channel, "This user is already muted!")
106+
107+
mute_embed = discord.Embed(title=f"New case | Mute | {member}", color=15066368)
108+
mute_embed.add_field(name="User", value=member.mention)
109+
mute_embed.add_field(name="Moderator", value=ctx.author.mention)
110+
if type(length) == tuple:
111+
mute_embed.add_field(name="Length", value=f"{length[0]} {length[1]}")
112+
mute_embed.add_field(name="Reason", value=reason, inline=False)
113+
114+
try:
115+
await member.send("You have been muted in %s: %s" % (ctx.guild.name, reason))
116+
except (discord.errors.HTTPException, discord.errors.Forbidden):
117+
util.logger.warn("Failed to notify mute")
118+
119+
seconds = length[2] if type(length) == tuple else None
120+
await tasks.create_mute_task(member, seconds)
121+
await util.strike(member.id, Strike.MUTE, reason)
122+
await self.mute_service.mute(member, reason, seconds)
123+
await util.say(configs.LOGS, embed=mute_embed)
124+
await util.react_to(ctx.message, u"\u2705")
125+
126+
@commands.command()
127+
@commands.has_guild_permissions(manage_guild=True)
128+
async def unmute(self, ctx: Context, member: discord.Member, *, reason:str=NO_REASON):
129+
"""
130+
USAGE EXAMPLES:
131+
!unmute @DeveloperAnonymous
132+
!unmute @DeveloperAnonymous Is not a noob anymore
133+
"""
134+
if not await util.has_role(member, ctx.guild.get_role(configs.MUTED_ROLE)):
135+
return await util.exception(ctx.channel, "This user is not muted!")
136+
137+
mute_embed = discord.Embed(title=f"New case | Unmute | {member}", color=15066368)
138+
mute_embed.add_field(name="User", value=member.mention)
139+
mute_embed.add_field(name="Reason", value=reason, inline=False)
140+
141+
await util.unmute(member)
142+
await tasks.delete_task(member, Strike.MUTE, reason)
143+
await util.strike(member.id, Strike.UNMUTE, reason)
144+
await util.say(configs.LOGS, embed=mute_embed)
145+
await util.react_to(ctx.message, u"\u2705")
146+
147+
@commands.command()
148+
@commands.has_guild_permissions(kick_members=True)
149+
async def kick(self, ctx: Context, member: discord.Member, *, reason:str=NO_REASON):
150+
"""
151+
USAGE EXAMPLES:
152+
!kick @DeveloperAnonymous
153+
!kick @DeveloperAnonymous Is a noob
154+
"""
155+
kick_embed = discord.Embed(title=f"New case | Kick | {member}", color=16758079)
156+
kick_embed.add_field(name="User", value=member.mention)
157+
kick_embed.add_field(name="Reason", value=reason, inline=False)
158+
159+
try:
160+
await member.send("You have been kicked in %s: %s" % (ctx.guild.name, reason))
161+
except (discord.errors.HTTPException, discord.errors.Forbidden):
162+
util.logger.warn("Failed to notify kick")
163+
164+
await member.kick(reason=reason)
165+
await util.strike(member.id, Strike.KICK, reason)
166+
await util.say(configs.LOGS, embed=kick_embed)
167+
await util.react_to(ctx.message, u"\u2705")
168+
169+
@commands.command()
170+
@commands.has_guild_permissions(ban_members=True)
171+
async def ban(self, ctx: Context, user: discord.User, *, reason:str=NO_REASON):
172+
"""
173+
USAGE EXAMPLES:
174+
!ban @DeveloperAnonymous
175+
!ban @DeveloperAnonymous Is a noob
176+
"""
177+
guild:discord.Guild = ctx.guild
178+
179+
if user in [entry.user for entry in await guild.bans()]:
180+
return await util.exception(ctx.channel, "This user is already banned!")
181+
182+
ban_embed = discord.Embed(title=f"New case | Ban | {user}", color=993326)
183+
ban_embed.add_field(name="User", value=user.mention)
184+
ban_embed.add_field(name="Reason", value=reason, inline=False)
185+
186+
try:
187+
await user.send("You have been banned in %s: %s" % (ctx.guild.name, reason))
188+
except (discord.errors.HTTPException, discord.errors.Forbidden):
189+
util.logger.warn("Failed to notify ban")
190+
191+
await ctx.guild.ban(user, reason=reason)
192+
await util.strike(user.id, Strike.BAN, reason)
193+
await util.say(configs.LOGS, embed=ban_embed)
194+
await util.react_to(ctx.message, u"\u2705")
195+
196+
@commands.command()
197+
@commands.has_guild_permissions(ban_members=True)
198+
async def unban(self, ctx: Context, user: discord.User, *, reason:str=NO_REASON):
199+
"""
200+
USAGE EXAMPLES:
201+
!unban @DeveloperAnonymous
202+
!unban @DeveloperAnonymous Is not a noob anymore
203+
"""
204+
guild:discord.Guild = ctx.guild
205+
206+
if user not in [entry.user for entry in await guild.bans()]:
207+
return await util.exception(ctx.channel, "This user is not banned!")
208+
209+
unban_embed = discord.Embed(title=f"New case | Unban | {user}", color=993326)
210+
unban_embed.add_field(name="User", value=user.mention)
211+
unban_embed.add_field(name="Reason", value=reason, inline=False)
212+
213+
await guild.unban(user, reason=reason)
214+
await util.strike(user.id, Strike.UNBAN, reason)
215+
await util.say(configs.LOGS, embed=unban_embed)
216+
await util.react_to(ctx.message, u"\u2705")
217+
218+
async def cog_command_error(self, ctx: Context, error):
219+
await util.say(ctx.channel, error)

bot/http/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .data import *
2+
from .ban import *
3+
from .mute import *

bot/http/models/ban.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from . import BaseRequest
2+
3+
class BanRequest(BaseRequest):
4+
def __init__(self, reason: str, duration: int) -> None:
5+
self.reason = reason
6+
self.duration = duration
7+
8+
class BanResponse():
9+
pass

bot/http/models/data.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class BaseRequest:
2+
pass

0 commit comments

Comments
 (0)