mirror of
https://github.com/nexy7574/LCC-bot.git
synced 2024-09-19 18:16:34 +01:00
Update verification flow
This commit is contained in:
parent
58ad83d445
commit
78fa87737c
4 changed files with 164 additions and 83 deletions
|
@ -2,7 +2,7 @@ import discord
|
||||||
import orm
|
import orm
|
||||||
import re
|
import re
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from utils import send_verification_code, VerifyCode, Student, console, get_or_none, BannedStudentID
|
from utils import VerifyCode, Student, VerifyView
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,11 +11,9 @@ class VerifyCog(commands.Cog):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@commands.slash_command()
|
@commands.slash_command()
|
||||||
async def verify(self, ctx: discord.ApplicationContext, *, code: str = None):
|
@discord.guild_only()
|
||||||
|
async def verify(self, ctx: discord.ApplicationContext):
|
||||||
"""Verifies or generates a verification code"""
|
"""Verifies or generates a verification code"""
|
||||||
guild = self.bot.get_guild(config.guilds[0])
|
|
||||||
# if ctx.guild is not None:
|
|
||||||
# return await ctx.respond("\N{cross mark} This command can only be run in my DMs!", ephemeral=True)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
student: Student = await Student.objects.get(user_id=ctx.author.id)
|
student: Student = await Student.objects.get(user_id=ctx.author.id)
|
||||||
|
@ -23,81 +21,8 @@ class VerifyCog(commands.Cog):
|
||||||
except orm.NoMatch:
|
except orm.NoMatch:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if code is None:
|
view = VerifyView(ctx)
|
||||||
|
return await ctx.respond(view=view, ephemeral=True)
|
||||||
class Modal(discord.ui.Modal):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(
|
|
||||||
discord.ui.InputText(
|
|
||||||
custom_id="student_id",
|
|
||||||
label="What is your student ID",
|
|
||||||
placeholder="B...",
|
|
||||||
min_length=7,
|
|
||||||
max_length=7,
|
|
||||||
),
|
|
||||||
title="Enter your student ID number",
|
|
||||||
)
|
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
|
||||||
await interaction.response.defer()
|
|
||||||
st = self.children[0].value
|
|
||||||
if not st: # timed out
|
|
||||||
return
|
|
||||||
|
|
||||||
if not re.match(r"^B\d{6}$", st):
|
|
||||||
return await interaction.response.send_message(
|
|
||||||
"\N{cross mark} Invalid student ID.",
|
|
||||||
delete_after=60
|
|
||||||
)
|
|
||||||
|
|
||||||
ex = await get_or_none(Student, id=st)
|
|
||||||
if ex:
|
|
||||||
return await interaction.response.send_message(
|
|
||||||
"\N{cross mark} Student ID is already associated.",
|
|
||||||
delete_after=60
|
|
||||||
)
|
|
||||||
|
|
||||||
_code = await send_verification_code(ctx.author, st)
|
|
||||||
console.log(f"Sending verification email to {ctx.author} ({ctx.author.id}/{st})...")
|
|
||||||
__code = await VerifyCode.objects.create(code=_code, bind=ctx.author.id, student_id=st)
|
|
||||||
console.log(f"[green]Sent verification email to {ctx.author} ({ctx.author.id}/{st}): {_code!r}")
|
|
||||||
await interaction.followup.send(
|
|
||||||
"\N{white heavy check mark} Verification email sent to your college email "
|
|
||||||
f"({st}@my.leedscitycollege.ac.uk)\n"
|
|
||||||
f"Once you get that email, run this command again, with the first option being the 16"
|
|
||||||
f" character code.\n\n"
|
|
||||||
f">>> If you don't know how to access your email, go to <https://gmail.com>, then "
|
|
||||||
f"sign in as `{st}@leedscitycollege.ac.uk` (notice there's no `my.` "
|
|
||||||
f"prefix to sign into gmail), and you should be greeted by your inbox. The default password "
|
|
||||||
f"is your birthday, !, and the first three letters of your first or last name"
|
|
||||||
f" (for example, `John Doe`, born on the 1st of february 2006, would be either "
|
|
||||||
f"`01022006!Joh` or `01022006!Doe`).",
|
|
||||||
ephemeral=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return await ctx.send_modal(Modal())
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
existing: VerifyCode = await VerifyCode.objects.get(code=code)
|
|
||||||
except orm.NoMatch:
|
|
||||||
return await ctx.respond(
|
|
||||||
"\N{cross mark} Invalid or unknown verification code. Try again!", ephemeral=True
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
ban = await get_or_none(BannedStudentID, student_id=existing.student_id)
|
|
||||||
if ban is not None:
|
|
||||||
return await ctx.author.ban(
|
|
||||||
reason=f"Attempted to verify with banned student ID {ban.student_id}"
|
|
||||||
f" (originally associated with account {ban.associated_account})"
|
|
||||||
)
|
|
||||||
await Student.objects.create(id=existing.student_id, user_id=ctx.author.id)
|
|
||||||
await existing.delete()
|
|
||||||
role = discord.utils.find(lambda r: r.name.lower() == "verified", guild.roles)
|
|
||||||
if role and role < guild.me.top_role:
|
|
||||||
member = await guild.fetch_member(ctx.author.id)
|
|
||||||
await member.add_roles(role, reason="Verified")
|
|
||||||
console.log(f"[green]{ctx.author} verified ({ctx.author.id}/{existing.student_id})")
|
|
||||||
return await ctx.respond("\N{white heavy check mark} Verification complete!", ephemeral=True)
|
|
||||||
|
|
||||||
@commands.command(name="de-verify")
|
@commands.command(name="de-verify")
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
from ._email import *
|
from ._email import *
|
||||||
from .db import *
|
from .db import *
|
||||||
from .console import *
|
from .console import *
|
||||||
|
from .views import *
|
||||||
|
|
|
@ -7,14 +7,18 @@ import aiosmtplib as smtp
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
|
|
||||||
gmail_cfg = {"addr": "smtp.gmail.com", "username": config.email, "password": config.email_password, "port": 465}
|
gmail_cfg = {"addr": "smtp.gmail.com", "username": config.email, "password": config.email_password, "port": 465}
|
||||||
|
TOKEN_LENGTH = 16
|
||||||
|
|
||||||
|
|
||||||
async def send_verification_code(user: discord.User, student_number: str, **kwargs) -> str:
|
async def send_verification_code(user: discord.User, student_number: str, **kwargs) -> str:
|
||||||
"""Sends a verification code, returning said verification code, to the student."""
|
"""Sends a verification code, returning said verification code, to the student."""
|
||||||
code = secrets.token_hex(16)
|
code = secrets.token_hex(TOKEN_LENGTH)
|
||||||
text = (
|
text = (
|
||||||
f"Hey {user} ({student_number})! The code to join the Unscrupulous Nonsense is '{code}' - use "
|
f"Hey {user} ({student_number})! The code to join the Unscrupulous Nonsense is '{code}'.\n\n"
|
||||||
f"'/verify {code}' in the bot's DMs to continue \N{dancer}\n\n~nex"
|
f"Go back to the #verify channel, and click 'I have a verification code!', and put {code} in the modal"
|
||||||
|
f" that pops up\n\n"
|
||||||
|
f"If you have any issues getting in, feel free to reply to this email, or DM eek#7574.\n"
|
||||||
|
f"~Nex"
|
||||||
)
|
)
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
msg["From"] = "B593764@my.leedscitycollege.ac.uk"
|
msg["From"] = "B593764@my.leedscitycollege.ac.uk"
|
||||||
|
|
151
utils/views.py
Normal file
151
utils/views.py
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
import discord
|
||||||
|
import re
|
||||||
|
import orm
|
||||||
|
|
||||||
|
from utils import send_verification_code, get_or_none, Student, VerifyCode, console, TOKEN_LENGTH, BannedStudentID
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyView(discord.ui.View):
|
||||||
|
def __init__(self, ctx: discord.ApplicationContext):
|
||||||
|
self.ctx = ctx
|
||||||
|
super().__init__(timeout=300, disable_on_timeout=True)
|
||||||
|
|
||||||
|
@discord.ui.button(
|
||||||
|
label="I have a verification code!",
|
||||||
|
emoji="\U0001f4e7",
|
||||||
|
custom_id="have"
|
||||||
|
)
|
||||||
|
async def have(self, _, interaction1: discord.Interaction):
|
||||||
|
class Modal(discord.ui.Modal):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
discord.ui.InputText(
|
||||||
|
custom_id="code",
|
||||||
|
label="Verification Code",
|
||||||
|
placeholder="e.g: " + secrets.token_hex(TOKEN_LENGTH),
|
||||||
|
min_length=TOKEN_LENGTH*2,
|
||||||
|
max_length=TOKEN_LENGTH*2,
|
||||||
|
),
|
||||||
|
title="Enter the verification code in your inbox",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
await interaction.response.defer()
|
||||||
|
code = self.children[0].value
|
||||||
|
if not code: # timed out
|
||||||
|
self.stop()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
existing: VerifyCode = await VerifyCode.objects.get(code=code)
|
||||||
|
except orm.NoMatch:
|
||||||
|
self.stop()
|
||||||
|
return await interaction.followup.send(
|
||||||
|
"\N{cross mark} Invalid or unknown verification code. Try again!", ephemeral=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ban = await get_or_none(BannedStudentID, student_id=existing.student_id)
|
||||||
|
if ban is not None:
|
||||||
|
self.stop()
|
||||||
|
return await interaction.user.ban(
|
||||||
|
reason=f"Attempted to verify with banned student ID {ban.student_id}"
|
||||||
|
f" (originally associated with account {ban.associated_account})"
|
||||||
|
)
|
||||||
|
await Student.objects.create(id=existing.student_id, user_id=interaction.user.id)
|
||||||
|
await existing.delete()
|
||||||
|
role = discord.utils.find(lambda r: r.name.lower() == "verified", interaction.guild.roles)
|
||||||
|
if role and role < interaction.guild.me.top_role:
|
||||||
|
member = await interaction.guild.fetch_member(interaction.user.id)
|
||||||
|
await member.add_roles(role, reason="Verified")
|
||||||
|
console.log(f"[green]{interaction.user} verified ({interaction.user.id}/{existing.student_id})")
|
||||||
|
self.stop()
|
||||||
|
return await interaction.followup.send(
|
||||||
|
"\N{white heavy check mark} Verification complete!",
|
||||||
|
ephemeral=True
|
||||||
|
)
|
||||||
|
|
||||||
|
await interaction1.response.send_modal(Modal())
|
||||||
|
self.disable_all_items()
|
||||||
|
await interaction1.edit_original_response(view=self)
|
||||||
|
await interaction1.delete_original_response(delay=1)
|
||||||
|
|
||||||
|
@discord.ui.button(
|
||||||
|
label="Send me a verification code.",
|
||||||
|
emoji="\U0001f4e5"
|
||||||
|
)
|
||||||
|
async def send(self, btn: discord.ui.Button, interaction1: discord.Interaction):
|
||||||
|
class Modal(discord.ui.Modal):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
discord.ui.InputText(
|
||||||
|
custom_id="student_id",
|
||||||
|
label="What is your student ID",
|
||||||
|
placeholder="B...",
|
||||||
|
min_length=7,
|
||||||
|
max_length=7,
|
||||||
|
),
|
||||||
|
title="Enter your student ID number",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
await interaction.response.defer()
|
||||||
|
st = self.children[0].value
|
||||||
|
if not st: # timed out
|
||||||
|
return
|
||||||
|
|
||||||
|
if not re.match(r"^B\d{6}$", st):
|
||||||
|
return await interaction.response.send_message(
|
||||||
|
"\N{cross mark} Invalid student ID.",
|
||||||
|
delete_after=60
|
||||||
|
)
|
||||||
|
|
||||||
|
ex = await get_or_none(Student, id=st)
|
||||||
|
if ex:
|
||||||
|
return await interaction.response.send_message(
|
||||||
|
"\N{cross mark} Student ID is already associated.",
|
||||||
|
delete_after=60
|
||||||
|
)
|
||||||
|
|
||||||
|
_code = await send_verification_code(interaction.user, st)
|
||||||
|
console.log(f"Sending verification email to {interaction.user} ({interaction.user.id}/{st})...")
|
||||||
|
__code = await VerifyCode.objects.create(code=_code, bind=interaction.id, student_id=st)
|
||||||
|
console.log(f"[green]Sent verification email to {interaction.user} ({interaction.user.id}/{st}): "
|
||||||
|
f"{_code!r}")
|
||||||
|
await interaction.followup.send(
|
||||||
|
"\N{white heavy check mark} Verification email sent to your college email "
|
||||||
|
f"({st}@my.leedscitycollege.ac.uk)\n"
|
||||||
|
f"Once you get that email, run this command again, with the first option being the 16"
|
||||||
|
f" character code.\n\n"
|
||||||
|
f">>> If you don't know how to access your email, go to <https://gmail.com>, then "
|
||||||
|
f"sign in as `{st}@leedscitycollege.ac.uk` (notice there's no `my.` "
|
||||||
|
f"prefix to sign into gmail), and you should be greeted by your inbox. The default password "
|
||||||
|
f"is your birthday, !, and the first three letters of your first or last name"
|
||||||
|
f" (for example, `John Doe`, born on the 1st of february 2006, would be either "
|
||||||
|
f"`01022006!Joh` or `01022006!Doe`).",
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
await interaction1.response.send_modal(Modal())
|
||||||
|
btn.disabled = True
|
||||||
|
await interaction1.edit_original_response(view=self)
|
||||||
|
|
||||||
|
@discord.ui.button(
|
||||||
|
label="Why do I need a verification code?",
|
||||||
|
emoji="\U0001f616"
|
||||||
|
)
|
||||||
|
async def why(self, _, interaction: discord.Interaction):
|
||||||
|
await interaction.response.defer(
|
||||||
|
ephemeral=True
|
||||||
|
)
|
||||||
|
await interaction.followup.send(
|
||||||
|
"In order to access this server, you need to enter your student ID.\n"
|
||||||
|
"We require this to make sure only **students** in our course can access the server.\n"
|
||||||
|
"Your B number (student ID) is found on your ID card (the one you use to scan into the building).\n"
|
||||||
|
"This is not invading your privacy, your B number is publicly visible, as it is the start of your email,"
|
||||||
|
" plus can be found on google chat.",
|
||||||
|
ephemeral=True,
|
||||||
|
delete_after=60
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue