Prepare for HTTP server

This commit is contained in:
Nexus 2023-02-22 15:17:53 +00:00
parent bc9cd015db
commit 5b8c75a549
Signed by: nex
GPG key ID: 0FA334385D0B689F
9 changed files with 340 additions and 185 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ venv
domains.txt
geckodriver.log
targets.json
*.kdev4

View file

@ -9,7 +9,10 @@
<auth-provider>no-auth</auth-provider>
<schema-mapping>
<introspection-scope>
<node kind="schema" qname="@" />
<node kind="schema">
<name qname="@" />
<name qname="main" />
</node>
</introspection-scope>
</schema-mapping>
</data-source>

View file

@ -1316,305 +1316,339 @@
<table id="482" parent="166" name="assignments"/>
<table id="483" parent="166" name="banned"/>
<table id="484" parent="166" name="codes"/>
<table id="485" parent="166" name="sqlite_master">
<table id="485" parent="166" name="jimmy_bans"/>
<table id="486" parent="166" name="sqlite_master">
<System>1</System>
</table>
<table id="486" parent="166" name="starboard"/>
<table id="487" parent="166" name="students"/>
<table id="488" parent="166" name="uptime"/>
<column id="489" parent="482" name="entry_id">
<table id="487" parent="166" name="starboard"/>
<table id="488" parent="166" name="students"/>
<table id="489" parent="166" name="uptime"/>
<column id="490" parent="482" name="entry_id">
<DasType>INTEGER|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="490" parent="482" name="created_by">
<column id="491" parent="482" name="created_by">
<DasType>CHAR(32)|0s</DasType>
<Position>2</Position>
</column>
<column id="491" parent="482" name="title">
<column id="492" parent="482" name="title">
<DasType>VARCHAR(2000)|0s</DasType>
<NotNull>1</NotNull>
<Position>3</Position>
</column>
<column id="492" parent="482" name="classroom">
<column id="493" parent="482" name="classroom">
<DasType>VARCHAR(4096)|0s</DasType>
<Position>4</Position>
</column>
<column id="493" parent="482" name="shared_doc">
<column id="494" parent="482" name="shared_doc">
<DasType>VARCHAR(4096)|0s</DasType>
<Position>5</Position>
</column>
<column id="494" parent="482" name="created_at">
<column id="495" parent="482" name="created_at">
<DasType>FLOAT|0s</DasType>
<NotNull>1</NotNull>
<Position>6</Position>
</column>
<column id="495" parent="482" name="due_by">
<column id="496" parent="482" name="due_by">
<DasType>FLOAT|0s</DasType>
<NotNull>1</NotNull>
<Position>7</Position>
</column>
<column id="496" parent="482" name="tutor">
<column id="497" parent="482" name="tutor">
<DasType>VARCHAR(7)|0s</DasType>
<NotNull>1</NotNull>
<Position>8</Position>
</column>
<column id="497" parent="482" name="reminders">
<column id="498" parent="482" name="reminders">
<DasType>JSON|0s</DasType>
<NotNull>1</NotNull>
<Position>9</Position>
</column>
<column id="498" parent="482" name="finished">
<column id="499" parent="482" name="finished">
<DasType>BOOLEAN|0s</DasType>
<NotNull>1</NotNull>
<Position>10</Position>
</column>
<column id="499" parent="482" name="submitted">
<column id="500" parent="482" name="submitted">
<DasType>BOOLEAN|0s</DasType>
<NotNull>1</NotNull>
<Position>11</Position>
</column>
<column id="500" parent="482" name="assignees">
<column id="501" parent="482" name="assignees">
<DasType>JSON|0s</DasType>
<NotNull>1</NotNull>
<Position>12</Position>
</column>
<foreign-key id="501" parent="482">
<foreign-key id="502" parent="482">
<ColNames>created_by</ColNames>
<RefColNames>entry_id</RefColNames>
<RefTableName>students</RefTableName>
</foreign-key>
<key id="502" parent="482">
<key id="503" parent="482">
<ColNames>entry_id</ColNames>
<Primary>1</Primary>
</key>
<column id="503" parent="483" name="entry_id">
<column id="504" parent="483" name="entry_id">
<DasType>CHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="504" parent="483" name="student_id">
<column id="505" parent="483" name="student_id">
<DasType>VARCHAR(7)|0s</DasType>
<NotNull>1</NotNull>
<Position>2</Position>
</column>
<column id="505" parent="483" name="associated_account">
<column id="506" parent="483" name="associated_account">
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<Position>3</Position>
</column>
<column id="506" parent="483" name="banned_at_timestamp">
<column id="507" parent="483" name="banned_at_timestamp">
<DasType>FLOAT|0s</DasType>
<NotNull>1</NotNull>
<Position>4</Position>
</column>
<index id="507" parent="483" name="sqlite_autoindex_banned_1">
<index id="508" parent="483" name="sqlite_autoindex_banned_1">
<ColNames>entry_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<index id="508" parent="483" name="sqlite_autoindex_banned_2">
<index id="509" parent="483" name="sqlite_autoindex_banned_2">
<ColNames>student_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="509" parent="483">
<key id="510" parent="483">
<ColNames>entry_id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_banned_1</UnderlyingIndexName>
</key>
<key id="510" parent="483">
<key id="511" parent="483">
<ColNames>student_id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_banned_2</UnderlyingIndexName>
</key>
<column id="511" parent="484" name="id">
<column id="512" parent="484" name="id">
<DasType>INTEGER|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="512" parent="484" name="code">
<column id="513" parent="484" name="code">
<DasType>VARCHAR(64)|0s</DasType>
<NotNull>1</NotNull>
<Position>2</Position>
</column>
<column id="513" parent="484" name="bind">
<column id="514" parent="484" name="bind">
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<Position>3</Position>
</column>
<column id="514" parent="484" name="student_id">
<column id="515" parent="484" name="student_id">
<DasType>VARCHAR(7)|0s</DasType>
<NotNull>1</NotNull>
<Position>4</Position>
</column>
<column id="515" parent="484" name="name">
<column id="516" parent="484" name="name">
<DasType>VARCHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>5</Position>
</column>
<index id="516" parent="484" name="sqlite_autoindex_codes_1">
<index id="517" parent="484" name="sqlite_autoindex_codes_1">
<ColNames>code</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="517" parent="484">
<key id="518" parent="484">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<key id="518" parent="484">
<key id="519" parent="484">
<ColNames>code</ColNames>
<UnderlyingIndexName>sqlite_autoindex_codes_1</UnderlyingIndexName>
</key>
<column id="519" parent="485" name="type">
<DasType>TEXT|0s</DasType>
<Position>1</Position>
</column>
<column id="520" parent="485" name="name">
<DasType>TEXT|0s</DasType>
<Position>2</Position>
</column>
<column id="521" parent="485" name="tbl_name">
<DasType>TEXT|0s</DasType>
<Position>3</Position>
</column>
<column id="522" parent="485" name="rootpage">
<DasType>INT|0s</DasType>
<Position>4</Position>
</column>
<column id="523" parent="485" name="sql">
<DasType>TEXT|0s</DasType>
<Position>5</Position>
</column>
<column id="524" parent="486" name="entry_id">
<column id="520" parent="485" name="entry_id">
<DasType>CHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="525" parent="486" name="id">
<column id="521" parent="485" name="user_id">
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<Position>2</Position>
</column>
<column id="526" parent="486" name="channel">
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<column id="522" parent="485" name="reason">
<DasType>TEXT|0s</DasType>
<Position>3</Position>
</column>
<column id="527" parent="486" name="starboard_message">
<DasType>BIGINT|0s</DasType>
<column id="523" parent="485" name="timestamp">
<DasType>FLOAT|0s</DasType>
<NotNull>1</NotNull>
<Position>4</Position>
</column>
<index id="528" parent="486" name="sqlite_autoindex_starboard_1">
<column id="524" parent="485" name="until">
<DasType>FLOAT|0s</DasType>
<Position>5</Position>
</column>
<index id="525" parent="485" name="sqlite_autoindex_jimmy_bans_1">
<ColNames>entry_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<index id="529" parent="486" name="sqlite_autoindex_starboard_2">
<ColNames>id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="530" parent="486">
<key id="526" parent="485">
<ColNames>entry_id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_starboard_1</UnderlyingIndexName>
</key>
<key id="531" parent="486">
<ColNames>id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_starboard_2</UnderlyingIndexName>
<UnderlyingIndexName>sqlite_autoindex_jimmy_bans_1</UnderlyingIndexName>
</key>
<column id="527" parent="486" name="type">
<DasType>TEXT|0s</DasType>
<Position>1</Position>
</column>
<column id="528" parent="486" name="name">
<DasType>TEXT|0s</DasType>
<Position>2</Position>
</column>
<column id="529" parent="486" name="tbl_name">
<DasType>TEXT|0s</DasType>
<Position>3</Position>
</column>
<column id="530" parent="486" name="rootpage">
<DasType>INT|0s</DasType>
<Position>4</Position>
</column>
<column id="531" parent="486" name="sql">
<DasType>TEXT|0s</DasType>
<Position>5</Position>
</column>
<column id="532" parent="487" name="entry_id">
<DasType>CHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="533" parent="487" name="id">
<DasType>VARCHAR(7)|0s</DasType>
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<Position>2</Position>
</column>
<column id="534" parent="487" name="user_id">
<column id="534" parent="487" name="channel">
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<Position>3</Position>
</column>
<column id="535" parent="487" name="name">
<DasType>VARCHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<column id="535" parent="487" name="starboard_message">
<DasType>BIGINT|0s</DasType>
<Position>4</Position>
</column>
<index id="536" parent="487" name="sqlite_autoindex_students_1">
<index id="536" parent="487" name="sqlite_autoindex_starboard_1">
<ColNames>entry_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<index id="537" parent="487" name="sqlite_autoindex_students_2">
<index id="537" parent="487" name="sqlite_autoindex_starboard_2">
<ColNames>id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<index id="538" parent="487" name="sqlite_autoindex_students_3">
<ColNames>user_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="539" parent="487">
<key id="538" parent="487">
<ColNames>entry_id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_students_1</UnderlyingIndexName>
<UnderlyingIndexName>sqlite_autoindex_starboard_1</UnderlyingIndexName>
</key>
<key id="540" parent="487">
<key id="539" parent="487">
<ColNames>id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_students_2</UnderlyingIndexName>
<UnderlyingIndexName>sqlite_autoindex_starboard_2</UnderlyingIndexName>
</key>
<key id="541" parent="487">
<ColNames>user_id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_students_3</UnderlyingIndexName>
</key>
<column id="542" parent="488" name="entry_id">
<column id="540" parent="488" name="entry_id">
<DasType>CHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="543" parent="488" name="target_id">
<DasType>VARCHAR(128)|0s</DasType>
<column id="541" parent="488" name="id">
<DasType>VARCHAR(7)|0s</DasType>
<NotNull>1</NotNull>
<Position>2</Position>
</column>
<column id="544" parent="488" name="target">
<DasType>VARCHAR(128)|0s</DasType>
<column id="542" parent="488" name="user_id">
<DasType>BIGINT|0s</DasType>
<NotNull>1</NotNull>
<Position>3</Position>
</column>
<column id="545" parent="488" name="is_up">
<DasType>BOOLEAN|0s</DasType>
<column id="543" parent="488" name="name">
<DasType>VARCHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>4</Position>
</column>
<column id="546" parent="488" name="timestamp">
<DasType>FLOAT|0s</DasType>
<NotNull>1</NotNull>
<Position>5</Position>
</column>
<column id="547" parent="488" name="response_time">
<DasType>INTEGER|0s</DasType>
<Position>6</Position>
</column>
<column id="548" parent="488" name="notes">
<DasType>TEXT|0s</DasType>
<Position>7</Position>
</column>
<column id="549" parent="488" name="notice_sent">
<DasType>BOOLEAN|0s</DasType>
<NotNull>1</NotNull>
<Position>8</Position>
</column>
<index id="550" parent="488" name="sqlite_autoindex_uptime_1">
<index id="544" parent="488" name="sqlite_autoindex_students_1">
<ColNames>entry_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="551" parent="488">
<index id="545" parent="488" name="sqlite_autoindex_students_2">
<ColNames>id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<index id="546" parent="488" name="sqlite_autoindex_students_3">
<ColNames>user_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="547" parent="488">
<ColNames>entry_id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_students_1</UnderlyingIndexName>
</key>
<key id="548" parent="488">
<ColNames>id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_students_2</UnderlyingIndexName>
</key>
<key id="549" parent="488">
<ColNames>user_id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_students_3</UnderlyingIndexName>
</key>
<column id="550" parent="489" name="entry_id">
<DasType>CHAR(32)|0s</DasType>
<NotNull>1</NotNull>
<Position>1</Position>
</column>
<column id="551" parent="489" name="target_id">
<DasType>VARCHAR(128)|0s</DasType>
<NotNull>1</NotNull>
<Position>2</Position>
</column>
<column id="552" parent="489" name="target">
<DasType>VARCHAR(128)|0s</DasType>
<NotNull>1</NotNull>
<Position>3</Position>
</column>
<column id="553" parent="489" name="is_up">
<DasType>BOOLEAN|0s</DasType>
<NotNull>1</NotNull>
<Position>4</Position>
</column>
<column id="554" parent="489" name="timestamp">
<DasType>FLOAT|0s</DasType>
<NotNull>1</NotNull>
<Position>5</Position>
</column>
<column id="555" parent="489" name="response_time">
<DasType>INTEGER|0s</DasType>
<Position>6</Position>
</column>
<column id="556" parent="489" name="notes">
<DasType>TEXT|0s</DasType>
<Position>7</Position>
</column>
<column id="557" parent="489" name="notice_sent">
<DasType>BOOLEAN|0s</DasType>
<NotNull>1</NotNull>
<Position>8</Position>
</column>
<index id="558" parent="489" name="sqlite_autoindex_uptime_1">
<ColNames>entry_id</ColNames>
<NameSurrogate>1</NameSurrogate>
<Unique>1</Unique>
</index>
<key id="559" parent="489">
<ColNames>entry_id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_uptime_1</UnderlyingIndexName>

View file

@ -13,6 +13,7 @@ import os
import discord
# The IDs of guilds the bot should be in; used to determine where to make slash commands
# If you put multiple IDs in here, the first one should be your "primary" server.
guilds = [994710566612500550]
# Email & email password for the email verification system
@ -46,7 +47,15 @@ HTTP_HOST = "127.0.0.1"
HTTP_PORT = 3762
# You can also just fully turn the web server off
WEB_SERVER = False
WEB_SERVER = True # change this to False to disable it
# Or change uvicorn settings (see: https://www.uvicorn.org/settings/)
# Note that passing `host` or `port` will raise an error, as those are configured above.
UVICORN_CONFIG = {
"log_level": "error",
"access_log": False,
"lifespan": "off"
}
# Only change this if you want to test changes to the bot without sending too much traffic to discord.
# Connect modes:

93
main.py
View file

@ -1,61 +1,11 @@
import asyncio
import discord
from discord.ext import commands
from asyncio import Lock
import config
from datetime import datetime, timezone, timedelta
from utils import registry, console, get_or_none, JimmyBans
from web.server import app
import uvicorn
intents = discord.Intents.default()
intents += discord.Intents.messages
intents += discord.Intents.message_content
intents += discord.Intents.members
intents += discord.Intents.presences
extensions = [
"jishaku",
"cogs.verify",
"cogs.mod",
"cogs.events",
"cogs.assignments",
"cogs.timetable",
"cogs.other",
"cogs.starboard",
"cogs.uptime",
]
class Bot(commands.Bot):
def __init__(self):
super().__init__(
command_prefix=self.get_prefix,
debug_guilds=config.guilds,
allowed_mentions=discord.AllowedMentions.none(),
intents=intents,
)
self.training_lock = Lock()
self.started_at = datetime.now(tz=timezone.utc)
self.bans = JimmyBans()
for ext in extensions:
try:
bot.load_extension(ext)
except discord.ExtensionFailed as e:
console.log(f"[red]Failed to load extension {ext}: {e}")
else:
console.log(f"Loaded extension [green]{ext}")
app.state.bot = self
config = uvicorn.Config(
app,
port=3762
)
bot = Bot()
bot.loop.run_until_complete(registry.create_all())
from utils import console, get_or_none, JimmyBans
from utils.client import bot
@bot.listen()
@ -101,6 +51,9 @@ async def on_application_command(ctx: discord.ApplicationContext):
@bot.event
async def on_ready():
console.log("Logged in as", bot.user)
if getattr(config, "CONNECT_MODE", None) == 1:
console.log("Bot is now ready and exit target 1 is set, shutting down.")
await bot.close()
@bot.slash_command()
@ -135,4 +88,36 @@ async def check_not_banned(ctx: discord.ApplicationContext | commands.Context):
if __name__ == "__main__":
console.log("Starting...")
bot.started_at = discord.utils.utcnow()
if getattr(config, "WEB_SERVER", True):
from web.server import app
import uvicorn
http_config = uvicorn.Config(
app,
host=getattr(config, "HTTP_HOST", "127.0.0.1"),
port=getattr(config, "HTTP_PORT", 3762),
lifespan="off",
access_log=False,
**getattr(config, "UVICORN_CONFIG", {})
)
server = uvicorn.Server(http_config)
console.log("Starting web server...")
loop = bot.loop
http_server_task = loop.create_task(server.serve())
bot.web = {
"server": server,
"config": http_config,
"task": http_server_task,
}
bot.run(config.token)
if hasattr(bot, "web"):
console.log("Cancelling web task...")
bot.web["task"].cancel()
console.log("Shutting down web server...")
try:
bot.web["task"].result()
except asyncio.CancelledError:
pass
console.log("Web server closed.")

View file

@ -13,4 +13,3 @@ aiofiles==22.1.0
httpx==0.23.0
fastapi==0.92.0
uvicorn==0.20.0
bcrypt==4.0.1

64
utils/client.py Normal file
View file

@ -0,0 +1,64 @@
import discord
import config
from asyncio import Lock
from discord.ext import commands
from datetime import datetime, timezone
__all__ = ("Bot", 'bot')
# noinspection PyAbstractClass
class Bot(commands.Bot):
def __init__(self, intents: discord.Intents, guilds: list[int], extensions: list[str]):
from .db import JimmyBans, registry
from .console import console
super().__init__(
command_prefix=self.get_prefix,
debug_guilds=guilds,
allowed_mentions=discord.AllowedMentions.none(),
intents=intents,
)
self.loop.run_until_complete(registry.create_all())
self.training_lock = Lock()
self.started_at = datetime.now(tz=timezone.utc)
self.bans = JimmyBans()
self.console = console
for ext in extensions:
try:
self.load_extension(ext)
except discord.ExtensionFailed as e:
console.log(f"[red]Failed to load extension {ext}: {e}")
if getattr(config, "dev", False):
console.print_exception()
else:
console.log(f"Loaded extension [green]{ext}")
if getattr(config, "CONNECT_MODE", None) == 2:
async def connect(self, *, reconnect: bool = True) -> None:
self.console.log("Exit target 2 reached, shutting down (not connecting to discord).")
return
try:
from config import intents as _intents
except ImportError:
_intents = discord.Intents.all()
try:
from config import extensions as _extensions
except ImportError:
_extensions = [
"jishaku",
"cogs.verify",
"cogs.mod",
"cogs.events",
"cogs.assignments",
"cogs.timetable",
"cogs.other",
"cogs.starboard",
"cogs.uptime",
]
bot = Bot(_intents, config.guilds, _extensions)

View file

@ -83,6 +83,9 @@ class Student(orm.Model):
id: str
user_id: int
name: str
access_token: str | None
ip_info: dict | None
access_token_hash: str | None
class BannedStudentID(orm.Model):

View file

@ -1,11 +1,13 @@
import discord
import os
import httpx
from datetime import datetime, timedelta, timezone
from datetime import datetime, timezone
from hashlib import sha512
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse, RedirectResponse
from utils.db import Student, get_or_none
from utils import Student, get_or_none, VerifyCode, console, BannedStudentID
from config import guilds
try:
from config import OAUTH_ID, OAUTH_SECRET, OAUTH_REDIRECT_URI
@ -37,9 +39,10 @@ def ping():
"ping": "pong",
"online": app.state.bot.is_ready(),
"latency": app.state.bot.latency,
"uptime": bot_started
"uptime": bot_started.total_seconds()
}
@app.get("/auth")
async def authenticate(req: Request, code: str = None, state: str = None):
if not (code and state) or state not in app.state.states:
@ -94,7 +97,7 @@ async def authenticate(req: Request, code: str = None, state: str = None):
user = response.json()
# Now we need to fetch the student from the database
student = await get_or_none(Student, discord_id=user["id"])
student = await get_or_none(Student, user_id=user["id"])
if not student:
raise HTTPException(
status_code=404,
@ -125,15 +128,69 @@ async def authenticate(req: Request, code: str = None, state: str = None):
"/",
status_code=307,
headers={
"Cache-Control": "max-age=86400"
"Cache-Control": "max-age=604800"
}
)
# set the cookie for at most 86400 seconds - expire after that
# set the cookie for at most 604800 seconds - expire after that
response.set_cookie(
"token",
token,
max_age=86400,
same_site="strict",
max_age=604800,
samesite="strict",
httponly=True,
)
return response
@app.get("/verify/{code}")
async def verify(code: str):
guild = app.state.bot.get_guild(guilds[0])
if not guild:
raise HTTPException(
status_code=503,
detail="Not ready."
)
# First, we need to fetch the code from the database
verify_code = await get_or_none(VerifyCode, code=code)
if not verify_code:
raise HTTPException(
status_code=404,
detail="Code not found."
)
# Now we need to fetch the student from the database
student = await get_or_none(Student, user_id=verify_code.bind)
if student:
raise HTTPException(
status_code=400,
detail="Already verified."
)
ban = await get_or_none(BannedStudentID, student_id=verify_code.student_id)
if ban is not None:
return await guild.kick(
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=verify_code.student_id, user_id=verify_code.bind, name=verify_code.name
)
await verify_code.delete()
role = discord.utils.find(lambda r: r.name.lower() == "verified", guild.roles)
member = await guild.fetch_member(verify_code.bind)
if role and role < guild.me.top_role:
await member.add_roles(role, reason="Verified")
try:
await member.edit(nick=f"{verify_code.name}", reason="Verified")
except discord.HTTPException:
pass
# And delete the code
await verify_code.delete()
console.log(f"[green]{verify_code.bind} verified ({verify_code.bing}/{verify_code.student_id})")
return {
"message": "Successfully verified."
}