diff --git a/poetry.lock b/poetry.lock index 20cea5aa..76993f55 100644 --- a/poetry.lock +++ b/poetry.lock @@ -145,6 +145,14 @@ pytz = "*" regex = "*" tzlocal = "*" +[[package]] +category = "main" +description = "Decorators for Humans" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.1" + [[package]] category = "main" description = "A library to handle automated deprecations" @@ -178,7 +186,7 @@ description = "Python audio data toolkit (ID3 and MP3)" name = "eyed3" optional = false python-versions = "*" -version = "0.9" +version = "0.9.2" [package.dependencies] deprecation = "*" @@ -271,10 +279,10 @@ description = "HTTP/2 State-Machine based protocol implementation" name = "h2" optional = false python-versions = "*" -version = "3.1.1" +version = "3.2.0" [package.dependencies] -hpack = ">=2.3,<4" +hpack = ">=3.0,<4" hyperframe = ">=5.2.0,<6" [[package]] @@ -300,7 +308,7 @@ description = "Human friendly output for text interfaces using Python" name = "humanfriendly" optional = false python-versions = "*" -version = "5.0" +version = "6.1" [package.dependencies] pyreadline = "*" @@ -511,11 +519,12 @@ description = "We have made you a wrapper you can't refuse" name = "python-telegram-bot" optional = false python-versions = "*" -version = "12.3.0" +version = "12.4.2" [package.dependencies] certifi = "*" cryptography = "*" +decorator = ">=4.4.0" future = ">=0.16.0" tornado = ">=5.1" @@ -577,7 +586,7 @@ description = "A multipurpose bot and web framework" name = "royalnet" optional = false python-versions = ">=3.8,<4.0" -version = "5.5a3" +version = "5.5a5" [package.dependencies] dateparser = ">=0.7.2,<0.8.0" @@ -858,7 +867,7 @@ python-versions = "*" version = "2020.1.24" [metadata] -content-hash = "7cb56343d564bb2209fbc1624f64ab008088537a3b23aeb3f93497ff0cebca63" +content-hash = "03b83f374f0113316752e6df25d265ec5d45a2f932f73de1422c92ba06621f66" python-versions = "^3.8" [metadata.files] @@ -981,6 +990,10 @@ dateparser = [ {file = "dateparser-0.7.2-py2.py3-none-any.whl", hash = "sha256:983d84b5e3861cb0aa240cad07f12899bb10b62328aae188b9007e04ce37d665"}, {file = "dateparser-0.7.2.tar.gz", hash = "sha256:e1eac8ef28de69a554d5fcdb60b172d526d61924b1a40afbbb08df459a36006b"}, ] +decorator = [ + {file = "decorator-4.4.1-py2.py3-none-any.whl", hash = "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"}, + {file = "decorator-4.4.1.tar.gz", hash = "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce"}, +] deprecation = [ {file = "deprecation-2.0.7-py2.py3-none-any.whl", hash = "sha256:dc9b4f252b7aca8165ce2764a71da92a653b5ffbf7a389461d7a640f6536ecb2"}, {file = "deprecation-2.0.7.tar.gz", hash = "sha256:c0392f676a6146f0238db5744d73e786a43510d54033f80994ef2f4c9df192ed"}, @@ -989,9 +1002,9 @@ deprecation = [ {file = "discord.py-1.3.1-py3-none-any.whl", hash = "sha256:8bfe5628d31771744000f19135c386c74ac337479d7282c26cc1627b9d31f360"}, ] eyed3 = [ - {file = "eyeD3-0.9-py2.py3-none-any.whl", hash = "sha256:6015669333df2115809102ccf1b29585115b5c233cf4530d9f995ad562634819"}, - {file = "eyeD3-0.9-py3.8.egg", hash = "sha256:8e3a7a2ce2932260f77c6234d624737807cdb4404f5bac3c5348c1f9da3d7250"}, - {file = "eyeD3-0.9.tar.gz", hash = "sha256:8874762fd4fd93fa64676185ccaa77ea8b3396aea65ba86bca7325f1136f9c8a"}, + {file = "eyeD3-0.9.2-py2.py3-none-any.whl", hash = "sha256:14d387e74097b03163c15d071448924d6e9b700bf8b51276a2d7af4ea21cbe0a"}, + {file = "eyeD3-0.9.2-py3.8.egg", hash = "sha256:d6562a4a260f5085ee2c469a1904f358f6358e830ad84118032778befec8552e"}, + {file = "eyeD3-0.9.2.tar.gz", hash = "sha256:96f1dc92d29da529bf5a0caac6b62a3da2dae319409678491eb7f3e3e1c0359a"}, ] ffmpeg-python = [ {file = "ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127"}, @@ -1059,8 +1072,8 @@ h11 = [ {file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"}, ] h2 = [ - {file = "h2-3.1.1-py2.py3-none-any.whl", hash = "sha256:ac377fcf586314ef3177bfd90c12c7826ab0840edeb03f0f24f511858326049e"}, - {file = "h2-3.1.1.tar.gz", hash = "sha256:b8a32bd282594424c0ac55845377eea13fa54fe4a8db012f3a198ed923dc3ab4"}, + {file = "h2-3.2.0-py2.py3-none-any.whl", hash = "sha256:61e0f6601fa709f35cdb730863b4e5ec7ad449792add80d1410d4174ed139af5"}, + {file = "h2-3.2.0.tar.gz", hash = "sha256:875f41ebd6f2c44781259005b157faed1a5031df3ae5aa7bcb4628a6c0782f14"}, ] hpack = [ {file = "hpack-3.0.0-py2.py3-none-any.whl", hash = "sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89"}, @@ -1070,8 +1083,8 @@ httptools = [ {file = "httptools-0.0.13.tar.gz", hash = "sha256:e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc"}, ] humanfriendly = [ - {file = "humanfriendly-5.0-py2.py3-none-any.whl", hash = "sha256:eee2e57dea6185fee19914c7d8ba8bc3e488aee2a3c96a7668ce355e2ae275ae"}, - {file = "humanfriendly-5.0.tar.gz", hash = "sha256:b166d90b0fd2e7deafc5d20f64ddfdd4251189a1a6bdc5ed32c6678c79b546d5"}, + {file = "humanfriendly-6.1-py2.py3-none-any.whl", hash = "sha256:5a57c973dd28a24f45ab723521c84b111fbe79e9d9fcdca6f9aeb668c18a0f40"}, + {file = "humanfriendly-6.1.tar.gz", hash = "sha256:b77d1aa8d73b6fe7e8860fa516fbc0e2aa85bff159d7525b0213353817cf1cfc"}, ] hyperframe = [ {file = "hyperframe-5.2.0-py2.py3-none-any.whl", hash = "sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40"}, @@ -1255,8 +1268,8 @@ python-multipart = [ {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, ] python-telegram-bot = [ - {file = "python-telegram-bot-12.3.0.tar.gz", hash = "sha256:41608512a3025a04ff7472efcaae344ad7533a77ae207685bb10fc2fc8282f7b"}, - {file = "python_telegram_bot-12.3.0-py2.py3-none-any.whl", hash = "sha256:5e2156f829402e41bb5ea7196e450bf7f121c5689c5100ae180507d72f3777f5"}, + {file = "python-telegram-bot-12.4.2.tar.gz", hash = "sha256:0a97cbca638f949582b4ee326170d2f8d7f4bf559a4e511132bb2203623e04ad"}, + {file = "python_telegram_bot-12.4.2-py2.py3-none-any.whl", hash = "sha256:d3cffd020af4094d07c11783f875e5c682072ba7f5bc21ce89ff0222f4e6d742"}, ] pytz = [ {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, @@ -1294,8 +1307,8 @@ riotwatcher = [ {file = "riotwatcher-2.7.1.tar.gz", hash = "sha256:5349271c7e00637b7619491a6070e66603705db60558ea2a690e7016f6e6d9a4"}, ] royalnet = [ - {file = "royalnet-5.5a3-py3-none-any.whl", hash = "sha256:26bb171a55913a42f06a06759df32cccf9edc18b41c9edfeb6022b230f4f85fb"}, - {file = "royalnet-5.5a3.tar.gz", hash = "sha256:8c362af1fde43fd67420ef6dc905527044cf0f3e8fc40d298c2a63afd52c92fe"}, + {file = "royalnet-5.5a5-py3-none-any.whl", hash = "sha256:f913ec05849c56885bc2a455c6589d960e5af124aacc703bbd3a0e1f004d26b8"}, + {file = "royalnet-5.5a5.tar.gz", hash = "sha256:cbb22f322f6bc8ec0a6404a14d3faf046ad3fb74971917e4f87c16bb2aa300f7"}, ] royalspells = [ {file = "royalspells-3.2.tar.gz", hash = "sha256:2bd4a9a66514532e35c02c3907425af48c7cb292364c4843c795719a82b25dfe"}, diff --git a/pyproject.toml b/pyproject.toml index 1fe6df6a..9f328803 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ steam = "^0.9.1" [tool.poetry.dependencies.royalnet] - version = "^5.5a3" + version = "^5.5a6" # Maybe... there is a way to make these selectable? extras = [ "telegram", diff --git a/royalpack/pycharm/test_apis.http b/royalpack/pycharm/test_apis.http new file mode 100644 index 00000000..e51c3cb9 --- /dev/null +++ b/royalpack/pycharm/test_apis.http @@ -0,0 +1,70 @@ +POST http://localhost:44445/api/login/royalnet/v1 +Content-Type: application/json + +{ + "username": "Steffo", + "password": "ciao" +} + +### + +POST http://localhost:44445/api/token/create/v1 +Content-Type: application/json + +{ + "token": "NFFU4qg6-WxfAWMN-IW6dexEjNcLzNQNZJko2_pbTsE", + "duration": 31536000 +} + +### + +GET http://localhost:44445/api/bio/get/v1 +Content-Type: application/json + +{ + "token": "NFFU4qg6-WxfAWMN-IW6dexEjNcLzNQNZJko2_pbTsE", + "id": 1 +} + +### + +POST http://localhost:44445/api/bio/set/v1 +Content-Type: application/json + +{ + "token": "NFFU4qg6-WxfAWMN-IW6dexEjNcLzNQNZJko2_pbTsE", + "contents": "Ciao!" +} + +### + +GET http://localhost:44445/api/wiki/list/v1 + +### + +POST http://localhost:44445/api/wiki/edit/v1 +Content-Type: application/json + +{ + "token": "NFFU4qg6-WxfAWMN-IW6dexEjNcLzNQNZJko2_pbTsE", + "title": "Prova!", + "contents": "Questa è una pagina wiki di prova.", + "format": "text", + "theme": "default" +} + +### + +POST http://localhost:44445/api/wiki/edit/v1 +Content-Type: application/json + +{ + "id": "80d54849-fab1-4458-9d89-2429773118ef", + "token": "NFFU4qg6-WxfAWMN-IW6dexEjNcLzNQNZJko2_pbTsE", + "title": "Prova2", + "contents": "Questa è una pagina wiki di prova2.", + "format": "text", + "theme": "default" +} + +### diff --git a/royalpack/stars/__init__.py b/royalpack/stars/__init__.py index 20c08bbd..0798d650 100644 --- a/royalpack/stars/__init__.py +++ b/royalpack/stars/__init__.py @@ -1,21 +1,27 @@ # Imports go here! +from .api_bio_get import ApiBioGetStar +from .api_bio_set import ApiBioSetStar from .api_diario_get import ApiDiarioGetStar from .api_diario_list import ApiDiarioListStar from .api_user_list import ApiUserListStar from .api_user_get import ApiUserGetStar from .api_discord_cv import ApiDiscordCvStar from .api_discord_play import ApiDiscordPlayStar +from .api_wiki_edit import ApiWikiEditStar from .api_wiki_get import ApiWikiGetStar from .api_wiki_list import ApiWikiListStar # Enter the PageStars of your Pack here! available_page_stars = [ + ApiBioGetStar, + ApiBioSetStar, ApiDiarioGetStar, ApiDiarioListStar, ApiDiscordCvStar, ApiDiscordPlayStar, ApiUserGetStar, ApiUserListStar, + ApiWikiEditStar, ApiWikiGetStar, ApiWikiListStar, ] diff --git a/royalpack/stars/api_bio_get.py b/royalpack/stars/api_bio_get.py new file mode 100644 index 00000000..56d2deee --- /dev/null +++ b/royalpack/stars/api_bio_get.py @@ -0,0 +1,14 @@ +from royalnet.utils import * +from royalnet.backpack.tables import * +from royalnet.constellation.api import * +from ..utils import find_user_api + + +class ApiBioGetStar(ApiStar): + path = "/api/bio/get/v1" + + async def api(self, data: ApiData) -> dict: + user = await find_user_api(data["id"], self.alchemy, data.session) + if user.bio is None: + raise NotFoundError("User has no bio set.") + return user.bio.json() diff --git a/royalpack/stars/api_bio_set.py b/royalpack/stars/api_bio_set.py new file mode 100644 index 00000000..5018eb9a --- /dev/null +++ b/royalpack/stars/api_bio_set.py @@ -0,0 +1,23 @@ +import royalnet.utils as ru +from royalnet.backpack.tables import * +from royalnet.constellation.api import * +from ..tables import Bio + + +class ApiBioSetStar(ApiStar): + path = "/api/bio/set/v1" + + methods = ["POST"] + + async def api(self, data: ApiData) -> ru.JSON: + contents = data["contents"] + BioT = self.alchemy.get(Bio) + user = await data.user() + bio = user.bio + if bio is None: + bio = BioT(user=user, contents=contents) + data.session.add(bio) + else: + bio.contents = contents + await data.session_commit() + return bio.json() diff --git a/royalpack/stars/api_wiki_edit.py b/royalpack/stars/api_wiki_edit.py new file mode 100644 index 00000000..74f20805 --- /dev/null +++ b/royalpack/stars/api_wiki_edit.py @@ -0,0 +1,50 @@ +import uuid +import royalnet.utils as ru +from royalnet.backpack.tables import * +from royalnet.constellation.api import * +from ..tables import WikiPage + + +class ApiWikiEditStar(ApiStar): + path = "/api/wiki/edit/v1" + + methods = ["POST"] + + async def api(self, data: ApiData) -> ru.JSON: + page_id = data.get("id") + title = data["title"] + contents = data["contents"] + format = data["format"] + theme = data["theme"] + + WikiPageT = self.alchemy.get(WikiPage) + + user = await data.user() + if not (user.role == "Admin" or user.role == "Member" or user.role == "Bot"): + raise ForbiddenError("You do not have sufficient permissions to edit this page.") + + if page_id is None: + page = WikiPageT( + page_id=uuid.uuid4(), + title=title, + contents=contents, + format=format, + theme=theme + ) + data.session.add(page) + else: + page = await ru.asyncify( + data.session + .query(WikiPageT) + .filter_by(page_id=uuid.UUID(page_id)) + .one_or_none + ) + if page is None: + raise NotFoundError(f"No page with the id {repr(page_id)} found.") + page.title = title + page.contents = contents + page.format = format + page.theme = theme + + await data.session_commit() + return page.json_full() diff --git a/royalpack/stars/api_wiki_list.py b/royalpack/stars/api_wiki_list.py index ca378635..21a02a71 100644 --- a/royalpack/stars/api_wiki_list.py +++ b/royalpack/stars/api_wiki_list.py @@ -7,7 +7,7 @@ from ..tables import * from royalnet.constellation.api import * -class ApiWikiListStar(ApiData): +class ApiWikiListStar(ApiStar): path = "/api/wiki/list/v1" async def api(self, data: ApiData) -> dict: diff --git a/royalpack/tables/__init__.py b/royalpack/tables/__init__.py index 489127bd..a15e3c13 100644 --- a/royalpack/tables/__init__.py +++ b/royalpack/tables/__init__.py @@ -1,12 +1,9 @@ # Imports go here! from .diario import Diario from .wikipages import WikiPage -from .wikirevisions import WikiRevision from .bios import Bio from .reminders import Reminder from .triviascores import TriviaScore -from .mmevents import MMEvent -from .mmresponse import MMResponse from .leagueoflegends import LeagueOfLegends from .fiorygi import Fiorygi from .steam import Steam @@ -17,12 +14,9 @@ from .fiorygitransactions import FiorygiTransaction available_tables = [ Diario, WikiPage, - WikiRevision, Bio, Reminder, TriviaScore, - MMEvent, - MMResponse, LeagueOfLegends, Fiorygi, Steam, diff --git a/royalpack/tables/bios.py b/royalpack/tables/bios.py index 25a3a630..ebd6bf0d 100644 --- a/royalpack/tables/bios.py +++ b/royalpack/tables/bios.py @@ -10,19 +10,24 @@ class Bio: __tablename__ = "bios" @declared_attr - def royal_id(self): + def user_id(self): return Column(Integer, ForeignKey("users.uid"), primary_key=True) @declared_attr - def royal(self): + def user(self): return relationship("User", backref=backref("bio", uselist=False)) @declared_attr def contents(self): return Column(Text, nullable=False, default="") + def json(self) -> dict: + return { + "contents": self.contents + } + def __repr__(self): - return f"" + return f"" def __str__(self): return self.contents diff --git a/royalpack/tables/mmevents.py b/royalpack/tables/mmevents.py deleted file mode 100644 index f633c0f8..00000000 --- a/royalpack/tables/mmevents.py +++ /dev/null @@ -1,52 +0,0 @@ -import pickle -from sqlalchemy import * -from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declared_attr - - -class MMEvent: - __tablename__ = "mmevents" - - @declared_attr - def creator_id(self): - return Column(Integer, ForeignKey("users.uid"), nullable=False) - - @declared_attr - def creator(self): - return relationship("User", backref="mmevents_created") - - @declared_attr - def mmid(self): - return Column(Integer, primary_key=True) - - @declared_attr - def datetime(self): - return Column(DateTime, nullable=False) - - @declared_attr - def title(self): - return Column(String, nullable=False) - - @declared_attr - def description(self): - return Column(Text, nullable=False, default="") - - @declared_attr - def interface(self): - return Column(String, nullable=False) - - @declared_attr - def raw_interface_data(self): - # The default is a pickled None - return Column(Binary, nullable=False, default=b'\x80\x03N.') - - @property - def interface_data(self): - return pickle.loads(self.raw_interface_data) - - @interface_data.setter - def interface_data(self, value): - self.raw_interface_data = pickle.dumps(value) - - def __repr__(self): - return f"" diff --git a/royalpack/tables/mmresponse.py b/royalpack/tables/mmresponse.py deleted file mode 100644 index 4d04931a..00000000 --- a/royalpack/tables/mmresponse.py +++ /dev/null @@ -1,31 +0,0 @@ -from sqlalchemy import * -from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declared_attr -from ..utils import MMChoice - - -class MMResponse: - __tablename__ = "mmresponse" - - @declared_attr - def user_id(self): - return Column(Integer, ForeignKey("users.uid"), primary_key=True) - - @declared_attr - def user(self): - return relationship("User", backref="mmresponses_given") - - @declared_attr - def mmevent_id(self): - return Column(Integer, ForeignKey("mmevents.mmid"), primary_key=True) - - @declared_attr - def mmevent(self): - return relationship("MMEvent", backref="responses") - - @declared_attr - def choice(self): - return Column(Enum(MMChoice), nullable=False) - - def __repr__(self): - return f"" diff --git a/royalpack/tables/wikipages.py b/royalpack/tables/wikipages.py index c1fadfc9..facfdd02 100644 --- a/royalpack/tables/wikipages.py +++ b/royalpack/tables/wikipages.py @@ -31,7 +31,7 @@ class WikiPage: @declared_attr def theme(self): - return Column(String) + return Column(String, nullable=False, default="default") @property def page_short_id(self): diff --git a/royalpack/tables/wikirevisions.py b/royalpack/tables/wikirevisions.py deleted file mode 100644 index 456376d7..00000000 --- a/royalpack/tables/wikirevisions.py +++ /dev/null @@ -1,48 +0,0 @@ -from sqlalchemy import Column, \ - Integer, \ - Text, \ - DateTime, \ - ForeignKey -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declared_attr - - -class WikiRevision: - """A wiki page revision. - - Warning: - Requires PostgreSQL!""" - __tablename__ = "wikirevisions" - - @declared_attr - def revision_id(self): - return Column(UUID(as_uuid=True), primary_key=True) - - @declared_attr - def page_id(self): - return Column(UUID(as_uuid=True), ForeignKey("wikipages.page_id"), nullable=False) - - @declared_attr - def page(self): - return relationship("WikiPage", foreign_keys=self.page_id, backref="revisions") - - @declared_attr - def author_id(self): - return Column(Integer, ForeignKey("users.uid"), nullable=False) - - @declared_attr - def author(self): - return relationship("User", foreign_keys=self.author_id, backref="wiki_contributions") - - @declared_attr - def timestamp(self): - return Column(DateTime, nullable=False) - - @declared_attr - def reason(self): - return Column(Text) - - @declared_attr - def diff(self): - return Column(Text) diff --git a/royalpack/utils/__init__.py b/royalpack/utils/__init__.py index 4a9b5117..ba204e8f 100644 --- a/royalpack/utils/__init__.py +++ b/royalpack/utils/__init__.py @@ -7,6 +7,7 @@ from .royalqueue import RoyalQueue from .dotamedal import DotaMedal from .dotastars import DotaStars from .dotarank import DotaRank +from .finduser import find_user_api __all__ = [ "MMChoice", @@ -19,4 +20,5 @@ __all__ = [ "DotaMedal", "DotaStars", "DotaRank", + "find_user_api", ] diff --git a/royalpack/utils/finduser.py b/royalpack/utils/finduser.py new file mode 100644 index 00000000..d13ea95b --- /dev/null +++ b/royalpack/utils/finduser.py @@ -0,0 +1,19 @@ +from typing import * +from royalnet.constellation.api import * +from royalnet.backpack.tables.users import User +from royalnet.utils import asyncify + +async def find_user_api(input: Union[int, str], alchemy, session): + if isinstance(input, int): + user_id = input + elif isinstance(input, str): + try: + user_id = int(input) + except ValueError: + raise InvalidParameterError(f"Invalid user id passed to {find_user_api.__name__}") + else: + raise TypeError(f"Invalid input type passed to {find_user_api.__name__}") + user: User = await asyncify(session.query(alchemy.get(User)).get, user_id) + if user is None: + raise NotFoundError("No such user.") + return user