66from email .message import EmailMessage
77from email .mime .text import MIMEText
88
9- import brawlstats
109from sanic import Blueprint , response
11- from sanic .exceptions import abort
10+ from sanic .exceptions import Forbidden , NotFound , ServerError
11+ from sanic .request import Request
1212
1313from core .utils import (add_message , disable_xss , get_school_week ,
1414 login_required , open_db_connection , render_template )
1515
1616root = Blueprint ('root' )
1717
1818@root .middleware ('request' )
19- async def setup_session_dict (request ):
19+ async def setup_session_dict (request : Request ):
2020 """Sets up session attributes if they do not exist already"""
2121 if request .ctx .session .get ('logged_in' , None ) is None :
2222 request .ctx .session ['logged_in' ] = False
@@ -25,46 +25,44 @@ async def setup_session_dict(request):
2525 request .ctx .session ['messages' ] = []
2626
2727@root .get ('/' )
28- async def index (request ):
29- async with request .app .session .get ('https://api.github.com/users/SharpBit/events/public' ) as resp :
28+ async def index (request : Request ):
29+ async with request .app .ctx . aiohttp .get ('https://api.github.com/users/SharpBit/events/public' ) as resp :
3030 info = await resp .json ()
3131 recent_commits = filter (lambda x : x ['repo' ]['name' ] != 'SharpBit/modmail' and x ['type' ] == 'PushEvent' , info )
3232 return await render_template ('index' , request , title = "Home Page" , description = 'Home Page' , recent = recent_commits )
3333
34- @root .get ('/invite' )
35- async def invite (request ):
36- return response .redirect ('https://discord.gg/C2tnmHa' )
37-
3834@root .get ('/repo/<name>' )
39- async def repo (request , name ):
35+ async def repo (request : Request , name : str ):
4036 return response .redirect (f'https://github.com/SharpBit/{ name } ' )
4137
42-
4338@root .get ('/login' )
44- async def login (request ):
39+ async def login (request : Request ):
4540 if request .ctx .session ['logged_in' ]:
4641 return response .redirect ('/' )
47- return response .redirect (request .app .oauth .discord_login_url )
42+ return response .redirect (request .app .ctx . oauth .discord_login_url )
4843
4944@root .get ('/callback' )
50- async def callback (request ):
45+ async def callback (request : Request ):
5146 app = request .app
5247 code = request .args .get ('code' )
53- access_token = await app .oauth .get_access_token (code )
54- user = await app .oauth .get_user_json (access_token )
48+ access_token = await app .ctx . oauth .get_access_token (code )
49+ user = await app .ctx . oauth .get_user_json (access_token )
5550 if user .get ('message' ):
5651 return await render_template ('unauthorized' , request , description = 'Discord Oauth Unauthorized.' )
5752
5853 if user .get ('avatar' ):
59- avatar = ' https://cdn.discordapp.com/avatars/{}/{}.png' . format ( user ['id' ], user ['avatar' ])
60- else : # in case of default avatar users
61- avatar = ' https://cdn.discordapp.com/embed/avatars/{}.png' . format ( user ['discriminator' ] % 5 )
54+ avatar = f" https://cdn.discordapp.com/avatars/{ user ['id' ]} / { user ['avatar' ]} .png"
55+ else : # in case of default avatar users
56+ avatar = f" https://cdn.discordapp.com/embed/avatars/{ user ['discriminator' ] % 5 } .png"
6257
6358 async with open_db_connection (request .app ) as conn :
6459 await conn .executemany (
6560 '''INSERT INTO users(id, name, discrim, avatar) VALUES ($1, $2, $3, $4)
6661 ON CONFLICT (id) DO UPDATE SET id=$1, name=$2, discrim=$3, avatar=$4''' ,
67- [(user ['id' ], user ['username' ], user ['discriminator' ], avatar ), (user ['id' ], user ['username' ], user ['discriminator' ], avatar )]
62+ [
63+ (user ['id' ], user ['username' ], user ['discriminator' ], avatar ),
64+ (user ['id' ], user ['username' ], user ['discriminator' ], avatar )
65+ ]
6866 )
6967
7068 request .ctx .session ['logged_in' ] = True
@@ -73,14 +71,14 @@ async def callback(request):
7371 return response .redirect ('/dashboard' )
7472
7573@root .get ('/logout' )
76- async def logout (request ):
74+ async def logout (request : Request ):
7775 del request .ctx .session ['logged_in' ]
7876 del request .ctx .session ['id' ]
7977 return response .redirect ('/' )
8078
8179@root .get ('/dashboard' )
8280@login_required ()
83- async def dashboard_home (request ):
81+ async def dashboard_home (request : Request ):
8482 async with open_db_connection (request .app ) as conn :
8583 urls = await conn .fetch ('SELECT * FROM urls WHERE user_id = $1' , request .ctx .session ['id' ])
8684 pastes = await conn .fetch ('SELECT * FROM pastebin WHERE user_id = $1' , request .ctx .session ['id' ])
@@ -94,12 +92,12 @@ async def dashboard_home(request):
9492 )
9593
9694@root .get ('/urlshortener' )
97- async def url_shortener_home (request ):
95+ async def url_shortener_home (request : Request ):
9896 return await render_template ('url_shortener' , request , title = 'URL Shortener' , description = 'Shorten a URL!' )
9997
10098@root .post ('/url/create' )
10199# @authorized()
102- async def create_url (request ):
100+ async def create_url (request : Request ):
103101 chars = string .ascii_letters + string .digits
104102 code = '' .join (random .choice (chars ) for i in range (8 ))
105103 try :
@@ -115,29 +113,31 @@ async def create_url(request):
115113 if existing :
116114 return add_message (request , 'error' , 'That code is already taken. Try another one.' , '/urlshortener' )
117115 await conn .execute ('INSERT INTO urls(user_id, code, url) VALUES ($1, $2, $3)' , account , code , url )
116+ secure = 's' if not request .app .config .DEV else ''
118117 return add_message (
119118 request ,
120119 'success' ,
121- f"Shortened URL created at <a href=\" http{ 's' if not request . app . config . DEV else '' } ://{ request .app .config .DOMAIN } /{ code } \" >"
120+ f"Shortened URL created at <a href=\" http{ secure } ://{ request .app .config .DOMAIN } /{ code } \" >"
122121 f"http{ 's' if not request .app .config .DEV else '' } ://{ request .app .config .DOMAIN } /{ code } </a>" ,
123122 '/urlshortener'
124123 )
125124
126125@root .get ('/<code>' )
127- async def existing_code (request , code ):
126+ async def existing_code (request : Request , code : str ):
128127 async with open_db_connection (request .app ) as conn :
129128 res = await conn .fetchrow ('SELECT * FROM urls WHERE code = $1' , code )
130129 if not res :
131- abort ( 404 , message = f'Requested URL { request .path } not found' )
130+ raise NotFound ( message = f'Requested URL { request .path } not found' )
132131 return response .redirect (res ['url' ])
133132
134133@root .get ('/pastebin' )
135- async def pastebin_home (request ):
136- return await render_template ('pastebin' , request , title = "Pastebin" , description = 'Paste in code for easy access later!' )
134+ async def pastebin_home (request : Request ):
135+ return await render_template ('pastebin' , request , title = "Pastebin" ,
136+ description = 'Paste some code for easy access later!' )
137137
138138@root .post ('/pastebin/create' )
139139# @authorized()
140- async def create_pastebin (request ):
140+ async def create_pastebin (request : Request ):
141141 chars = string .ascii_letters + string .digits
142142 code = '' .join (random .choice (chars ) for i in range (8 ))
143143 try :
@@ -150,126 +150,49 @@ async def create_pastebin(request):
150150 return response .redirect (f'/pastebin/{ code } ' )
151151
152152@root .get ('/pastebin/<code>' )
153- async def existing_pastebin (request , code ):
153+ async def existing_pastebin (request : Request , code : str ):
154154 async with open_db_connection (request .app ) as conn :
155155 res = await conn .fetchrow ('SELECT * FROM pastebin WHERE code = $1' , code )
156156 if not res :
157- abort ( 404 , message = f'Requested URL { request .path } not found' )
157+ raise NotFound ( message = f'Requested URL { request .path } not found' )
158158 text = disable_xss (res ['text' ])
159- return await render_template ('saved_pastebin' , request , title = "Pastebin - Saved" , description = "Saved Pastebin" , code = text )
160-
161- @root .get ('/challenges' )
162- async def challenge_home (request ):
163- return await render_template (
164- template = 'challenge_home' ,
165- request = request ,
166- title = 'Brawl Stars Challenges' ,
167- description = 'Search up your tag to view the logs of your Brawl Stars challenge games.'
168- )
169-
170- @root .post ('/challenges/post' )
171- async def challenge_post (request ):
172- try :
173- form_tag = request .form ['tag' ][0 ]
174- except KeyError :
175- return add_message (request , 'error' , 'Enter a player tag.' , '/challenges' )
176-
177- try :
178- tag = brawlstats .utils .bstag (form_tag )
179- except brawlstats .NotFoundError as e :
180- invalid_chars = e .message .split ('\n ' )
181- invalid_chars = invalid_chars [- 1 ]
182- return add_message (request , 'error' , invalid_chars , '/challenges' )
183- return response .redirect (f'/challenges/{ tag } ' )
184-
185- @root .get ('/challenges/<tag>' )
186- async def challenge_stats (request , tag ):
187- client = request .app .brawl_client
188- try :
189- logs = await client .get_battle_logs (tag )
190- except brawlstats .NotFoundError :
191- return add_message (request , 'error' , f'Tag { disable_xss (tag .upper ())} was not found.' , '/challenges' )
192-
193- event_map = {
194- 'gemGrab' : 'Gem Grab' ,
195- 'brawlBall' : 'Brawl Ball' ,
196- 'bounty' : 'Bounty' ,
197- 'heist' : 'Heist' ,
198- 'siege' : 'Siege'
199- }
200-
201- def filter_challenge_games (battle ):
202- try :
203- if battle .battle .trophy_change == 1 and 'Showdown' not in battle .event .mode :
204- return True
205- except :
206- valid_modes = ['gemGrab' , 'brawlBall' , 'bounty' , 'heist' , 'siege' ]
207- if battle .event .mode in valid_modes :
208- # Hacky way to filter out ranked matches
209- # still possible for a ranked match to be in the result but very unlikely
210- for team in battle .battle .teams :
211- for player in team :
212- if player .brawler .trophies % 100 > 3 or player .brawler .power < 10 :
213- return False
214- return True
215- return False
216-
217- games = list (filter (filter_challenge_games , logs ))[::- 1 ]
218-
219- if len (games ) == 0 :
220- return add_message (request , 'error' , 'No recent challenge games were found.' , '/challenges' )
221-
222- battlelog = []
223- for battle in games :
224- battle_info = {
225- 'event' : event_map [battle .event .mode ],
226- 'map' : battle .event .map ,
227- 'result' : battle .battle .result .title (),
228- 'teams' : battle .battle .teams .to_list ()
229- }
230- for i , team in enumerate (battle_info ['teams' ]):
231- for j , player in enumerate (team ):
232- if player ['tag' ] == battle .battle .star_player .tag :
233- battle_info ['teams' ][i ][j ]['star_player' ] = 'star'
234- else :
235- battle_info ['teams' ][i ][j ]['star_player' ] = 'normal'
236-
237- battlelog .append (battle_info )
238159 return await render_template (
239- template = 'challenge_stats ' ,
160+ template = 'saved_pastebin ' ,
240161 request = request ,
241- games = battlelog ,
242- brawler_key = {'EL PRIMO' : 'El-Primo' , 'MR. P' : 'Mr.P' },
243- title = 'Brawl Stars Challenges' ,
244- description = 'View the logs of your Brawl Stars challenge games.'
162+ title = "Pastebin - Saved" ,
163+ description = "Saved Pastebin" ,
164+ code = text
245165 )
246166
247167@root .get ('/brawlstats/<endpoint:path>' )
248- async def brawlstats_tests_proxy (request , endpoint ):
249- app = request .app
168+ async def brawlstats_tests_proxy (request : Request , endpoint : str ):
250169 endpoint = '/' .join (request .url .split ('/' )[4 :])
251170 if not request .token :
252- return response . text ('Invalid authorization' , status = 403 )
171+ raise Forbidden ('Invalid authorization' )
253172 headers = {
254173 'Authorization' : f'Bearer { request .token } ' ,
255174 'Accept-Encoding' : 'gzip'
256175 }
257176 try :
258- async with app .session .get (f'https://api.brawlstars.com/v1/{ endpoint } ' , timeout = 30 , headers = headers ) as resp :
177+ async with request .app .ctx .aiohttp .get (
178+ f'https://api.brawlstars.com/v1/{ endpoint } ' ,
179+ timeout = 30 ,
180+ headers = headers
181+ ) as resp :
259182 return response .json (await resp .json (), status = resp .status )
260183 except asyncio .TimeoutError :
261- return response . text ('Request failed' , status = 503 )
184+ raise ServerError ('Request failed' , status_code = 503 )
262185
263186@root .get ('/schoolweek' )
264- async def schoolweektoday (request ):
187+ async def schoolweektoday (request : Request ):
265188 return response .redirect (f'/schoolweek/{ date .today ()} ' )
266189
267190@root .get ('/schoolweek/<requested_date_str>' )
268- async def schoolweek (request , requested_date_str ):
191+ async def schoolweek (request : Request , requested_date_str : str ):
269192 requested_date = date (* map (int , requested_date_str .split ('-' )))
270193 first_day = date (2020 , 9 , 8 )
271194 if not first_day <= requested_date <= date (2021 , 3 , 11 ):
272- abort ( 404 , message = f'Requested URL { request .path } not found' )
195+ raise NotFound ( f'Requested URL { request .path } not found. Maybe try a date between 9/8/2020 and 3/11/2021? ' )
273196
274197 week_fmt = await get_school_week (requested_date , first_day , week = True )
275198
@@ -300,8 +223,9 @@ async def email_subscribe(request):
300223 msg ['Subject' ] = 'Thank you for subscribing to GCHS Daily Updates!'
301224 msg ['From' ] = request .app .config .CUSTOM_EMAIL
302225 msg ['To' ] = email
226+ secure = 's' if not request .app .config .DEV else ''
303227 body = MIMEText (
304- f"If this wasn't you, click <a href=\" http{ 's' if not request . app . config . DEV else '' } ://{ request .app .config .DOMAIN } "
228+ f"If this wasn't you, click <a href=\" http{ secure } ://{ request .app .config .DOMAIN } "
305229 f"/schoolweek/unsubscribe/{ email } \" >here</a> to unsubscribe." , 'html' )
306230 msg .set_content (body )
307231
@@ -320,3 +244,10 @@ async def email_unsubscribe(request, email):
320244 async with open_db_connection (request .app ) as conn :
321245 await conn .execute ('DELETE FROM mailing_list WHERE email = $1' , email )
322246 return add_message (request , 'success' , 'Your email has been removed from mailing list.' , '/schoolweek' )
247+
248+ @root .get ('/japanese-conjugation-practice' )
249+ async def jap_conj (request ):
250+ return await render_template (
251+ template = 'jap-conj' ,
252+ request = request
253+ )
0 commit comments