Loading .gitignore +0 −1 Changes for .gitignore: 0 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ youtube* playlist_creator.py artist_genre_tag.py uv.lock py.typed pyproject.toml mypy.ini .python-version No newline at end of file base.py +38 −31 Changes for base.py: 38 added lines, 31 removed lines. Original line number Diff line number Diff line import importlib import sys sys.path.insert(0, ".") import logging import asyncio Loading @@ -12,58 +13,57 @@ logger = logging.getLogger() logger.setLevel(logging.INFO) loop = asyncio.get_event_loop() app = FastAPI(title="codey.lol API", app = FastAPI( title="codey.lol API", version="1.0", contact={ 'name': 'codey' }, contact={"name": "codey"}, redirect_slashes=False, loop=loop) loop=loop, ) constants = importlib.import_module("constants").Constants() util = importlib.import_module("util").Utilities(app, constants) origins = [ "https://codey.lol", "https://api.codey.lol" ] origins = ["https://codey.lol", "https://api.codey.lol"] app.add_middleware(CORSMiddleware, # type: ignore app.add_middleware( CORSMiddleware, # type: ignore allow_origins=origins, allow_credentials=True, allow_methods=["POST", "GET", "HEAD"], allow_headers=["*"]) # type: ignore allow_headers=["*"], ) # type: ignore """ Blacklisted routes """ @app.get("/", include_in_schema=False) def disallow_get(): return util.get_blocked_response() @app.head("/", include_in_schema=False) def base_head(): return @app.get("/{path}", include_in_schema=False) def disallow_get_any(request: Request, var: Any = None): path = request.path_params['path'] if not ( isinstance(path, str) and path.split("/", maxsplit=1) == "widget" ): path = request.path_params["path"] if not (isinstance(path, str) and path.split("/", maxsplit=1) == "widget"): return util.get_blocked_response() else: logging.info("OK, %s", path) logging.info("OK, %s", path) @app.post("/", include_in_schema=False) def disallow_base_post(): return util.get_blocked_response() """ End Blacklisted Routes """ Loading @@ -73,20 +73,28 @@ Actionable Routes """ routes: dict = { 'randmsg': importlib.import_module("endpoints.rand_msg").RandMsg(app, util, constants), 'transcriptions': importlib.import_module("endpoints.transcriptions").Transcriptions(app, util, constants), 'lyrics': importlib.import_module("endpoints.lyric_search").LyricSearch(app, util, constants), 'lastfm': importlib.import_module("endpoints.lastfm").LastFM(app, util, constants), 'yt': importlib.import_module("endpoints.yt").YT(app, util, constants), 'karma': importlib.import_module("endpoints.karma").Karma(app, util, constants), 'radio': importlib.import_module("endpoints.radio").Radio(app, util, constants), 'mgr': importlib.import_module("endpoints.mgr.mgr_test").Mgr(app, util, constants), "randmsg": importlib.import_module("endpoints.rand_msg").RandMsg( app, util, constants ), "transcriptions": importlib.import_module( "endpoints.transcriptions" ).Transcriptions(app, util, constants), "lyrics": importlib.import_module("endpoints.lyric_search").LyricSearch( app, util, constants ), "lastfm": importlib.import_module("endpoints.lastfm").LastFM(app, util, constants), "yt": importlib.import_module("endpoints.yt").YT(app, util, constants), "karma": importlib.import_module("endpoints.karma").Karma(app, util, constants), "radio": importlib.import_module("endpoints.radio").Radio(app, util, constants), "mgr": importlib.import_module("endpoints.mgr.mgr_test").Mgr(app, util, constants), } # Misc endpoint depends on radio endpoint instance radio_endpoint = routes.get('radio') radio_endpoint = routes.get("radio") if radio_endpoint: routes['misc'] = importlib.import_module("endpoints.misc").Misc(app, util, constants, radio_endpoint) routes["misc"] = importlib.import_module("endpoints.misc").Misc( app, util, constants, radio_endpoint ) """ End Actionable Routes Loading @@ -98,5 +106,4 @@ Startup """ redis = redis_cache.RedisCache() loop.create_task( redis.create_index()) No newline at end of file loop.create_task(redis.create_index()) endpoints/constructors.py +41 −4 Changes for endpoints/constructors.py: 41 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ from pydantic import BaseModel Karma """ class ValidKarmaUpdateRequest(BaseModel): """ Requires authentication Loading @@ -25,19 +26,24 @@ class ValidKarmaRetrievalRequest(BaseModel): keyword: str class ValidTopKarmaRequest(BaseModel): """ - **n**: Number of top results to return (default: 10) """ n: Optional[int] = 10 """ LastFM """ class LastFMException(Exception): pass class ValidArtistSearchRequest(BaseModel): """ - **a**: artist name Loading @@ -50,10 +56,12 @@ class ValidArtistSearchRequest(BaseModel): "examples": [ { "a": "eminem", }] } ] } } class ValidAlbumDetailRequest(BaseModel): """ - **a**: artist name Loading @@ -69,9 +77,11 @@ class ValidAlbumDetailRequest(BaseModel): { "a": "eminem", "release": "houdini", }] } ] } } class ValidTrackInfoRequest(BaseModel): """ Loading @@ -88,14 +98,17 @@ class ValidTrackInfoRequest(BaseModel): { "a": "eminem", "t": "rap god", }] } ] } } """ Rand Msg """ class RandMsgRequest(BaseModel): """ - **short**: Short randmsg? Loading @@ -103,10 +116,12 @@ class RandMsgRequest(BaseModel): short: Optional[bool] = False """ YT """ class ValidYTSearchRequest(BaseModel): """ - **t**: title to search Loading @@ -114,10 +129,12 @@ class ValidYTSearchRequest(BaseModel): t: str = "rick astley - never gonna give you up" """ XC """ class ValidXCRequest(BaseModel): """ - **key**: valid XC API key Loading @@ -131,10 +148,12 @@ class ValidXCRequest(BaseModel): cmd: str data: Optional[dict] """ Transcriptions """ class ValidShowEpisodeListRequest(BaseModel): """ - **s**: show id Loading @@ -142,6 +161,7 @@ class ValidShowEpisodeListRequest(BaseModel): s: int class ValidShowEpisodeLineRequest(BaseModel): """ - **s**: show id Loading @@ -151,10 +171,12 @@ class ValidShowEpisodeLineRequest(BaseModel): s: int e: int """ Lyric Search """ class ValidLyricRequest(BaseModel): """ - **a**: artist Loading Loading @@ -186,7 +208,8 @@ class ValidLyricRequest(BaseModel): "extra": True, "lrc": False, "excluded_sources": [], }] } ] } } Loading @@ -195,15 +218,19 @@ class ValidTypeAheadRequest(BaseModel): """ - **query**: query string """ query: str """ Radio """ class RadioException(Exception): pass class ValidRadioSongRequest(BaseModel): """ - **key**: API Key Loading @@ -212,16 +239,19 @@ class ValidRadioSongRequest(BaseModel): - **artistsong**: may be used IN PLACE OF artist/song to perform a combined/string search in the format "artist - song" - **alsoSkip**: Whether to skip immediately to this track [not implemented] """ key: str artist: Optional[str] = None song: Optional[str] = None artistsong: Optional[str] = None alsoSkip: Optional[bool] = False class ValidRadioTypeaheadRequest(BaseModel): """ - **query**: Typeahead query """ query: str Loading @@ -234,33 +264,40 @@ class ValidRadioQueueGetRequest(BaseModel): key: Optional[str] = None limit: Optional[int] = 15_000 class ValidRadioNextRequest(BaseModel): """ - **key**: API Key - **skipTo**: UUID to skip to [optional] """ key: str skipTo: Optional[str] = None class ValidRadioReshuffleRequest(ValidRadioNextRequest): """ - **key**: API Key """ class ValidRadioQueueShiftRequest(BaseModel): """ - **key**: API Key - **uuid**: UUID to shift - **next**: Play next if true, immediately if false, default False """ key: str uuid: str next: Optional[bool] = False class ValidRadioQueueRemovalRequest(BaseModel): """ - **key**: API Key - **uuid**: UUID to remove """ key: str uuid: str endpoints/karma.py +120 −55 Changes for endpoints/karma.py: 120 added lines, 55 removed lines. Original line number Diff line number Diff line Loading @@ -7,14 +7,20 @@ import aiosqlite as sqlite3 from typing import LiteralString, Optional, Union from fastapi import FastAPI, Request, HTTPException from fastapi.responses import JSONResponse from .constructors import (ValidTopKarmaRequest, ValidKarmaRetrievalRequest, ValidKarmaUpdateRequest) from .constructors import ( ValidTopKarmaRequest, ValidKarmaRetrievalRequest, ValidKarmaUpdateRequest, ) class KarmaDB: """Karma DB Util""" def __init__(self) -> None: self.db_path: LiteralString = os.path.join("/", "usr", "local", "share", "sqlite_dbs", "karma.db") self.db_path: LiteralString = os.path.join( "/", "usr", "local", "share", "sqlite_dbs", "karma.db" ) async def get_karma(self, keyword: str) -> Union[int, dict]: """Get Karma Value for Keyword Loading @@ -24,14 +30,16 @@ class KarmaDB: Union[int, dict] """ async with sqlite3.connect(self.db_path, timeout=2) as db_conn: async with await db_conn.execute("SELECT score FROM karma WHERE keyword LIKE ? LIMIT 1", (keyword,)) as db_cursor: async with await db_conn.execute( "SELECT score FROM karma WHERE keyword LIKE ? LIMIT 1", (keyword,) ) as db_cursor: try: (score,) = await db_cursor.fetchone() return score except TypeError: return { 'err': True, 'errorText': f'No records for {keyword}', "err": True, "errorText": f"No records for {keyword}", } async def get_top(self, n: Optional[int] = 10) -> Optional[list[tuple]]: Loading @@ -44,14 +52,17 @@ class KarmaDB: """ try: async with sqlite3.connect(self.db_path, timeout=2) as db_conn: async with await db_conn.execute("SELECT keyword, score FROM karma ORDER BY score DESC LIMIT ?", (n,)) as db_cursor: async with await db_conn.execute( "SELECT keyword, score FROM karma ORDER BY score DESC LIMIT ?", (n,) ) as db_cursor: return await db_cursor.fetchall() except: traceback.print_exc() return None async def update_karma(self, granter: str, keyword: str, flag: int) -> Optional[bool]: async def update_karma( self, granter: str, keyword: str, flag: int ) -> Optional[bool]: """ Update Karma for Keyword Args: Loading @@ -66,26 +77,53 @@ class KarmaDB: return None modifier: str = "score + 1" if not flag else "score - 1" query: str = f"UPDATE karma SET score = {modifier}, last_change = ? WHERE keyword LIKE ?" new_keyword_query: str = "INSERT INTO karma(keyword, score, last_change) VALUES(?, ?, ?)" query: str = ( f"UPDATE karma SET score = {modifier}, last_change = ? WHERE keyword LIKE ?" ) new_keyword_query: str = ( "INSERT INTO karma(keyword, score, last_change) VALUES(?, ?, ?)" ) friendly_flag: str = "++" if not flag else "--" audit_message: str = f"{granter} adjusted karma for {keyword} @ {datetime.datetime.now().isoformat()}: {friendly_flag}" audit_query: str = "INSERT INTO karma_audit(impacted_keyword, comment) VALUES(?, ?)" audit_message: str = ( f"{granter} adjusted karma for {keyword} @ {datetime.datetime.now().isoformat()}: {friendly_flag}" ) audit_query: str = ( "INSERT INTO karma_audit(impacted_keyword, comment) VALUES(?, ?)" ) now: int = int(time.time()) logging.debug("Audit message: %s{audit_message}\nKeyword: %s{keyword}") async with sqlite3.connect(self.db_path, timeout=2) as db_conn: async with await db_conn.execute(audit_query, (keyword, audit_message,)) as db_cursor: async with await db_conn.execute( audit_query, ( keyword, audit_message, ), ) as db_cursor: await db_conn.commit() async with await db_conn.execute(query, (now, keyword,)) as db_cursor: async with await db_conn.execute( query, ( now, keyword, ), ) as db_cursor: if db_cursor.rowcount: await db_conn.commit() return True if db_cursor.rowcount < 1: # Keyword does not already exist await db_cursor.close() new_val = 1 if not flag else -1 async with await db_conn.execute(new_keyword_query, (keyword, new_val, now,)) as db_cursor: async with await db_conn.execute( new_keyword_query, ( keyword, new_val, now, ), ) as db_cursor: if db_cursor.rowcount >= 1: await db_conn.commit() return True Loading @@ -93,10 +131,12 @@ class KarmaDB: return False return False class Karma(FastAPI): """ Karma Endpoints """ def __init__(self, app: FastAPI, util, constants) -> None: self.app: FastAPI = app self.util = util Loading @@ -110,66 +150,83 @@ class Karma(FastAPI): } for endpoint, handler in self.endpoints.items(): app.add_api_route(f"/{endpoint}", handler, methods=["POST"], include_in_schema=True) app.add_api_route( f"/{endpoint}", handler, methods=["POST"], include_in_schema=True ) async def top_karma_handler(self, request: Request, data: Optional[ValidTopKarmaRequest] = None) -> JSONResponse: async def top_karma_handler( self, request: Request, data: Optional[ValidTopKarmaRequest] = None ) -> JSONResponse: """ Get top keywords for karma - **n**: Number of top results to return (default: 10) """ if not self.util.check_key(request.url.path, request.headers.get('X-Authd-With')): if not self.util.check_key( request.url.path, request.headers.get("X-Authd-With") ): raise HTTPException(status_code=403, detail="Unauthorized") n: int = 10 if data and data.n: n = int(data.n) try: top10: Optional[list[tuple]] = await self.db.get_top(n=n) if not top10: return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'General failure', }) return JSONResponse( status_code=500, content={ "err": True, "errorText": "General failure", }, ) return JSONResponse(content=top10) except: traceback.print_exc() return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'Exception occurred.', }) return JSONResponse( status_code=500, content={ "err": True, "errorText": "Exception occurred.", }, ) async def get_karma_handler(self, data: ValidKarmaRetrievalRequest, request: Request) -> JSONResponse: async def get_karma_handler( self, data: ValidKarmaRetrievalRequest, request: Request ) -> JSONResponse: """ Get current karma value - **keyword**: Keyword to retrieve karma value for """ if not self.util.check_key(request.url.path, request.headers.get('X-Authd-With')): if not self.util.check_key( request.url.path, request.headers.get("X-Authd-With") ): raise HTTPException(status_code=403, detail="Unauthorized") keyword: str = data.keyword try: count: Union[int, dict] = await self.db.get_karma(keyword) return JSONResponse(content={ 'keyword': keyword, 'count': count, }) return JSONResponse( content={ "keyword": keyword, "count": count, } ) except: traceback.print_exc() return JSONResponse(status_code=500, content={ 'err': True, 'errorText': "Exception occurred.", }) return JSONResponse( status_code=500, content={ "err": True, "errorText": "Exception occurred.", }, ) async def modify_karma_handler(self, data: ValidKarmaUpdateRequest, request: Request) -> JSONResponse: async def modify_karma_handler( self, data: ValidKarmaUpdateRequest, request: Request ) -> JSONResponse: """ Update karma count - **granter**: User who granted the karma Loading @@ -177,16 +234,24 @@ class Karma(FastAPI): - **flag**: 0 to decrement (--), 1 to increment (++) """ if not self.util.check_key(request.url.path, request.headers.get('X-Authd-With'), 2): if not self.util.check_key( request.url.path, request.headers.get("X-Authd-With"), 2 ): raise HTTPException(status_code=403, detail="Unauthorized") if not data.flag in [0, 1]: return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'Invalid request', }) return JSONResponse(content={ 'success': await self.db.update_karma(data.granter, data.keyword, data.flag) }) No newline at end of file return JSONResponse( status_code=500, content={ "err": True, "errorText": "Invalid request", }, ) return JSONResponse( content={ "success": await self.db.update_karma( data.granter, data.keyword, data.flag ) } ) endpoints/lastfm.py +136 −89 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
.gitignore +0 −1 Changes for .gitignore: 0 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ youtube* playlist_creator.py artist_genre_tag.py uv.lock py.typed pyproject.toml mypy.ini .python-version No newline at end of file
base.py +38 −31 Changes for base.py: 38 added lines, 31 removed lines. Original line number Diff line number Diff line import importlib import sys sys.path.insert(0, ".") import logging import asyncio Loading @@ -12,58 +13,57 @@ logger = logging.getLogger() logger.setLevel(logging.INFO) loop = asyncio.get_event_loop() app = FastAPI(title="codey.lol API", app = FastAPI( title="codey.lol API", version="1.0", contact={ 'name': 'codey' }, contact={"name": "codey"}, redirect_slashes=False, loop=loop) loop=loop, ) constants = importlib.import_module("constants").Constants() util = importlib.import_module("util").Utilities(app, constants) origins = [ "https://codey.lol", "https://api.codey.lol" ] origins = ["https://codey.lol", "https://api.codey.lol"] app.add_middleware(CORSMiddleware, # type: ignore app.add_middleware( CORSMiddleware, # type: ignore allow_origins=origins, allow_credentials=True, allow_methods=["POST", "GET", "HEAD"], allow_headers=["*"]) # type: ignore allow_headers=["*"], ) # type: ignore """ Blacklisted routes """ @app.get("/", include_in_schema=False) def disallow_get(): return util.get_blocked_response() @app.head("/", include_in_schema=False) def base_head(): return @app.get("/{path}", include_in_schema=False) def disallow_get_any(request: Request, var: Any = None): path = request.path_params['path'] if not ( isinstance(path, str) and path.split("/", maxsplit=1) == "widget" ): path = request.path_params["path"] if not (isinstance(path, str) and path.split("/", maxsplit=1) == "widget"): return util.get_blocked_response() else: logging.info("OK, %s", path) logging.info("OK, %s", path) @app.post("/", include_in_schema=False) def disallow_base_post(): return util.get_blocked_response() """ End Blacklisted Routes """ Loading @@ -73,20 +73,28 @@ Actionable Routes """ routes: dict = { 'randmsg': importlib.import_module("endpoints.rand_msg").RandMsg(app, util, constants), 'transcriptions': importlib.import_module("endpoints.transcriptions").Transcriptions(app, util, constants), 'lyrics': importlib.import_module("endpoints.lyric_search").LyricSearch(app, util, constants), 'lastfm': importlib.import_module("endpoints.lastfm").LastFM(app, util, constants), 'yt': importlib.import_module("endpoints.yt").YT(app, util, constants), 'karma': importlib.import_module("endpoints.karma").Karma(app, util, constants), 'radio': importlib.import_module("endpoints.radio").Radio(app, util, constants), 'mgr': importlib.import_module("endpoints.mgr.mgr_test").Mgr(app, util, constants), "randmsg": importlib.import_module("endpoints.rand_msg").RandMsg( app, util, constants ), "transcriptions": importlib.import_module( "endpoints.transcriptions" ).Transcriptions(app, util, constants), "lyrics": importlib.import_module("endpoints.lyric_search").LyricSearch( app, util, constants ), "lastfm": importlib.import_module("endpoints.lastfm").LastFM(app, util, constants), "yt": importlib.import_module("endpoints.yt").YT(app, util, constants), "karma": importlib.import_module("endpoints.karma").Karma(app, util, constants), "radio": importlib.import_module("endpoints.radio").Radio(app, util, constants), "mgr": importlib.import_module("endpoints.mgr.mgr_test").Mgr(app, util, constants), } # Misc endpoint depends on radio endpoint instance radio_endpoint = routes.get('radio') radio_endpoint = routes.get("radio") if radio_endpoint: routes['misc'] = importlib.import_module("endpoints.misc").Misc(app, util, constants, radio_endpoint) routes["misc"] = importlib.import_module("endpoints.misc").Misc( app, util, constants, radio_endpoint ) """ End Actionable Routes Loading @@ -98,5 +106,4 @@ Startup """ redis = redis_cache.RedisCache() loop.create_task( redis.create_index()) No newline at end of file loop.create_task(redis.create_index())
endpoints/constructors.py +41 −4 Changes for endpoints/constructors.py: 41 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ from pydantic import BaseModel Karma """ class ValidKarmaUpdateRequest(BaseModel): """ Requires authentication Loading @@ -25,19 +26,24 @@ class ValidKarmaRetrievalRequest(BaseModel): keyword: str class ValidTopKarmaRequest(BaseModel): """ - **n**: Number of top results to return (default: 10) """ n: Optional[int] = 10 """ LastFM """ class LastFMException(Exception): pass class ValidArtistSearchRequest(BaseModel): """ - **a**: artist name Loading @@ -50,10 +56,12 @@ class ValidArtistSearchRequest(BaseModel): "examples": [ { "a": "eminem", }] } ] } } class ValidAlbumDetailRequest(BaseModel): """ - **a**: artist name Loading @@ -69,9 +77,11 @@ class ValidAlbumDetailRequest(BaseModel): { "a": "eminem", "release": "houdini", }] } ] } } class ValidTrackInfoRequest(BaseModel): """ Loading @@ -88,14 +98,17 @@ class ValidTrackInfoRequest(BaseModel): { "a": "eminem", "t": "rap god", }] } ] } } """ Rand Msg """ class RandMsgRequest(BaseModel): """ - **short**: Short randmsg? Loading @@ -103,10 +116,12 @@ class RandMsgRequest(BaseModel): short: Optional[bool] = False """ YT """ class ValidYTSearchRequest(BaseModel): """ - **t**: title to search Loading @@ -114,10 +129,12 @@ class ValidYTSearchRequest(BaseModel): t: str = "rick astley - never gonna give you up" """ XC """ class ValidXCRequest(BaseModel): """ - **key**: valid XC API key Loading @@ -131,10 +148,12 @@ class ValidXCRequest(BaseModel): cmd: str data: Optional[dict] """ Transcriptions """ class ValidShowEpisodeListRequest(BaseModel): """ - **s**: show id Loading @@ -142,6 +161,7 @@ class ValidShowEpisodeListRequest(BaseModel): s: int class ValidShowEpisodeLineRequest(BaseModel): """ - **s**: show id Loading @@ -151,10 +171,12 @@ class ValidShowEpisodeLineRequest(BaseModel): s: int e: int """ Lyric Search """ class ValidLyricRequest(BaseModel): """ - **a**: artist Loading Loading @@ -186,7 +208,8 @@ class ValidLyricRequest(BaseModel): "extra": True, "lrc": False, "excluded_sources": [], }] } ] } } Loading @@ -195,15 +218,19 @@ class ValidTypeAheadRequest(BaseModel): """ - **query**: query string """ query: str """ Radio """ class RadioException(Exception): pass class ValidRadioSongRequest(BaseModel): """ - **key**: API Key Loading @@ -212,16 +239,19 @@ class ValidRadioSongRequest(BaseModel): - **artistsong**: may be used IN PLACE OF artist/song to perform a combined/string search in the format "artist - song" - **alsoSkip**: Whether to skip immediately to this track [not implemented] """ key: str artist: Optional[str] = None song: Optional[str] = None artistsong: Optional[str] = None alsoSkip: Optional[bool] = False class ValidRadioTypeaheadRequest(BaseModel): """ - **query**: Typeahead query """ query: str Loading @@ -234,33 +264,40 @@ class ValidRadioQueueGetRequest(BaseModel): key: Optional[str] = None limit: Optional[int] = 15_000 class ValidRadioNextRequest(BaseModel): """ - **key**: API Key - **skipTo**: UUID to skip to [optional] """ key: str skipTo: Optional[str] = None class ValidRadioReshuffleRequest(ValidRadioNextRequest): """ - **key**: API Key """ class ValidRadioQueueShiftRequest(BaseModel): """ - **key**: API Key - **uuid**: UUID to shift - **next**: Play next if true, immediately if false, default False """ key: str uuid: str next: Optional[bool] = False class ValidRadioQueueRemovalRequest(BaseModel): """ - **key**: API Key - **uuid**: UUID to remove """ key: str uuid: str
endpoints/karma.py +120 −55 Changes for endpoints/karma.py: 120 added lines, 55 removed lines. Original line number Diff line number Diff line Loading @@ -7,14 +7,20 @@ import aiosqlite as sqlite3 from typing import LiteralString, Optional, Union from fastapi import FastAPI, Request, HTTPException from fastapi.responses import JSONResponse from .constructors import (ValidTopKarmaRequest, ValidKarmaRetrievalRequest, ValidKarmaUpdateRequest) from .constructors import ( ValidTopKarmaRequest, ValidKarmaRetrievalRequest, ValidKarmaUpdateRequest, ) class KarmaDB: """Karma DB Util""" def __init__(self) -> None: self.db_path: LiteralString = os.path.join("/", "usr", "local", "share", "sqlite_dbs", "karma.db") self.db_path: LiteralString = os.path.join( "/", "usr", "local", "share", "sqlite_dbs", "karma.db" ) async def get_karma(self, keyword: str) -> Union[int, dict]: """Get Karma Value for Keyword Loading @@ -24,14 +30,16 @@ class KarmaDB: Union[int, dict] """ async with sqlite3.connect(self.db_path, timeout=2) as db_conn: async with await db_conn.execute("SELECT score FROM karma WHERE keyword LIKE ? LIMIT 1", (keyword,)) as db_cursor: async with await db_conn.execute( "SELECT score FROM karma WHERE keyword LIKE ? LIMIT 1", (keyword,) ) as db_cursor: try: (score,) = await db_cursor.fetchone() return score except TypeError: return { 'err': True, 'errorText': f'No records for {keyword}', "err": True, "errorText": f"No records for {keyword}", } async def get_top(self, n: Optional[int] = 10) -> Optional[list[tuple]]: Loading @@ -44,14 +52,17 @@ class KarmaDB: """ try: async with sqlite3.connect(self.db_path, timeout=2) as db_conn: async with await db_conn.execute("SELECT keyword, score FROM karma ORDER BY score DESC LIMIT ?", (n,)) as db_cursor: async with await db_conn.execute( "SELECT keyword, score FROM karma ORDER BY score DESC LIMIT ?", (n,) ) as db_cursor: return await db_cursor.fetchall() except: traceback.print_exc() return None async def update_karma(self, granter: str, keyword: str, flag: int) -> Optional[bool]: async def update_karma( self, granter: str, keyword: str, flag: int ) -> Optional[bool]: """ Update Karma for Keyword Args: Loading @@ -66,26 +77,53 @@ class KarmaDB: return None modifier: str = "score + 1" if not flag else "score - 1" query: str = f"UPDATE karma SET score = {modifier}, last_change = ? WHERE keyword LIKE ?" new_keyword_query: str = "INSERT INTO karma(keyword, score, last_change) VALUES(?, ?, ?)" query: str = ( f"UPDATE karma SET score = {modifier}, last_change = ? WHERE keyword LIKE ?" ) new_keyword_query: str = ( "INSERT INTO karma(keyword, score, last_change) VALUES(?, ?, ?)" ) friendly_flag: str = "++" if not flag else "--" audit_message: str = f"{granter} adjusted karma for {keyword} @ {datetime.datetime.now().isoformat()}: {friendly_flag}" audit_query: str = "INSERT INTO karma_audit(impacted_keyword, comment) VALUES(?, ?)" audit_message: str = ( f"{granter} adjusted karma for {keyword} @ {datetime.datetime.now().isoformat()}: {friendly_flag}" ) audit_query: str = ( "INSERT INTO karma_audit(impacted_keyword, comment) VALUES(?, ?)" ) now: int = int(time.time()) logging.debug("Audit message: %s{audit_message}\nKeyword: %s{keyword}") async with sqlite3.connect(self.db_path, timeout=2) as db_conn: async with await db_conn.execute(audit_query, (keyword, audit_message,)) as db_cursor: async with await db_conn.execute( audit_query, ( keyword, audit_message, ), ) as db_cursor: await db_conn.commit() async with await db_conn.execute(query, (now, keyword,)) as db_cursor: async with await db_conn.execute( query, ( now, keyword, ), ) as db_cursor: if db_cursor.rowcount: await db_conn.commit() return True if db_cursor.rowcount < 1: # Keyword does not already exist await db_cursor.close() new_val = 1 if not flag else -1 async with await db_conn.execute(new_keyword_query, (keyword, new_val, now,)) as db_cursor: async with await db_conn.execute( new_keyword_query, ( keyword, new_val, now, ), ) as db_cursor: if db_cursor.rowcount >= 1: await db_conn.commit() return True Loading @@ -93,10 +131,12 @@ class KarmaDB: return False return False class Karma(FastAPI): """ Karma Endpoints """ def __init__(self, app: FastAPI, util, constants) -> None: self.app: FastAPI = app self.util = util Loading @@ -110,66 +150,83 @@ class Karma(FastAPI): } for endpoint, handler in self.endpoints.items(): app.add_api_route(f"/{endpoint}", handler, methods=["POST"], include_in_schema=True) app.add_api_route( f"/{endpoint}", handler, methods=["POST"], include_in_schema=True ) async def top_karma_handler(self, request: Request, data: Optional[ValidTopKarmaRequest] = None) -> JSONResponse: async def top_karma_handler( self, request: Request, data: Optional[ValidTopKarmaRequest] = None ) -> JSONResponse: """ Get top keywords for karma - **n**: Number of top results to return (default: 10) """ if not self.util.check_key(request.url.path, request.headers.get('X-Authd-With')): if not self.util.check_key( request.url.path, request.headers.get("X-Authd-With") ): raise HTTPException(status_code=403, detail="Unauthorized") n: int = 10 if data and data.n: n = int(data.n) try: top10: Optional[list[tuple]] = await self.db.get_top(n=n) if not top10: return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'General failure', }) return JSONResponse( status_code=500, content={ "err": True, "errorText": "General failure", }, ) return JSONResponse(content=top10) except: traceback.print_exc() return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'Exception occurred.', }) return JSONResponse( status_code=500, content={ "err": True, "errorText": "Exception occurred.", }, ) async def get_karma_handler(self, data: ValidKarmaRetrievalRequest, request: Request) -> JSONResponse: async def get_karma_handler( self, data: ValidKarmaRetrievalRequest, request: Request ) -> JSONResponse: """ Get current karma value - **keyword**: Keyword to retrieve karma value for """ if not self.util.check_key(request.url.path, request.headers.get('X-Authd-With')): if not self.util.check_key( request.url.path, request.headers.get("X-Authd-With") ): raise HTTPException(status_code=403, detail="Unauthorized") keyword: str = data.keyword try: count: Union[int, dict] = await self.db.get_karma(keyword) return JSONResponse(content={ 'keyword': keyword, 'count': count, }) return JSONResponse( content={ "keyword": keyword, "count": count, } ) except: traceback.print_exc() return JSONResponse(status_code=500, content={ 'err': True, 'errorText': "Exception occurred.", }) return JSONResponse( status_code=500, content={ "err": True, "errorText": "Exception occurred.", }, ) async def modify_karma_handler(self, data: ValidKarmaUpdateRequest, request: Request) -> JSONResponse: async def modify_karma_handler( self, data: ValidKarmaUpdateRequest, request: Request ) -> JSONResponse: """ Update karma count - **granter**: User who granted the karma Loading @@ -177,16 +234,24 @@ class Karma(FastAPI): - **flag**: 0 to decrement (--), 1 to increment (++) """ if not self.util.check_key(request.url.path, request.headers.get('X-Authd-With'), 2): if not self.util.check_key( request.url.path, request.headers.get("X-Authd-With"), 2 ): raise HTTPException(status_code=403, detail="Unauthorized") if not data.flag in [0, 1]: return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'Invalid request', }) return JSONResponse(content={ 'success': await self.db.update_karma(data.granter, data.keyword, data.flag) }) No newline at end of file return JSONResponse( status_code=500, content={ "err": True, "errorText": "Invalid request", }, ) return JSONResponse( content={ "success": await self.db.update_karma( data.granter, data.keyword, data.flag ) } )
endpoints/lastfm.py +136 −89 File changed.Preview size limit exceeded, changes collapsed. Show changes