1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-30 15:04:18 +00:00
royalnet/db.py

1361 lines
46 KiB
Python
Raw Normal View History

2017-10-19 16:28:28 +00:00
import datetime
2018-09-18 22:02:39 +00:00
import logging
2018-09-18 22:13:41 +00:00
import os
import coloredlogs
2017-10-04 16:51:40 +00:00
from sqlalchemy.ext.declarative import declarative_base
2018-10-07 15:19:42 +00:00
from sqlalchemy.orm import sessionmaker, relationship
2018-07-23 16:45:43 +00:00
from sqlalchemy.ext.hybrid import hybrid_property
2019-01-02 20:10:54 +00:00
from sqlalchemy import Column, BigInteger, Integer, String, DateTime, ForeignKey, Float, Enum, create_engine, \
2019-01-03 11:32:59 +00:00
UniqueConstraint, PrimaryKeyConstraint, Boolean, LargeBinary, Text, Date, func
2019-01-09 12:54:26 +00:00
from sqlalchemy.inspection import inspect
2017-10-05 07:47:59 +00:00
import requests
2019-01-09 15:04:57 +00:00
import errors
2018-12-18 16:34:34 +00:00
from errors import NotFoundError, AlreadyExistingError, PrivateError
2017-10-05 07:47:59 +00:00
import re
2017-10-08 18:26:51 +00:00
import enum
2018-10-01 15:46:06 +00:00
import loldata
2019-01-23 14:00:30 +00:00
from dirty import Dirty, DirtyDelta
2019-01-25 19:35:14 +00:00
import sql_queries
from flask import escape
2019-01-08 20:03:18 +00:00
import configparser
2019-01-28 22:36:45 +00:00
import typing
if typing.TYPE_CHECKING:
# noinspection PyPackageRequirements
from discord import User as DiscordUser
# noinspection PyPackageRequirements
from telegram import User as TelegramUser
2017-10-04 16:51:40 +00:00
# Init the config reader
config = configparser.ConfigParser()
config.read("config.ini")
# Init the sqlalchemy engine
engine = create_engine(config["Database"]["database_uri"])
Base = declarative_base(bind=engine)
Session = sessionmaker(bind=engine)
2018-09-18 22:13:41 +00:00
logging.getLogger().disabled = True
logger = logging.getLogger(__name__)
os.environ["COLOREDLOGS_LOG_FORMAT"] = "%(asctime)s %(levelname)s %(name)s %(message)s"
coloredlogs.install(level="DEBUG", logger=logger)
2018-02-26 09:34:44 +00:00
2018-10-07 15:19:42 +00:00
2019-01-09 14:04:21 +00:00
def relationship_name_search(_class, keyword) -> typing.Optional[tuple]:
2019-01-09 12:54:26 +00:00
"""Recursively find a relationship with a given name."""
inspected = set()
def search(_mapper, chain):
inspected.add(_mapper)
relationships = _mapper.relationships
try:
return chain + (relationships[keyword],)
except KeyError:
for _relationship in set(relationships):
if _relationship.mapper in inspected:
continue
result = search(_relationship.mapper, chain + (_relationship,))
if result is not None:
return result
return None
return search(inspect(_class), tuple())
2019-01-09 14:04:21 +00:00
def relationship_link_chain(starting_class, ending_class) -> typing.Optional[tuple]:
"""Find the path to follow to get from the starting table to the ending table."""
inspected = set()
def search(_mapper, chain):
inspected.add(_mapper)
if _mapper.class_ == ending_class:
return chain
relationships = _mapper.relationships
for _relationship in set(relationships):
if _relationship.mapper in inspected:
continue
2019-01-09 15:04:57 +00:00
try:
return search(_relationship.mapper, chain + (_relationship,))
except errors.NotFoundError:
continue
raise errors.NotFoundError()
2019-01-09 14:04:21 +00:00
return search(inspect(starting_class), tuple())
2019-01-09 12:54:26 +00:00
class Mini(object):
2019-01-09 15:04:57 +00:00
"""Mixin for every table that has an associated mini."""
_mini_full_name = NotImplemented
_mini_name = NotImplemented
2019-01-09 15:52:34 +00:00
_mini_order = NotImplemented
2019-01-09 15:04:57 +00:00
2019-01-09 12:54:26 +00:00
@classmethod
def mini_get_all(cls, session: Session) -> list:
2019-01-09 15:52:34 +00:00
return session.query(cls).order_by(*cls._mini_order).all()
2019-01-09 12:54:26 +00:00
@classmethod
def mini_get_single(cls, session: Session, **kwargs):
return session.query(cls).filter_by(**kwargs).one_or_none()
@classmethod
def mini_get_single_from_royal(cls, session: Session, royal: "Royal"):
2019-01-09 15:04:57 +00:00
chain = relationship_link_chain(cls, Royal)
if chain is None:
2019-01-09 16:07:35 +00:00
chain = tuple()
2019-01-09 15:04:57 +00:00
start = session.query(cls)
for connection in chain:
start = start.join(connection.mapper.class_)
start = start.filter(Royal.id == royal.id)
mini = start.one()
2019-01-09 16:07:35 +00:00
return mini
2019-01-09 12:54:26 +00:00
class Royal(Base, Mini):
2017-10-04 16:51:40 +00:00
__tablename__ = "royals"
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
2018-05-07 10:51:24 +00:00
password = Column(LargeBinary)
2018-06-08 18:13:20 +00:00
role = Column(String)
2018-05-07 10:51:24 +00:00
fiorygi = Column(Integer, default=0)
2018-06-08 18:54:58 +00:00
member_since = Column(Date)
2017-10-04 16:51:40 +00:00
2019-01-09 15:52:34 +00:00
_mini_full_name = "Royalnet"
_mini_name = "ryg"
_mini_order = [fiorygi.desc()]
2017-10-17 18:49:28 +00:00
@staticmethod
2017-10-30 09:46:37 +00:00
def create(session: Session, username: str):
2017-10-17 18:49:28 +00:00
r = session.query(Royal).filter_by(username=username).first()
if r is not None:
2017-10-27 09:53:05 +00:00
raise AlreadyExistingError(repr(r))
2017-10-17 18:49:28 +00:00
return Royal(username=username)
2017-10-04 16:51:40 +00:00
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.Royal {self.username}>"
2017-10-04 16:51:40 +00:00
2019-01-09 12:54:26 +00:00
@classmethod
def mini_get_single_from_royal(cls, session: Session, royal: "Royal"):
return royal
2017-10-04 16:51:40 +00:00
2019-01-09 15:04:57 +00:00
class Telegram(Base, Mini):
2017-10-04 16:51:40 +00:00
__tablename__ = "telegram"
2018-08-28 13:14:29 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"))
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="telegram", lazy="joined")
2017-10-04 16:51:40 +00:00
telegram_id = Column(BigInteger, primary_key=True)
2018-08-28 13:14:29 +00:00
first_name = Column(String)
2017-10-04 16:51:40 +00:00
last_name = Column(String)
username = Column(String)
2019-01-09 15:52:34 +00:00
_mini_full_name = "Telegram"
_mini_name = "tg"
_mini_order = [telegram_id]
2017-10-30 09:46:37 +00:00
@staticmethod
2019-01-28 22:38:34 +00:00
def create(session: Session, royal_username, telegram_user: "TelegramUser"):
2017-10-30 09:46:37 +00:00
t = session.query(Telegram).filter_by(telegram_id=telegram_user.id).first()
if t is not None:
raise AlreadyExistingError(repr(t))
r = session.query(Royal).filter(Royal.username == royal_username).first()
if r is None:
raise NotFoundError("No Royal exists with that username")
t = session.query(Telegram).filter(Telegram.royal_id == r.id).first()
if t is not None:
raise AlreadyExistingError(repr(t))
return Telegram(royal=r,
telegram_id=telegram_user.id,
first_name=telegram_user.first_name,
last_name=telegram_user.last_name,
username=telegram_user.username)
2017-10-04 16:51:40 +00:00
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.Telegram {self.telegram_id}>"
2017-10-04 16:51:40 +00:00
2018-03-14 10:53:26 +00:00
def mention(self):
2017-10-04 16:51:40 +00:00
if self.username is not None:
return f"@{self.username}"
2018-03-14 10:53:26 +00:00
else:
return self.first_name
def __str__(self):
if self.username is not None:
return self.username
2017-10-04 16:51:40 +00:00
elif self.last_name is not None:
return f"{self.first_name} {self.last_name}"
else:
return self.first_name
2019-01-09 12:54:26 +00:00
@classmethod
def mini_get_single_from_royal(cls, session: Session, royal: "Royal"):
return royal.telegram
2017-10-04 16:51:40 +00:00
2019-01-09 15:04:57 +00:00
class Steam(Base, Mini):
2017-10-04 21:29:02 +00:00
__tablename__ = "steam"
2018-08-28 13:14:29 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"))
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="steam", lazy="joined")
2017-10-04 21:29:02 +00:00
steam_id = Column(String, primary_key=True)
2018-06-05 08:34:59 +00:00
persona_name = Column(String)
avatar_hex = Column(String)
2017-10-07 22:43:41 +00:00
trade_token = Column(String)
2018-06-05 08:34:59 +00:00
most_played_game_id = Column(BigInteger)
2017-10-04 21:29:02 +00:00
2019-01-09 15:52:34 +00:00
_mini_full_name = "Steam"
_mini_name = "steam"
_mini_order = [steam_id]
2017-10-04 21:29:02 +00:00
def __repr__(self):
2018-09-17 22:07:00 +00:00
if not self.persona_name:
return f"<db.Steam {self.steam_id}>"
return f"<db.Steam {self.persona_name}>"
2017-10-04 21:29:02 +00:00
def __str__(self):
2017-10-05 18:21:47 +00:00
if self.persona_name is not None:
return self.persona_name
2017-10-04 21:29:02 +00:00
else:
return self.steam_id
2018-06-05 08:34:59 +00:00
def most_played_game_url(self):
return f"https://steamcdn-a.akamaihd.net/steam/apps/{self.most_played_game_id}/header.jpg"
2017-10-25 09:09:06 +00:00
def avatar_url(self):
2019-01-02 20:10:54 +00:00
return f"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/{self.avatar_hex[0:2]}/"\
f"{self.avatar_hex}.jpg"
2017-10-25 09:09:06 +00:00
2017-10-08 18:26:51 +00:00
@staticmethod
2017-10-30 09:46:37 +00:00
def create(session: Session, royal_id: int, steam_id: str):
2017-10-08 18:26:51 +00:00
s = session.query(Steam).get(steam_id)
if s is not None:
2017-10-27 09:53:05 +00:00
raise AlreadyExistingError(repr(s))
2019-01-02 20:10:54 +00:00
r = requests.get(f"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/"
f"?key={config['Steam']['api_key']}&steamids={steam_id}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-08 18:26:51 +00:00
j = r.json()
if len(j) == 0:
raise NotFoundError(f"The steam_id doesn't match any steam account")
s = Steam(royal_id=royal_id,
steam_id=steam_id,
persona_name=j["response"]["players"][0]["personaname"],
2019-01-02 20:10:54 +00:00
avatar_hex=re.search(r"https://steamcdn-a\.akamaihd\.net/steamcommunity/public/images/avatars/../"
r"(.+).jpg", j["response"]["players"][0]["avatar"]).group(1))
2017-10-08 18:26:51 +00:00
return s
2017-10-07 23:29:01 +00:00
2017-10-08 18:26:51 +00:00
@staticmethod
def find_trade_token(trade_url):
2019-01-02 20:10:54 +00:00
return re.search(r"https://steamcommunity\.com/tradeoffer/new/\?partner=[0-9]+&token=(.{8})", trade_url)\
.group(1)
2017-10-08 18:26:51 +00:00
@staticmethod
def to_steam_id_2(steam_id):
2017-10-07 23:29:01 +00:00
# Got this code from a random github gist. It could be completely wrong.
2017-10-08 18:26:51 +00:00
z = (int(steam_id) - 76561197960265728) // 2
y = int(steam_id) % 2
2017-10-07 23:29:01 +00:00
return f"STEAM_0:{y}:{z}"
2017-10-08 18:26:51 +00:00
@staticmethod
def to_steam_id_3(steam_id, full=False):
2017-10-07 23:29:01 +00:00
# Got this code from a random github gist. It could be completely wrong.
if full:
2017-10-08 18:26:51 +00:00
return f"[U:1:{int(steam_id) - 76561197960265728}]"
2017-10-07 23:29:01 +00:00
else:
2017-10-08 18:26:51 +00:00
return f"{int(steam_id) - 76561197960265728}"
2017-10-07 23:29:01 +00:00
2019-01-02 20:10:54 +00:00
# noinspection PyUnusedLocal
2018-10-07 22:42:45 +00:00
def update(self, session=None, raise_if_private: bool=False):
2019-01-02 20:10:54 +00:00
r = requests.get(f"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/"
f"?key={config['Steam']['api_key']}&steamids={self.steam_id}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-05 07:47:59 +00:00
j = r.json()
self.persona_name = j["response"]["players"][0]["personaname"]
2019-01-02 20:10:54 +00:00
self.avatar_hex = re.search(r"https://steamcdn-a\.akamaihd\.net/steamcommunity/public/images/avatars/../"
r"(.+).jpg", j["response"]["players"][0]["avatar"]).group(1)
r = requests.get(f"http://api.steampowered.com/IPlayerService/GetRecentlyPlayedGames/v0001/"
f"?key={config['Steam']['api_key']}&steamid={self.steam_id}&format=json")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2018-06-05 08:34:59 +00:00
j = r.json()
2018-12-18 16:34:34 +00:00
if "response" not in j or "games" not in j["response"] or len(j["response"]["games"]) < 1:
2018-09-24 18:46:38 +00:00
if raise_if_private:
2018-12-18 16:34:34 +00:00
raise PrivateError(f"Game data is private")
2018-09-24 18:46:38 +00:00
return
2018-06-05 08:34:59 +00:00
self.most_played_game_id = j["response"]["games"][0]["appid"]
2017-10-05 18:21:47 +00:00
2019-01-09 12:54:26 +00:00
@classmethod
def mini_get_single_from_royal(cls, session: Session, royal: "Royal"):
return royal.steam
2018-06-13 21:32:26 +00:00
2019-01-09 15:04:57 +00:00
class RocketLeague(Base, Mini):
2017-10-05 18:21:47 +00:00
__tablename__ = "rocketleague"
steam_id = Column(String, ForeignKey("steam.steam_id"), primary_key=True)
2018-07-15 12:41:42 +00:00
steam = relationship("Steam", backref="rl", lazy="joined")
2017-10-05 18:21:47 +00:00
season = Column(Integer)
single_rank = Column(Integer)
single_div = Column(Integer)
single_mmr = Column(Integer)
doubles_rank = Column(Integer)
doubles_div = Column(Integer)
doubles_mmr = Column(Integer)
standard_rank = Column(Integer)
standard_div = Column(Integer)
standard_mmr = Column(Integer)
solo_std_rank = Column(Integer)
solo_std_div = Column(Integer)
solo_std_mmr = Column(Integer)
wins = Column(Integer)
2019-01-09 15:52:34 +00:00
_mini_full_name = "Rocket League"
_mini_name = "rl"
_mini_order = [solo_std_mmr.desc().nullslast(),
doubles_mmr.desc().nullslast(),
standard_mmr.desc().nullslast(),
single_mmr.desc().nullslast()]
2017-10-05 18:21:47 +00:00
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.RocketLeague {self.steam_id}>"
2017-10-05 18:21:47 +00:00
2018-10-07 22:42:45 +00:00
def update(self, session=None, data=None):
2018-08-28 13:14:29 +00:00
raise NotImplementedError("rlstats API is no longer available.")
2017-10-05 18:21:47 +00:00
2018-06-04 11:13:59 +00:00
def solo_rank_image(self):
if self.single_rank is None:
rank = 0
else:
rank = self.single_rank
return f"https://rocketleaguestats.com/assets/img/rocket_league/ranked/season_four/{rank}.png"
def doubles_rank_image(self):
if self.doubles_rank is None:
rank = 0
else:
rank = self.doubles_rank
return f"https://rocketleaguestats.com/assets/img/rocket_league/ranked/season_four/{rank}.png"
def standard_rank_image(self):
if self.standard_rank is None:
rank = 0
else:
rank = self.standard_rank
return f"https://rocketleaguestats.com/assets/img/rocket_league/ranked/season_four/{rank}.png"
def solo_std_rank_image(self):
if self.solo_std_rank is None:
rank = 0
else:
rank = self.solo_std_rank
return f"https://rocketleaguestats.com/assets/img/rocket_league/ranked/season_four/{rank}.png"
2017-10-05 18:21:47 +00:00
2019-01-09 15:04:57 +00:00
class Dota(Base, Mini):
2017-10-08 18:26:51 +00:00
__tablename__ = "dota"
steam_id = Column(String, ForeignKey("steam.steam_id"), primary_key=True)
2018-07-15 12:41:42 +00:00
steam = relationship("Steam", backref="dota", lazy="joined")
2017-10-08 18:26:51 +00:00
2018-01-19 08:52:01 +00:00
rank_tier = Column(Integer)
2018-08-28 13:14:29 +00:00
wins = Column(Integer)
losses = Column(Integer)
most_played_hero = Column(Integer)
2017-10-08 18:26:51 +00:00
2019-01-09 15:52:34 +00:00
_mini_full_name = "DOTA 2"
_mini_name = "dota"
_mini_order = [rank_tier.desc().nullslast(), wins.desc().nullslast()]
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<db.Dota {self.steam_id}>"
2018-01-19 08:52:01 +00:00
def get_rank_icon_url(self):
2018-01-19 14:51:54 +00:00
# Rank icon is determined by the first digit of the rank tier
2019-01-02 20:10:54 +00:00
if self.rank_tier is None:
return f"https://www.opendota.com/assets/images/dota2/rank_icons/rank_icon_0.png"
return f"https://www.opendota.com/assets/images/dota2/rank_icons/rank_icon_{str(self.rank_tier)[0]}.png"
2018-01-19 14:51:54 +00:00
def get_rank_stars_url(self):
# Rank stars are determined by the second digit of the rank tier
if self.rank_tier is None or str(self.rank_tier)[1] == "0":
return ""
return f"https://www.opendota.com/assets/images/dota2/rank_icons/rank_star_{str(self.rank_tier)[1]}.png"
def get_rank_name(self):
# This should probably be an enum, but who cares
if self.rank_tier is None or self.rank_tier < 10:
return "Unranked"
number = str(self.rank_tier)[0]
if number == "1":
return "Harald"
elif number == "2":
return "Guardian"
elif number == "3":
return "Crusader"
elif number == "4":
return "Archon"
elif number == "5":
return "Legend"
elif number == "6":
return "Ancient"
elif number == "7":
return "Divine"
def get_rank_number(self):
2018-01-19 08:52:01 +00:00
if self.rank_tier is None or self.rank_tier < 10:
2018-01-19 14:51:54 +00:00
return ""
return str(self.rank_tier)[1]
2018-01-19 08:52:01 +00:00
2017-10-08 18:26:51 +00:00
@staticmethod
2018-08-28 13:14:29 +00:00
def create(session: Session, steam_id: int) -> "Dota":
2017-10-08 18:26:51 +00:00
d = session.query(Dota).get(steam_id)
if d is not None:
2017-10-27 09:53:05 +00:00
raise AlreadyExistingError(repr(d))
2017-10-08 18:26:51 +00:00
r = requests.get(f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(steam_id)}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-08 18:26:51 +00:00
data = r.json()
if "profile" not in data:
raise NotFoundError("The specified user has never played Dota or has a private match history")
2018-08-28 13:14:29 +00:00
new_record = Dota(steam_id=str(steam_id))
new_record.update()
2017-10-08 18:26:51 +00:00
return new_record
2019-01-02 20:10:54 +00:00
# noinspection PyUnusedLocal
2018-10-07 22:42:45 +00:00
def update(self, session=None) -> bool:
2017-10-08 18:26:51 +00:00
r = requests.get(f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(self.steam_id)}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-08 18:26:51 +00:00
data = r.json()
2017-10-10 21:30:06 +00:00
r = requests.get(f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(self.steam_id)}/wl")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-08 18:26:51 +00:00
wl = r.json()
2018-08-28 13:14:29 +00:00
r = requests.get(f"https://api.opendota.com/api/players/{Steam.to_steam_id_3(self.steam_id)}/heroes")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2018-08-28 13:14:29 +00:00
heroes = r.json()
2018-09-17 22:07:00 +00:00
changed = self.rank_tier != data["rank_tier"]
2018-01-19 08:52:01 +00:00
self.rank_tier = data["rank_tier"]
2017-10-08 18:26:51 +00:00
self.wins = wl["win"]
self.losses = wl["lose"]
2018-08-28 15:38:17 +00:00
self.most_played_hero = heroes[0]["hero_id"]
2018-09-17 22:07:00 +00:00
return changed
2017-10-08 18:26:51 +00:00
class LeagueOfLegendsRanks(enum.Enum):
2018-12-06 15:57:08 +00:00
IRON = 0
BRONZE = 1
SILVER = 2
GOLD = 3
PLATINUM = 4
DIAMOND = 5
MASTER = 6
GRANDMASTER = 7
CHALLENGER = 8
2017-10-08 18:26:51 +00:00
2018-12-18 18:28:27 +00:00
def __str__(self):
return self.name.capitalize()
2017-10-08 18:26:51 +00:00
class RomanNumerals(enum.Enum):
I = 1
II = 2
III = 3
IV = 4
2018-12-18 18:28:27 +00:00
def __str__(self):
return self.name
2017-10-08 18:26:51 +00:00
2019-01-09 15:04:57 +00:00
class LeagueOfLegends(Base, Mini):
2017-10-08 18:26:51 +00:00
__tablename__ = "leagueoflegends"
2018-08-28 13:14:29 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"))
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="lol", lazy="joined")
2017-10-08 18:26:51 +00:00
2018-12-18 16:34:34 +00:00
icon_id = Column(Integer)
summoner_id = Column(String, primary_key=True)
account_id = Column(String)
2018-08-28 13:14:29 +00:00
summoner_name = Column(String)
level = Column(Integer)
2017-10-08 18:26:51 +00:00
solo_division = Column(Enum(LeagueOfLegendsRanks))
solo_rank = Column(Enum(RomanNumerals))
flex_division = Column(Enum(LeagueOfLegendsRanks))
flex_rank = Column(Enum(RomanNumerals))
twtr_division = Column(Enum(LeagueOfLegendsRanks))
twtr_rank = Column(Enum(RomanNumerals))
2018-08-28 13:14:29 +00:00
highest_mastery_champ = Column(Integer)
2019-01-09 15:52:34 +00:00
_mini_full_name = "League of Legends"
_mini_name = "lol"
_mini_order = [solo_division.desc().nullslast(),
solo_rank.desc().nullslast(),
flex_division.desc().nullslast(),
flex_rank.desc().nullslast(),
twtr_division.desc().nullslast(),
twtr_rank.desc().nullslast()]
2018-09-17 22:07:00 +00:00
def __repr__(self):
if not self.summoner_name:
return f"<LeagueOfLegends {self.summoner_id}>"
return f"<LeagueOfLegends {(''.join([x if x.isalnum else '' for x in self.summoner_name]))}>"
2018-12-18 16:34:34 +00:00
@staticmethod
def create(royal_id, summoner_name) -> "LeagueOfLegends":
2019-01-02 20:10:54 +00:00
r = requests.get(f"https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/{summoner_name}"
f"?api_key={config['League of Legends']['riot_api_key']}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
data = r.json()
lol = LeagueOfLegends()
lol.royal_id = royal_id
lol.summoner_name = summoner_name
lol.summoner_id = data["id"]
lol.account_id = data["accountId"]
lol.icon_id = data["profileIconId"]
lol.level = data["summonerLevel"]
lol.update()
return lol
2019-01-02 20:10:54 +00:00
# noinspection PyUnusedLocal
2018-12-18 16:34:34 +00:00
def update(self, session=None):
2019-01-02 20:10:54 +00:00
r = requests.get(f"https://euw1.api.riotgames.com/lol/summoner/v4/summoners/"
f"{self.summoner_id}?api_key={config['League of Legends']['riot_api_key']}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-08 18:26:51 +00:00
data = r.json()
2019-01-02 20:10:54 +00:00
r = requests.get(f"https://euw1.api.riotgames.com/lol/league/v4/positions/by-summoner/"
f"{self.summoner_id}?api_key={config['League of Legends']['riot_api_key']}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-08 18:26:51 +00:00
rank = r.json()
2019-01-02 20:10:54 +00:00
r = requests.get(f"https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-summoner/"
f"{self.summoner_id}?api_key={config['League of Legends']['riot_api_key']}")
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2018-08-28 13:14:29 +00:00
mastery = r.json()
2018-12-18 16:34:34 +00:00
solo_q = None
flex_q = None
twtr_q = None
2017-10-08 18:26:51 +00:00
for league in rank:
if league["queueType"] == "RANKED_SOLO_5x5":
2018-12-18 16:34:34 +00:00
solo_q = league
2017-10-08 18:26:51 +00:00
elif league["queueType"] == "RANKED_FLEX_SR":
2018-12-18 16:34:34 +00:00
flex_q = league
2017-10-08 18:26:51 +00:00
elif league["queueType"] == "RANKED_FLEX_TT":
2018-12-18 16:34:34 +00:00
twtr_q = league
2017-10-08 18:26:51 +00:00
self.summoner_id = data["id"]
self.summoner_name = data["name"]
2018-10-01 15:46:06 +00:00
self.account_id = data["accountId"]
2017-10-08 18:26:51 +00:00
self.level = data["summonerLevel"]
2018-12-18 16:34:34 +00:00
solo = Dirty((self.solo_division, self.solo_rank))
flex = Dirty((self.flex_division, self.flex_rank))
twtr = Dirty((self.twtr_division, self.twtr_rank))
2019-01-02 20:10:54 +00:00
solo.value = (None, None) if solo_q is None else (LeagueOfLegendsRanks[solo_q["tier"]],
RomanNumerals[solo_q["rank"]])
flex.value = (None, None) if flex_q is None else (LeagueOfLegendsRanks[flex_q["tier"]],
RomanNumerals[flex_q["rank"]])
twtr.value = (None, None) if twtr_q is None else (LeagueOfLegendsRanks[twtr_q["tier"]],
RomanNumerals[twtr_q["rank"]])
2018-08-28 13:14:29 +00:00
self.highest_mastery_champ = mastery[0]["championId"]
2018-12-18 18:28:27 +00:00
self.solo_division = solo.value[0]
self.solo_rank = solo.value[1]
self.flex_division = flex.value[0]
self.flex_rank = flex.value[1]
self.twtr_division = twtr.value[0]
self.twtr_rank = twtr.value[1]
2018-12-18 16:34:34 +00:00
return solo, flex, twtr
2017-10-08 18:26:51 +00:00
2018-10-01 15:46:06 +00:00
def highest_mastery_champ_name(self):
champ = loldata.get_champ_by_key(self.highest_mastery_champ)
return champ["name"]
def highest_mastery_champ_image(self):
2019-01-02 17:16:53 +00:00
champ = loldata.get_champ_by_key(self.highest_mastery_champ)
2018-10-01 15:46:06 +00:00
return loldata.get_champ_icon(champ["name"])
2017-10-08 18:26:51 +00:00
2019-01-09 15:04:57 +00:00
class Osu(Base, Mini):
2017-10-09 11:45:42 +00:00
__tablename__ = "osu"
2017-10-17 18:49:28 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"), nullable=False)
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="osu", lazy="joined")
2017-10-09 11:45:42 +00:00
osu_id = Column(Integer, primary_key=True)
2018-08-28 13:14:29 +00:00
osu_name = Column(String)
2019-01-23 14:00:30 +00:00
std_pp = Column(Float, default=0)
std_best_song = Column(BigInteger)
taiko_pp = Column(Float, default=0)
taiko_best_song = Column(BigInteger)
catch_pp = Column(Float, default=0)
catch_best_song = Column(BigInteger)
mania_pp = Column(Float, default=0)
mania_best_song = Column(BigInteger)
2017-10-09 11:45:42 +00:00
2019-01-09 15:52:34 +00:00
_mini_full_name = "osu!"
_mini_name = "osu"
_mini_order = [mania_pp.desc().nullslast(),
std_pp.desc().nullslast(),
taiko_pp.desc().nullslast(),
catch_pp.desc().nullslast()]
2017-10-09 11:45:42 +00:00
@staticmethod
2017-10-30 09:46:37 +00:00
def create(session: Session, royal_id, osu_name):
2017-10-17 18:49:28 +00:00
o = session.query(Osu).filter(Osu.osu_name == osu_name).first()
2017-10-09 11:45:42 +00:00
if o is not None:
2017-10-27 09:53:05 +00:00
raise AlreadyExistingError(repr(o))
2017-10-09 11:45:42 +00:00
r0 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=0")
2018-12-18 16:34:34 +00:00
r0.raise_for_status()
2017-10-09 11:45:42 +00:00
r1 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=1")
2018-12-18 16:34:34 +00:00
r1.raise_for_status()
2017-10-09 11:45:42 +00:00
r2 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=2")
2018-12-18 16:34:34 +00:00
r2.raise_for_status()
2017-10-09 11:45:42 +00:00
r3 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={osu_name}&m=3")
2018-12-18 16:34:34 +00:00
r3.raise_for_status()
2017-10-09 11:45:42 +00:00
j0 = r0.json()[0]
j1 = r1.json()[0]
j2 = r2.json()[0]
j3 = r3.json()[0]
new_record = Osu(royal_id=royal_id,
osu_id=j0["user_id"],
osu_name=j0["username"],
std_pp=j0["pp_raw"],
taiko_pp=j1["pp_raw"],
catch_pp=j2["pp_raw"],
mania_pp=j3["pp_raw"])
return new_record
2019-01-02 20:10:54 +00:00
# noinspection PyUnusedLocal
2018-10-07 22:42:45 +00:00
def update(self, session=None):
2019-01-23 14:00:30 +00:00
std = DirtyDelta(self.std_pp)
taiko = DirtyDelta(self.taiko_pp)
catch = DirtyDelta(self.catch_pp)
mania = DirtyDelta(self.mania_pp)
2017-10-10 21:30:06 +00:00
r0 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=0")
2018-12-18 16:34:34 +00:00
r0.raise_for_status()
2019-01-23 14:00:30 +00:00
j0 = r0.json()[0]
2017-10-10 21:30:06 +00:00
r1 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=1")
2018-12-18 16:34:34 +00:00
r1.raise_for_status()
2019-01-23 14:00:30 +00:00
j1 = r1.json()[0]
2017-10-10 21:30:06 +00:00
r2 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=2")
2018-12-18 16:34:34 +00:00
r2.raise_for_status()
2019-01-23 14:00:30 +00:00
j2 = r2.json()[0]
2017-10-10 21:30:06 +00:00
r3 = requests.get(f"https://osu.ppy.sh/api/get_user?k={config['Osu!']['ppy_api_key']}&u={self.osu_name}&m=3")
2018-12-18 16:34:34 +00:00
r3.raise_for_status()
2017-10-09 11:45:42 +00:00
j3 = r3.json()[0]
self.osu_name = j0["username"]
2019-01-23 18:29:44 +00:00
std.value = float(j0["pp_raw"] or 0)
taiko.value = float(j1["pp_raw"] or 0)
catch.value = float(j2["pp_raw"] or 0)
mania.value = float(j3["pp_raw"] or 0)
2019-01-23 14:00:30 +00:00
self.std_pp = std.value
self.taiko_pp = taiko.value
self.catch_pp = catch.value
self.mania_pp = mania.value
return std, taiko, catch, mania
2017-10-09 11:45:42 +00:00
2018-09-17 22:07:00 +00:00
def __repr__(self):
if not self.osu_name:
return f"<db.Osu {self.osu_id}>"
return f"<db.Osu {self.osu_name}>"
2017-10-09 11:45:42 +00:00
2019-01-09 15:04:57 +00:00
class Discord(Base, Mini):
2017-10-17 18:49:28 +00:00
__tablename__ = "discord"
__table_args__ = tuple(UniqueConstraint("name", "discriminator"))
2018-08-28 13:14:29 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"))
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="discord", lazy="joined")
2017-10-17 18:49:28 +00:00
discord_id = Column(BigInteger, primary_key=True)
2018-08-28 13:14:29 +00:00
name = Column(String)
discriminator = Column(Integer)
2017-10-17 18:49:28 +00:00
avatar_hex = Column(String)
2019-01-09 15:52:34 +00:00
_mini_full_name = "Discord"
_mini_name = "discord"
_mini_order = [discord_id]
2017-10-17 18:49:28 +00:00
def __str__(self):
2018-03-12 12:29:12 +00:00
return f"{self.name}#{self.discriminator}"
2017-10-17 18:49:28 +00:00
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.Discord {self.discord_id}>"
2017-10-27 11:38:32 +00:00
@staticmethod
def create(session: Session, royal_username, discord_user: "DiscordUser"):
2017-10-27 11:38:32 +00:00
d = session.query(Discord).filter(Discord.discord_id == discord_user.id).first()
if d is not None:
raise AlreadyExistingError(repr(d))
r = session.query(Royal).filter(Royal.username == royal_username).first()
if r is None:
raise NotFoundError("No Royal exists with that username")
d = session.query(Discord).filter(Discord.royal_id == r.id).first()
if d is not None:
raise AlreadyExistingError(repr(d))
d = Discord(royal=r,
discord_id=discord_user.id,
name=discord_user.name,
discriminator=discord_user.discriminator,
avatar_hex=discord_user.avatar)
return d
2017-10-17 18:49:28 +00:00
def mention(self):
2018-12-18 16:34:34 +00:00
return f"<@{self.discord_id}>"
2017-10-17 18:49:28 +00:00
def avatar_url(self, size=256):
if self.avatar_hex is None:
return "https://discordapp.com/assets/6debd47ed13483642cf09e832ed0bc1b.png"
2018-03-12 12:29:12 +00:00
return f"https://cdn.discordapp.com/avatars/{self.discord_id}/{self.avatar_hex}.png?size={size}"
2017-10-17 18:49:28 +00:00
2019-01-09 16:19:39 +00:00
@classmethod
def mini_get_all(cls, session: Session):
2019-01-28 19:17:17 +00:00
return [dict(row) for row in session.execute(sql_queries.all_music_query)]
2019-01-09 16:19:39 +00:00
@classmethod
def mini_get_single(cls, session: Session, **kwargs):
2019-01-28 19:17:17 +00:00
return session.execute(sql_queries.one_music_query, {"royal": kwargs["royal"].id}).fetchone()
2019-01-09 16:19:39 +00:00
@classmethod
def mini_get_single_from_royal(cls, session: Session, royal: "Royal"):
return cls.mini_get_single(session, royal=royal)
2017-10-17 18:49:28 +00:00
2019-01-09 15:04:57 +00:00
class Overwatch(Base, Mini):
2017-10-17 18:49:28 +00:00
__tablename__ = "overwatch"
royal_id = Column(Integer, ForeignKey("royals.id"), nullable=False)
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="overwatch", lazy="joined")
2017-10-17 18:49:28 +00:00
battletag = Column(String, primary_key=True)
discriminator = Column(Integer, primary_key=True)
2018-08-28 13:14:29 +00:00
icon = Column(String)
level = Column(Integer)
2017-10-17 18:49:28 +00:00
rank = Column(Integer)
2019-01-09 15:52:34 +00:00
_mini_full_name = "Overwatch"
_mini_name = "ow"
_mini_order = [rank.desc().nullslast(), level.desc()]
2017-10-17 18:49:28 +00:00
def __str__(self, separator="#"):
return f"{self.battletag}{separator}{self.discriminator}"
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.Overwatch {self}>"
2017-10-17 18:49:28 +00:00
@staticmethod
2017-10-30 09:46:37 +00:00
def create(session: Session, royal_id, battletag, discriminator=None):
2017-10-17 18:49:28 +00:00
if discriminator is None:
battletag, discriminator = battletag.split("#", 1)
o = session.query(Overwatch).filter_by(battletag=battletag, discriminator=discriminator).first()
if o is not None:
2017-10-27 09:53:05 +00:00
raise AlreadyExistingError(repr(o))
2017-10-17 18:49:28 +00:00
o = Overwatch(royal_id=royal_id,
battletag=battletag,
2018-08-28 13:14:29 +00:00
discriminator=discriminator)
o.update()
2017-10-17 18:49:28 +00:00
return o
2017-10-25 09:09:06 +00:00
def icon_url(self):
return f"https://d1u1mce87gyfbn.cloudfront.net/game/unlocks/{self.icon}.png"
2019-01-02 20:10:54 +00:00
# noinspection PyUnusedLocal
2018-10-07 22:42:45 +00:00
def update(self, session=None):
2017-10-17 18:49:28 +00:00
r = requests.get(f"https://owapi.net/api/v3/u/{self.battletag}-{self.discriminator}/stats", headers={
2018-08-28 13:14:29 +00:00
"User-Agent": "Royal-Bot/4.1",
2017-10-17 18:49:28 +00:00
"From": "ste.pigozzi@gmail.com"
})
2018-12-18 16:34:34 +00:00
r.raise_for_status()
2017-10-17 18:49:28 +00:00
try:
2018-08-28 15:45:07 +00:00
j = r.json()["eu"]["stats"].get("competitive")
if j is None:
2018-12-18 18:45:08 +00:00
logger.debug(f"No stats for {repr(self)}, skipping...")
2018-09-18 22:02:39 +00:00
return
2018-08-28 15:53:58 +00:00
if not j["game_stats"]:
2018-12-18 18:45:08 +00:00
logger.debug(f"No stats for {repr(self)}, skipping...")
2018-09-18 22:02:39 +00:00
return
2018-08-28 15:45:07 +00:00
j = j["overall_stats"]
2017-10-17 18:49:28 +00:00
except TypeError:
2018-12-18 18:45:08 +00:00
logger.debug(f"No stats for {repr(self)}, skipping...")
2018-09-18 22:02:39 +00:00
return
2018-06-13 21:32:26 +00:00
try:
2018-08-28 15:53:58 +00:00
self.icon = re.search(r"https://.+\.cloudfront\.net/game/unlocks/(0x[0-9A-F]+)\.png", j["avatar"]).group(1)
2018-06-13 21:32:26 +00:00
except AttributeError:
2018-12-18 18:45:08 +00:00
logger.debug(f"No icon available for {repr(self)}.")
2017-10-17 18:49:28 +00:00
self.level = j["prestige"] * 100 + j["level"]
self.rank = j["comprank"]
def rank_url(self):
if self.rank < 1500:
n = 1
elif self.rank < 2000:
n = 2
elif self.rank < 2500:
n = 3
elif self.rank < 3000:
n = 4
elif self.rank < 3500:
n = 5
elif self.rank < 4000:
n = 6
else:
n = 7
return f"https://d1u1mce87gyfbn.cloudfront.net/game/rank-icons/season-2/rank-{n}.png"
2019-01-03 13:13:23 +00:00
def rank_name(self):
if self.rank < 1500:
return "Bronze"
elif self.rank < 2000:
return "Silver"
elif self.rank < 2500:
return "Gold"
elif self.rank < 3000:
return "Platinum"
elif self.rank < 3500:
return "Diamond"
elif self.rank < 4000:
return "Master"
else:
return "Grandmaster"
2017-10-19 16:28:28 +00:00
class Diario(Base):
__tablename__ = "diario"
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime, nullable=False)
saver_id = Column(Integer, ForeignKey("telegram.telegram_id"))
2018-07-15 12:41:42 +00:00
saver = relationship("Telegram", foreign_keys=saver_id, backref="diario_saves", lazy="joined")
2017-10-19 16:28:28 +00:00
author_id = Column(Integer, ForeignKey("telegram.telegram_id"))
2018-07-15 12:41:42 +00:00
author = relationship("Telegram", foreign_keys=author_id, backref="diario_authored", lazy="joined")
2018-07-23 20:21:52 +00:00
spoiler = Column(Boolean, default=False)
2017-10-19 16:28:28 +00:00
text = Column(String)
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.Diario {self.id}>"
2017-10-19 16:28:28 +00:00
def __str__(self):
return f"{self.id} - {self.timestamp} - {self.author}: {self.text}"
def to_telegram(self):
2019-02-08 00:44:48 +00:00
return '<a href="https://ryg.steffo.eu/diario#entry-{id}">#{id}</a> di <b>{author}</b>\n{text}'.format(
id=self.id,
author=self.author,
text=escape(self.text))
def to_html(self):
return str(escape(self.text)).replace("\n", "<br>")
2017-10-19 16:28:28 +00:00
@staticmethod
def import_from_json(file):
import json
2017-10-30 09:46:37 +00:00
session = Session()
2017-10-19 16:28:28 +00:00
file = open(file, "r")
j = json.load(file)
2018-01-25 14:24:17 +00:00
author_ids = {
"@Steffo": 25167391,
"@GoodBalu": 19611986,
"@gattopandacorno": 200821462,
"@Albertino04": 131057096,
"@Francesco_Cuoghi": 48371848,
"@VenomousDoc": 48371848,
"@MaxSensei": 1258401,
"@Protoh": 125711787,
"@McspKap": 304117728,
"@FrankRekt": 31436195,
"@EvilBalu": 26842090,
"@Dailir": 135816455,
"@Paltri": 186843362,
"@Doom_darth_vader": 165792255,
"@httpIma": 292086686,
"@DavidoMessori": 509208316,
"@DavidoNiichan": 509208316,
"@Peraemela99": 63804599,
"@infopz": 20403805,
"@Baithoven": 121537369,
"@Tauei": 102833717
}
for n, entry in enumerate(j):
author = author_ids[entry["sender"]] if "sender" in entry and entry["sender"] in author_ids else None
2017-10-19 16:28:28 +00:00
d = Diario(timestamp=datetime.datetime.fromtimestamp(float(entry["timestamp"])),
2018-01-25 14:24:17 +00:00
author_id=author,
2017-10-19 16:28:28 +00:00
text=entry["text"])
2018-01-25 14:24:17 +00:00
print(f"{n} - {d}")
2017-10-19 16:28:28 +00:00
session.add(d)
session.commit()
2017-10-30 09:46:37 +00:00
session.close()
2017-10-19 16:28:28 +00:00
2018-01-25 14:24:17 +00:00
class BaluRage(Base):
__tablename__ = "balurage"
id = Column(Integer, primary_key=True)
royal_id = Column(Integer, ForeignKey("royals.id"))
2018-07-15 12:41:42 +00:00
royal = relationship("Royal", backref="times_raged", lazy="joined")
2018-01-25 14:24:17 +00:00
reason = Column(String)
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.BaluRage {self.id}>"
2018-01-25 14:24:17 +00:00
2018-03-11 19:03:21 +00:00
class PlayedMusic(Base):
__tablename__ = "playedmusic"
id = Column(Integer, primary_key=True)
2018-03-12 12:29:12 +00:00
enqueuer_id = Column(BigInteger, ForeignKey("discord.discord_id"))
2018-07-15 12:41:42 +00:00
enqueuer = relationship("Discord", backref="music_played", lazy="joined")
2018-03-11 19:03:21 +00:00
filename = Column(String)
2018-07-26 17:26:03 +00:00
timestamp = Column(DateTime, nullable=False)
2018-03-11 19:03:21 +00:00
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.PlayedMusic {self.filename}>"
2018-03-11 19:03:21 +00:00
2018-03-14 10:53:26 +00:00
class VoteQuestion(Base):
__tablename__ = "votequestion"
id = Column(Integer, primary_key=True)
message_id = Column(BigInteger)
question = Column(String, nullable=False)
anonymous = Column(Boolean, nullable=False)
open = Column(Boolean, default=True)
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.Vote {self.id}>"
2018-03-14 10:53:26 +00:00
def generate_text(self, session: Session):
text = f"<b>{self.question}</b>\n\n"
none, yes, no, abstain = 0, 0, 0, 0
2018-03-14 15:28:49 +00:00
if self.message_id is not None:
2019-01-25 19:35:14 +00:00
query = session.execute(sql_queries.vote_answers, {"message_id": self.message_id})
2018-03-14 15:28:49 +00:00
for record in query:
if record["username"] == "royalgamesbot":
continue
elif record["question_id"] is None:
text += "⚪️"
none += 1
elif record["choice"] == "YES":
text += "🔵"
yes += 1
elif record["choice"] == "NO":
text += "🔴"
no += 1
elif record["choice"] == "ABSTAIN":
text += "⚫️"
abstain += 1
if not self.anonymous:
text += f" {str(record['username'])}\n"
if self.anonymous:
text += "\n"
text += f"\n" \
f"{none}\n" \
f"🔵 {yes}\n" \
f"🔴 {no}\n" \
f"⚫️ {abstain}"
2018-03-14 10:53:26 +00:00
return text
class VoteChoices(enum.Enum):
ABSTAIN = 1
YES = 2
NO = 3
class VoteAnswer(Base):
__tablename__ = "voteanswer"
question_id = Column(Integer, ForeignKey("votequestion.id"))
2018-07-15 12:41:42 +00:00
question = relationship("VoteQuestion", backref="answers", lazy="joined")
2018-03-14 10:53:26 +00:00
user_id = Column(BigInteger, ForeignKey("telegram.telegram_id"))
2018-07-15 12:41:42 +00:00
user = relationship("Telegram", backref="votes_cast", lazy="joined")
2018-03-14 10:53:26 +00:00
choice = Column(Enum(VoteChoices), nullable=False)
__table_args__ = (PrimaryKeyConstraint("question_id", "user_id"),)
def __repr__(self):
2018-09-17 22:07:00 +00:00
return f"<db.VoteAnswer {self.question_id} {self.user} {self.choice}>"
2018-03-14 10:53:26 +00:00
2018-08-01 16:05:18 +00:00
class ProfileData(Base):
__tablename__ = "profiledata"
2018-06-05 10:31:11 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"), primary_key=True)
2018-08-01 16:05:18 +00:00
royal = relationship("Royal", backref="profile_data", uselist=False, lazy="joined")
2018-06-05 10:31:11 +00:00
2018-08-01 16:05:18 +00:00
css = Column(Text)
bio = Column(Text)
2018-06-05 10:31:11 +00:00
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<ProfileData for {self.royal.username}>"
2018-06-05 10:31:11 +00:00
2018-07-15 12:41:42 +00:00
class WikiEntry(Base):
__tablename__ = "wikientries"
key = Column(String, primary_key=True)
content = Column(Text, nullable=False)
locked = Column(Boolean, default=False)
2018-07-15 12:41:42 +00:00
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<WikiEntry {self.key}>"
2018-07-15 12:41:42 +00:00
class WikiLog(Base):
__tablename__ = "wikilog"
edit_id = Column(Integer, primary_key=True)
editor_id = Column(Integer, ForeignKey("royals.id"), nullable=False)
editor = relationship("Royal", backref="wiki_edits", lazy="joined")
edited_key = Column(String, ForeignKey("wikientries.key"), nullable=False)
edited = relationship("WikiEntry", backref="edit_logs", lazy="joined")
timestamp = Column(DateTime, nullable=False)
reason = Column(Text)
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<WikiLog {self.edit_id}>"
2018-07-15 12:41:42 +00:00
2018-07-23 16:45:43 +00:00
class Event(Base):
__tablename__ = "events"
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("royals.id"), nullable=False)
2018-07-24 17:23:19 +00:00
author = relationship("Royal", lazy="joined")
2018-07-23 16:45:43 +00:00
name = Column(String, nullable=False)
description = Column(Text)
time = Column(DateTime, nullable=False)
@hybrid_property
def time_left(self) -> datetime.timedelta:
return self.time - datetime.datetime.now()
@time_left.setter
def time_left(self, value):
if not isinstance(value, datetime.timedelta):
raise TypeError("time_left should be a datetime.timedelta")
self.time = datetime.datetime.now() + value
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<Event {self.name}>"
2018-07-23 16:45:43 +00:00
2018-09-10 23:06:08 +00:00
class Reddit(Base):
__tablename__ = "reddit"
royal_id = Column(Integer, ForeignKey("royals.id"))
royal = relationship("Royal", backref="reddit", lazy="joined")
2018-09-11 00:02:09 +00:00
username = Column(String, primary_key=True)
2018-09-10 23:06:08 +00:00
karma = Column(BigInteger)
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<Reddit u/{self.username}>"
2018-09-10 23:06:08 +00:00
2018-09-05 17:48:34 +00:00
class ParsedRedditPost(Base):
__tablename__ = "parsedredditposts"
id = Column(String, primary_key=True)
2018-09-10 23:06:08 +00:00
author_username = Column(String)
2018-09-05 17:48:34 +00:00
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<ParsedRedditPost {self.id}>"
2018-09-05 17:48:34 +00:00
2018-09-13 21:51:06 +00:00
class LoginToken(Base):
__tablename__ = "logintoken"
royal_id = Column(Integer, ForeignKey("royals.id"))
royal = relationship("Royal", backref="tokens", lazy="joined")
token = Column(String, primary_key=True)
expiration = Column(DateTime, nullable=False)
2018-09-17 22:07:00 +00:00
def __repr__(self):
return f"<LoginToken for {self.royal.username}>"
2018-09-13 21:51:06 +00:00
2019-01-09 15:04:57 +00:00
class Halloween(Base, Mini):
2018-10-01 15:46:06 +00:00
"""This is some nice spaghetti, don't you think?"""
__tablename__ = "halloween"
2018-09-27 10:52:41 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"), primary_key=True)
2018-10-01 15:46:06 +00:00
royal = relationship("Royal", backref="halloween", lazy="joined")
first_trigger = Column(DateTime)
puzzle_piece_a = Column(DateTime)
puzzle_piece_b = Column(DateTime)
puzzle_piece_c = Column(DateTime)
puzzle_piece_d = Column(DateTime)
puzzle_piece_e = Column(DateTime)
puzzle_piece_f = Column(DateTime)
puzzle_piece_g = Column(DateTime)
boss_battle = Column(DateTime)
2019-01-09 15:52:34 +00:00
_mini_full_name = "Halloween 2018"
_mini_name = "halloween2018"
_mini_order = [first_trigger]
2018-10-03 17:43:56 +00:00
def __getitem__(self, item):
if not isinstance(item, int):
raise TypeError("The index should be an int")
if item == 1:
return self.puzzle_piece_a
elif item == 2:
return self.puzzle_piece_b
elif item == 3:
return self.puzzle_piece_c
elif item == 4:
return self.puzzle_piece_d
elif item == 5:
return self.puzzle_piece_e
elif item == 6:
return self.puzzle_piece_f
elif item == 7:
return self.puzzle_piece_g
else:
raise ValueError("No such puzzle piece")
def __setitem__(self, key, value):
if not isinstance(key, int):
raise TypeError("The index should be an int")
if key == 1:
self.puzzle_piece_a = value
elif key == 2:
self.puzzle_piece_b = value
elif key == 3:
self.puzzle_piece_c = value
elif key == 4:
self.puzzle_piece_d = value
elif key == 5:
self.puzzle_piece_e = value
elif key == 6:
self.puzzle_piece_f = value
elif key == 7:
self.puzzle_piece_g = value
else:
raise ValueError("No such puzzle piece")
2018-10-01 15:46:06 +00:00
def pieces_completed(self) -> int:
count = 0
2018-10-07 19:26:53 +00:00
for i in range(1, 8):
2018-10-03 17:43:56 +00:00
if self[i]:
count += 1
2018-10-01 15:46:06 +00:00
return count
2018-09-27 10:52:41 +00:00
2018-10-04 10:46:15 +00:00
@staticmethod
2018-10-07 15:19:42 +00:00
def puzzle_status() -> typing.Tuple[bool, typing.List[bool]]:
2018-10-02 22:18:41 +00:00
session = Session()
2018-10-01 16:21:39 +00:00
halloweens = session.query(Halloween).all()
session.close()
2018-10-07 19:26:53 +00:00
completed = [False for _ in range(7)]
2018-10-07 21:22:57 +00:00
started = False
2018-10-03 17:43:56 +00:00
for h in halloweens:
2018-10-12 18:28:59 +00:00
if h.royal.role == "Affiliato":
2018-10-12 18:27:05 +00:00
continue
2018-10-12 21:11:01 +00:00
if h.royal.username == "Steffo":
continue
2018-10-07 19:26:53 +00:00
for i in range(7):
2018-10-07 21:26:36 +00:00
if h.first_trigger is not None:
2018-10-07 21:22:57 +00:00
started = True
2018-10-07 21:24:16 +00:00
if h[i+1]:
2018-10-03 17:43:56 +00:00
completed[i] = True
2018-10-07 15:19:42 +00:00
return started, completed
2018-09-27 10:52:41 +00:00
2018-11-18 16:38:02 +00:00
class ActivityReport(Base):
__tablename__ = "activityreports"
timestamp = Column(DateTime, primary_key=True)
discord_members_online = Column(Integer)
discord_members_ingame = Column(Integer)
discord_cv = Column(Integer)
discord_members_cv = Column(Integer)
discord_channels_used = Column(Integer)
def __repr__(self):
return f"<ActivityReport at {self.timestamp.isoformat()}>"
class Quest(Base):
__tablename__ = "quests"
id = Column(Integer, primary_key=True)
title = Column(String)
description = Column(Text)
reward = Column(Integer)
expiration_date = Column(DateTime)
def __repr__(self):
return f"<Quest {self.id}: {self.title}>"
2019-01-09 15:04:57 +00:00
class Terraria13(Base, Mini):
2019-01-08 14:54:45 +00:00
__tablename__ = "terraria13"
2019-01-09 12:54:26 +00:00
game_name = "Terraria 13"
2019-01-08 14:54:45 +00:00
royal_id = Column(Integer, ForeignKey("royals.id"), primary_key=True)
royal = relationship("Royal", backref="terraria13", lazy="joined")
character_name = Column(String)
contribution = Column(Integer)
2019-01-09 15:52:34 +00:00
_mini_full_name = "Terraria 13"
_mini_name = "terraria13"
_mini_order = [contribution.desc()]
2019-01-09 12:54:26 +00:00
def __repr__(self):
return f"<Terraria13 {self.character_name} {self.contribution}>"
2019-01-09 16:07:35 +00:00
mini_list = [Royal, Telegram, Steam, Dota, LeagueOfLegends, Osu, Discord, Overwatch, Halloween,
2019-01-09 15:52:34 +00:00
Terraria13]
2019-01-23 22:11:17 +00:00
class Match(Base):
__tablename__ = "matches"
2019-01-23 18:41:29 +00:00
id = Column(Integer, primary_key=True)
2019-01-23 22:11:17 +00:00
timestamp = Column(DateTime)
creator_id = Column(BigInteger, ForeignKey("telegram.telegram_id"))
creator = relationship("Telegram", backref="matches_created", lazy="joined")
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
match_title = Column(String)
match_desc = Column(Text)
2019-01-23 18:41:29 +00:00
min_players = Column(Integer)
max_players = Column(Integer)
2019-01-23 22:11:17 +00:00
closed = Column(Boolean, default=False)
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
message_id = Column(BigInteger)
def active_players_count(self):
count = 0
for player in self.players:
2019-01-25 14:28:47 +00:00
if player.status == MatchmakingStatus.READY \
or player.status == MatchmakingStatus.WAIT_FOR_ME \
or player.status == MatchmakingStatus.SOMEONE_ELSE:
2019-01-23 22:11:17 +00:00
count += 1
return count
def generate_text(self, session):
player_list = session.query(MatchPartecipation).filter_by(match=self).all()
title = f"<b>{self.match_title}</b>"
2019-01-25 14:28:47 +00:00
description = f"{self.match_desc}\n" if self.match_desc else ""
2019-01-23 22:11:17 +00:00
if self.min_players:
minimum = f" <i>(minimo {self.min_players})</i>"
else:
minimum = ""
plist = f"Giocatori{minimum}:\n"
ignore_count = 0
for player in player_list:
2019-01-24 20:31:01 +00:00
if player.status == MatchmakingStatus.READY:
2019-01-23 22:11:17 +00:00
icon = "🔵"
2019-01-24 20:31:01 +00:00
elif player.status == MatchmakingStatus.WAIT_FOR_ME:
icon = "🕒"
elif player.status == MatchmakingStatus.MAYBE:
icon = ""
elif player.status == MatchmakingStatus.SOMEONE_ELSE:
icon = "💬"
2019-01-23 22:11:17 +00:00
elif player.status == MatchmakingStatus.IGNORED:
ignore_count += 1
continue
else:
continue
plist += f"{icon} {player.user.royal.username}\n"
if ignore_count:
2019-01-25 14:28:47 +00:00
ignored = f"❌ <i>{ignore_count} persone non sono interessate.</i>\n"
2019-01-23 22:11:17 +00:00
else:
ignored = ""
if self.max_players:
players = f"[{self.active_players_count()}/{self.max_players}]"
else:
players = f"[{self.active_players_count()}]"
2019-01-25 14:28:47 +00:00
close = f"[matchmaking terminato]\n" if self.closed else ""
2019-01-23 22:11:17 +00:00
message = f"{title} {players}\n" \
f"{description}\n" \
f"{plist}\n" \
f"{ignored}" \
f"{close}"
return message
def __repr__(self):
return f"<Match {self.match_title}>"
2019-01-23 18:41:29 +00:00
2019-01-25 14:28:47 +00:00
def format_dict(self) -> typing.Dict[str, str]:
return {
"id": self.id,
"timestamp": self.timestamp.isoformat(),
"creator_id": self.creator_id,
"creator_name": self.creator.mention(),
"match_title": self.match_title,
"match_desc": self.match_desc,
"min_players": self.min_players,
"max_players": self.max_players,
"active_players": self.active_players_count(),
2019-01-25 20:20:27 +00:00
"players": len(self.players)
2019-01-25 14:28:47 +00:00
}
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
class MatchmakingStatus(enum.IntEnum):
WAIT_FOR_ME = 1
READY = 2
2019-01-24 20:31:01 +00:00
MAYBE = 3
SOMEONE_ELSE = 4
2019-01-23 22:11:17 +00:00
IGNORED = -1
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
class MatchPartecipation(Base):
__tablename__ = "matchpartecipations"
__table_args__ = (PrimaryKeyConstraint("user_id", "match_id"),)
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
user_id = Column(BigInteger, ForeignKey("telegram.telegram_id"))
user = relationship("Telegram", backref="match_partecipations", lazy="joined")
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
match_id = Column(Integer, ForeignKey("matches.id"))
match = relationship("Match", backref="players", lazy="joined")
2019-01-23 18:41:29 +00:00
2019-01-23 22:11:17 +00:00
status = Column(Integer)
def __repr__(self):
return f"<MatchPartecipation {self.user.username} in {self.match.match_title}>"
2019-01-23 18:41:29 +00:00
2019-02-06 19:11:10 +00:00
class BindingOfIsaac(Base, Mini):
__tablename__ = "bindingofisaac"
steam_id = Column(String, ForeignKey("steam.steam_id"), primary_key=True)
steam = relationship("Steam", backref="binding_of_isaac", lazy="joined")
daily_victories = Column(Integer, default=0)
def __repr__(self):
return f"<db.BindingOfIsaac {self.steam_id}>"
def recalc_victories(self):
raise NotImplementedError() # TODO
class BindingOfIsaacRun(Base):
__tablename__ = "bindingofisaacruns"
__table_args__ = (PrimaryKeyConstraint("date", "player_id"),)
date = Column(Date)
player_id = Column(String, ForeignKey("bindingofisaac.steam_id"))
player = relationship("BindingOfIsaac", backref="runs", lazy="joined")
score = Column(BigInteger)
# time = Column(???)
def __repr__(self):
return f"<db.BindingOfIsaacRun {self.steam_id}: {self.score}>"
2017-10-04 16:51:40 +00:00
# If run as script, create all the tables in the db
if __name__ == "__main__":
print("Creating new tables...")
Base.metadata.create_all(bind=engine)
2018-07-15 12:41:42 +00:00
print("Done!")