mirror of
https://github.com/nexy7574/LCC-bot.git
synced 2024-09-19 18:16:34 +01:00
Email verification complete
This commit is contained in:
parent
ed1e3a25bc
commit
f097140133
8 changed files with 222 additions and 1 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
main.db
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
109
cogs/verify.py
Normal file
109
cogs/verify.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import discord
|
||||||
|
import orm
|
||||||
|
from discord.ext import commands
|
||||||
|
from utils import send_verification_code, VerifyCode, Student
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyCog(commands.Cog):
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@commands.slash_command()
|
||||||
|
async def verify(self, ctx: discord.ApplicationContext, *, code: str = None):
|
||||||
|
"""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:
|
||||||
|
student: Student = await Student.objects.get(user_id=ctx.author.id)
|
||||||
|
return await ctx.respond(f"\N{cross mark} You're already verified as {student.id}!", ephemeral=True)
|
||||||
|
except orm.NoMatch:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if code is None:
|
||||||
|
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()
|
||||||
|
if not self.children[0].value: # timed out
|
||||||
|
return
|
||||||
|
_code = await send_verification_code(
|
||||||
|
ctx.author,
|
||||||
|
self.children[0].value
|
||||||
|
)
|
||||||
|
__code = await VerifyCode.objects.create(
|
||||||
|
code=_code,
|
||||||
|
bind=ctx.author.id,
|
||||||
|
student_id=self.children[0].value
|
||||||
|
)
|
||||||
|
await interaction.followup.send(
|
||||||
|
"\N{white heavy check mark} Verification email sent to your college email "
|
||||||
|
f"({self.children[0].value}@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 `{self.children[0].value}@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:
|
||||||
|
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")
|
||||||
|
return await ctx.respond(
|
||||||
|
"\N{white heavy check mark} Verification complete!"
|
||||||
|
)
|
||||||
|
|
||||||
|
@commands.command(name="de-verify")
|
||||||
|
@commands.is_owner()
|
||||||
|
async def verification_del(self, ctx: commands.Context, *, user: discord.Member):
|
||||||
|
"""Removes a user's verification status"""
|
||||||
|
await ctx.trigger_typing()
|
||||||
|
for code in await VerifyCode.objects.all(bind=user.id):
|
||||||
|
await code.delete()
|
||||||
|
usr = await Student.objects.first(user_id=user.id)
|
||||||
|
if usr:
|
||||||
|
await usr.delete()
|
||||||
|
|
||||||
|
role = discord.utils.find(lambda r: r.name.lower() == "verified", ctx.guild.roles)
|
||||||
|
if role and role < ctx.me.top_role:
|
||||||
|
await user.remove_roles(role, reason=f"De-verified by {ctx.author}")
|
||||||
|
|
||||||
|
return await ctx.reply(f"\N{white heavy check mark} De-verified {user}.")
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(VerifyCog(bot))
|
6
main.py
6
main.py
|
@ -1,12 +1,16 @@
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import config
|
import config
|
||||||
|
from utils import registry
|
||||||
|
|
||||||
|
|
||||||
bot = commands.Bot(
|
bot = commands.Bot(
|
||||||
commands.when_mentioned_or("h!"),
|
commands.when_mentioned_or("h!"),
|
||||||
debug_guilds=config.guilds
|
debug_guilds=config.guilds,
|
||||||
|
allowed_mentions=discord.AllowedMentions.none()
|
||||||
)
|
)
|
||||||
|
bot.load_extension("cogs.verify")
|
||||||
|
bot.loop.run_until_complete(registry.create_all())
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
|
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
py-cord==2.1.3
|
||||||
|
aiosmtplib==1.1.7
|
||||||
|
orm[sqlite]==0.3.1
|
||||||
|
httpx==0.23.0
|
2
utils/__init__.py
Normal file
2
utils/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
from ._email import *
|
||||||
|
from .db import *
|
57
utils/_email.py
Normal file
57
utils/_email.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
import discord
|
||||||
|
|
||||||
|
import config
|
||||||
|
import aiosmtplib as smtp
|
||||||
|
from email.message import EmailMessage
|
||||||
|
|
||||||
|
gmail_cfg = {
|
||||||
|
"addr": "smtp.gmail.com",
|
||||||
|
"username": config.email,
|
||||||
|
"password": config.email_password,
|
||||||
|
"port": 465
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def send_verification_code(
|
||||||
|
user: discord.User,
|
||||||
|
student_number: str,
|
||||||
|
**kwargs
|
||||||
|
) -> str:
|
||||||
|
"""Sends a verification code, returning said verification code, to the student."""
|
||||||
|
code = secrets.token_hex(16)
|
||||||
|
text = f"Hey {user} ({student_number})! The code to join the hi^5 code is '{code}' - use " \
|
||||||
|
f"'/verify {code}' in the bot's DMs to continue \N{dancer}\n\n~nex"
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["From"] = gmail_cfg["username"]
|
||||||
|
msg["To"] = f"{student_number}@my.leedscitycollege.ac.uk"
|
||||||
|
msg["Subject"] = "Server Verification"
|
||||||
|
msg.set_content(text)
|
||||||
|
|
||||||
|
kwargs.setdefault(
|
||||||
|
"hostname", gmail_cfg["addr"]
|
||||||
|
)
|
||||||
|
kwargs.setdefault(
|
||||||
|
"port", gmail_cfg["port"]
|
||||||
|
)
|
||||||
|
kwargs.setdefault(
|
||||||
|
"use_tls", True
|
||||||
|
)
|
||||||
|
kwargs.setdefault(
|
||||||
|
"username", gmail_cfg["username"]
|
||||||
|
)
|
||||||
|
kwargs.setdefault(
|
||||||
|
"password", gmail_cfg["password"]
|
||||||
|
)
|
||||||
|
kwargs.setdefault(
|
||||||
|
"start_tls", not kwargs["use_tls"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert kwargs["start_tls"] != kwargs["use_tls"]
|
||||||
|
|
||||||
|
await smtp.send(
|
||||||
|
msg,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
return code
|
38
utils/db.py
Normal file
38
utils/db.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import uuid
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import orm
|
||||||
|
from databases import Database
|
||||||
|
|
||||||
|
|
||||||
|
registry = orm.ModelRegistry(Database("sqlite:///main.db"))
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyCode(orm.Model):
|
||||||
|
registry = registry
|
||||||
|
tablename = "codes"
|
||||||
|
fields = {
|
||||||
|
"id": orm.Integer(primary_key=True),
|
||||||
|
"code": orm.String(min_length=8, max_length=64, unique=True),
|
||||||
|
"bind": orm.BigInteger(),
|
||||||
|
"student_id": orm.String(min_length=7, max_length=7)
|
||||||
|
}
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
id: int
|
||||||
|
code: str
|
||||||
|
bind: int
|
||||||
|
student_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class Student(orm.Model):
|
||||||
|
registry = registry
|
||||||
|
tablename = "students"
|
||||||
|
fields = {
|
||||||
|
"entry_id": orm.UUID(primary_key=True, default=uuid.uuid4),
|
||||||
|
"id": orm.String(min_length=7, max_length=7, unique=True),
|
||||||
|
"user_id": orm.BigInteger(unique=True),
|
||||||
|
}
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
entry_id: uuid.UUID
|
||||||
|
id: str
|
||||||
|
user_id: int
|
Loading…
Reference in a new issue