1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 11:34:18 +00:00

Start work on 5.5

This commit is contained in:
Steffo 2020-02-04 00:15:33 +01:00
parent cdfd6c3e49
commit c3e77fa5e6
14 changed files with 173 additions and 85 deletions

41
poetry.lock generated
View file

@ -66,6 +66,21 @@ version = "2.8.0"
[package.dependencies]
pytz = ">=2015.7"
[[package]]
category = "main"
description = "Modern password hashing for your software and your servers"
name = "bcrypt"
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.1.7"
[package.dependencies]
cffi = ">=1.1"
six = ">=1.4.1"
[package.extras]
tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)"]
[[package]]
category = "main"
description = "Python package for providing Mozilla's CA Bundle."
@ -913,8 +928,8 @@ python-versions = "*"
version = "2020.1.24"
[extras]
alchemy_easy = ["sqlalchemy", "psycopg2_binary"]
alchemy_hard = ["sqlalchemy", "psycopg2"]
alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"]
alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"]
bard = ["ffmpeg_python", "youtube_dl", "eyed3"]
coloredlogs = ["coloredlogs"]
constellation = ["starlette", "uvicorn", "python-multipart"]
@ -925,7 +940,7 @@ sentry = ["sentry_sdk"]
telegram = ["python_telegram_bot"]
[metadata]
content-hash = "f275cd948fe28423a90d37d2825eabfec97e8ac0cdf52ee2d20f803d61987b40"
content-hash = "218f4a253a7ef17bb871abf541ae7182f1626cd43d55417de12f9561c58ca0e9"
python-versions = "^3.8"
[metadata.files]
@ -963,6 +978,26 @@ babel = [
{file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"},
{file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"},
]
bcrypt = [
{file = "bcrypt-3.1.7-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7"},
{file = "bcrypt-3.1.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31"},
{file = "bcrypt-3.1.7-cp27-cp27m-win32.whl", hash = "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161"},
{file = "bcrypt-3.1.7-cp27-cp27m-win_amd64.whl", hash = "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e"},
{file = "bcrypt-3.1.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0"},
{file = "bcrypt-3.1.7-cp34-abi3-macosx_10_6_intel.whl", hash = "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052"},
{file = "bcrypt-3.1.7-cp34-abi3-manylinux1_x86_64.whl", hash = "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105"},
{file = "bcrypt-3.1.7-cp34-cp34m-win32.whl", hash = "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de"},
{file = "bcrypt-3.1.7-cp34-cp34m-win_amd64.whl", hash = "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133"},
{file = "bcrypt-3.1.7-cp35-cp35m-win32.whl", hash = "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5"},
{file = "bcrypt-3.1.7-cp35-cp35m-win_amd64.whl", hash = "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09"},
{file = "bcrypt-3.1.7-cp36-cp36m-win32.whl", hash = "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c"},
{file = "bcrypt-3.1.7-cp36-cp36m-win_amd64.whl", hash = "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89"},
{file = "bcrypt-3.1.7-cp37-cp37m-win32.whl", hash = "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294"},
{file = "bcrypt-3.1.7-cp37-cp37m-win_amd64.whl", hash = "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc"},
{file = "bcrypt-3.1.7-cp38-cp38-win32.whl", hash = "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1"},
{file = "bcrypt-3.1.7-cp38-cp38-win_amd64.whl", hash = "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752"},
{file = "bcrypt-3.1.7.tar.gz", hash = "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42"},
]
certifi = [
{file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"},
{file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"},

View file

@ -3,6 +3,7 @@ from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declared_attr
# noinspection PyAttributeOutsideInit
#set($CAPITALIZED_NAME = $NAME.substring(0,1).toUpperCase() + $NAME.substring(1))
class ${CAPITALIZED_NAME}:
__tablename__ = "${NAME}"

View file

@ -44,6 +44,7 @@
sqlalchemy = {version="^1.3.10", optional=true}
psycopg2 = {version="^2.8.4", optional=true} # Requires quite a bit of stuff http://initd.org/psycopg/docs/install.html#install-from-source
psycopg2_binary = {version="^2.8.4", optional=true} # Prebuilt alternative to psycopg2, not recommended
bcrypt = {version="^3.1.7", optional=true}
# constellation
starlette = {version="^0.12.13", optional=true}
@ -71,8 +72,8 @@
telegram = ["python_telegram_bot"]
discord = ["discord.py", "pynacl"]
matrix = ["matrix-nio"]
alchemy_easy = ["sqlalchemy", "psycopg2_binary"]
alchemy_hard = ["sqlalchemy", "psycopg2"]
alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"]
alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"]
bard = ["ffmpeg_python", "youtube_dl", "eyed3"]
constellation = ["starlette", "uvicorn", "python-multipart"]
sentry = ["sentry_sdk"]

View file

@ -1,22 +1,10 @@
# Imports go here!
from .version import VersionCommand
from .exception import ExceptionCommand
from .excevent import ExceventCommand
from .keyboardtest import KeyboardtestCommand
# Enter the commands of your Pack here!
available_commands = [
VersionCommand,
]
# noinspection PyUnreachableCode
if __debug__:
available_commands = [
*available_commands,
ExceptionCommand,
ExceventCommand,
KeyboardtestCommand,
]
# Don't change this, it should automatically generate __all__
__all__ = [command.__name__ for command in available_commands]

View file

@ -1,11 +0,0 @@
import royalnet
from royalnet.commands import *
class ExceptionCommand(Command):
name: str = "exception"
description: str = "Raise an exception in the command."
async def run(self, args: CommandArgs, data: CommandData) -> None:
raise Exception(f"{self.interface.prefix}{self.name} was called")

View file

@ -1,12 +0,0 @@
import royalnet
from royalnet.commands import *
class ExceventCommand(Command):
name: str = "excevent"
description: str = "Call an event that raises an exception."
async def run(self, args: CommandArgs, data: CommandData) -> None:
await self.interface.call_herald_event(self.interface.name, "exception")
await data.reply("✅ Event called!")

View file

@ -1,28 +0,0 @@
from typing import *
from royalnet.commands import *
import functools
import asyncio
class KeyboardtestCommand(Command):
name: str = "keyboardtest"
description: str = "Create a new keyboard with the specified keys."
syntax: str = "{keys}+"
@staticmethod
async def echo(data: CommandData, echo: str):
await data.reply(echo)
async def run(self, args: CommandArgs, data: CommandData) -> None:
keys = []
for arg in args:
# noinspection PyTypeChecker
keys.append(KeyboardKey(interface=self.interface,
short=arg[0],
text=arg,
callback=functools.partial(self.echo, echo=arg)))
async with data.keyboard("This is a test keyboard.", keys):
await asyncio.sleep(10)
await data.reply("The keyboard is no longer in scope.")

View file

@ -0,0 +1,86 @@
from typing import *
import royalnet
import royalnet.commands as rc
import royalnet.utils as ru
from ..tables.telegram import Telegram
from ..tables.discord import Discord
class SyncCommand(rc.Command):
name: str = "sync"
description: str = "Connect your chat account to Royalnet!"
syntax: str = "{username} {password}"
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None:
username = args[0]
password = " ".join(args[1:])
author = await data.get_author(error_if_none=True)
user = await data.find_user(username)
try:
successful = user.test_password(password)
except ValueError:
raise rc.UserError(f"User {user} has no password set!")
if not successful:
raise rc.InvalidInputError(f"Invalid password!")
if self.interface.name == "telegram":
import telegram
message: telegram.Message = data.message
from_user: telegram.User = message.from_user
TelegramT = self.alchemy.get(Telegram)
tg_user: Telegram = await ru.asyncify(
data.session.query(TelegramT).filter_by(tg_id=from_user.id).one_or_none
)
if tg_user is None:
# Create
tg_user = TelegramT(
user=author,
tg_id=from_user.id,
first_name=from_user.first_name,
last_name=from_user.last_name,
username=from_user.username
)
data.session.add(tg_user)
else:
# Edit
tg_user.first_name = from_user.first_name
tg_user.last_name = from_user.last_name
tg_user.username = from_user.username
await data.session_commit()
await data.reply(f"↔️ Account {tg_user} synced to {author}!")
elif self.interface.name == "discord":
import discord
message: discord.Message = data.message
author: discord.User = message.author
DiscordT = self.alchemy.get(Discord)
ds_user: Discord = await ru.asyncify(
data.session.query(DiscordT).filter_by(discord_id=author.id).one_or_none
)
if ds_user is None:
# Create
ds_user = DiscordT(
user=author,
discord_id=author.id,
username=author.name,
discriminator=author.discriminator,
avatar_url=author.avatar_url
)
data.session.add(ds_user)
else:
# Edit
ds_user.username = author.name
ds_user.discriminator = author.discriminator
ds_user.avatar_url = author.avatar_url
await data.session_commit()
await data.reply(f"↔️ Account {ds_user} synced to {author}!")
elif self.interface.name == "matrix":
raise rc.UnsupportedError(f"{self} hasn't been implemented for Matrix yet")
else:
raise rc.UnsupportedError(f"Unknown interface: {self.interface.name}")

View file

@ -10,26 +10,30 @@ class Alias:
__tablename__ = "aliases"
@declared_attr
def royal_id(self):
return Column(Integer, ForeignKey("users.uid"))
def user_id(self):
return Column(Integer, ForeignKey("users.uid"), primary_key=True)
@declared_attr
def alias(self):
return Column(String, primary_key=True)
@declared_attr
def royal(self):
def user(self):
return relationship("User", backref="aliases")
@classmethod
def find_by_alias(cls, alchemy, session, alias: str):
def find_user(cls, alchemy, session, alias: str):
result = session.query(alchemy.get(cls)).filter_by(alias=alias.lower()).one_or_none()
if result is not None:
result = result.royal
return result
def __init__(self, user: str, alias: str):
self.user = user
self.alias = alias.lower()
def __repr__(self):
return f"<Alias {str(self)}>"
def __str__(self):
return f"{self.alias}->{self.royal_id}"
return f"{self.alias}->{self.user_id}"

View file

@ -13,9 +13,13 @@ class Discord:
__tablename__ = "discord"
@declared_attr
def royal_id(self):
def user_id(self):
return Column(Integer, ForeignKey("users.uid"))
@declared_attr
def user(self):
return relationship("User", backref="discord")
@declared_attr
def discord_id(self):
return Column(BigInteger, primary_key=True)
@ -29,13 +33,9 @@ class Discord:
return Column(String)
@declared_attr
def avatar_hash(self):
def avatar_url(self):
return Column(String)
@declared_attr
def royal(self):
return relationship("User", backref="discord")
def __repr__(self):
return f"<Discord {str(self)}>"

View file

@ -13,9 +13,13 @@ class Telegram:
__tablename__ = "telegram"
@declared_attr
def royal_id(self):
def user_id(self):
return Column(Integer, ForeignKey("users.uid"))
@declared_attr
def user(self):
return relationship("User", backref="telegram")
@declared_attr
def tg_id(self):
return Column(BigInteger, primary_key=True)
@ -32,10 +36,6 @@ class Telegram:
def username(self):
return Column(String)
@declared_attr
def royal(self):
return relationship("User", backref="telegram")
def __repr__(self):
return f"<Telegram {str(self)}>"

View file

@ -1,3 +1,4 @@
import bcrypt
from sqlalchemy import Column, \
Integer, \
String, \
@ -5,6 +6,7 @@ from sqlalchemy import Column, \
from sqlalchemy.ext.declarative import declared_attr
# noinspection PyAttributeOutsideInit
class User:
__tablename__ = "users"
@ -36,6 +38,16 @@ class User:
"avatar": self.avatar
}
def set_password(self, password: str):
byte_password: bytes = bytes(password, encoding="UTF8")
self.password = bcrypt.hashpw(byte_password, bcrypt.gensalt(14))
def test_password(self, password: str):
if self.password is None:
raise ValueError("No password is set")
byte_password: bytes = bytes(password, encoding="UTF8")
return bcrypt.checkpw(byte_password, self.password)
def __repr__(self):
return f"<{self.__class__.__qualname__} {self.username}>"

View file

@ -5,9 +5,11 @@ import asyncio as aio
import royalnet.utils as ru
from .errors import UnsupportedError
from .commandinterface import CommandInterface
from royalnet.backpack.tables.aliases import Alias
if TYPE_CHECKING:
from .keyboardkey import KeyboardKey
from royalnet.backpack.tables.users import User
log = logging.getLogger(__name__)
@ -64,6 +66,16 @@ class CommandData:
if error_if_unavailable:
raise UnsupportedError(f"'{self.delete_invoking.__name__}' is not supported")
async def find_user(self, alias: str) -> Optional["User"]:
"""Find the User having a specific Alias.
Parameters:
alias: the Alias to search for."""
return await ru.asyncify(
Alias.find_user(self._interface.alchemy, self.session, alias)
)
@contextlib.asynccontextmanager
async def keyboard(self, text, keys: List["KeyboardKey"]):
yield

View file

@ -1 +1 @@
semantic = "5.4"
semantic = "5.5"