mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-25 17:57:17 +00:00
some party impl
This commit is contained in:
parent
2d84459c4d
commit
4ff985bf81
75
migrations/20190916_01_zGTB1-party-id.py
Normal file
75
migrations/20190916_01_zGTB1-party-id.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
'''
|
||||||
|
party id
|
||||||
|
'''
|
||||||
|
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from yoyo import step
|
||||||
|
|
||||||
|
__depends__ = {'20190830_01_sYYZL-init-tables', '20190910_01_tgBmx-users-table'}
|
||||||
|
party_id = ''.join(random.sample(string.ascii_letters, 16))
|
||||||
|
|
||||||
|
steps = [
|
||||||
|
step('''create table players2 (
|
||||||
|
party_id text not null,
|
||||||
|
player_id integer primary key,
|
||||||
|
created integer not null,
|
||||||
|
nick text not null,
|
||||||
|
job text not null,
|
||||||
|
bis_link text,
|
||||||
|
priority integer not null default 1
|
||||||
|
)'''),
|
||||||
|
# not safe for injections, but sqlite and psycopg have different placeholders for parameters
|
||||||
|
step('''insert into players2 select '%s' as party_id, players.* from players''' % (party_id,)),
|
||||||
|
step('''drop index if exists players_nick_job_idx'''),
|
||||||
|
step('''create unique index players_nick_job_idx on players2(party_id, nick, job)'''),
|
||||||
|
|
||||||
|
step('''create table loot2 (
|
||||||
|
loot_id integer primary key,
|
||||||
|
player_id integer not null,
|
||||||
|
created integer not null,
|
||||||
|
piece text not null,
|
||||||
|
is_tome integer not null,
|
||||||
|
foreign key (player_id) references players2(player_id) on delete cascade
|
||||||
|
)'''),
|
||||||
|
step('''insert into loot2 select * from loot'''),
|
||||||
|
step('''drop index if exists loot_owner_idx'''),
|
||||||
|
step('''create index loot_owner_idx on loot(player_id)'''),
|
||||||
|
|
||||||
|
step('''create table bis2 (
|
||||||
|
player_id integer not null,
|
||||||
|
created integer not null,
|
||||||
|
piece text not null,
|
||||||
|
is_tome integer not null,
|
||||||
|
foreign key (player_id) references players2(player_id) on delete cascade
|
||||||
|
)'''),
|
||||||
|
step('''insert into bis2 select * from bis'''),
|
||||||
|
step('''drop index if exists bis_piece_player_id_idx'''),
|
||||||
|
step('''create unique index bis_piece_player_id_idx on bis2(player_id, piece)'''),
|
||||||
|
|
||||||
|
step('''create table users2 (
|
||||||
|
party_id text not null,
|
||||||
|
user_id integer primary key,
|
||||||
|
username text not null,
|
||||||
|
password text not null,
|
||||||
|
permission text not null,
|
||||||
|
foreign key (party_id) references players2(party_id) on delete cascade
|
||||||
|
)'''),
|
||||||
|
# not safe for injections, but sqlite and psycopg have different placeholders for parameters
|
||||||
|
step('''insert into users2 select '%s' as party_id, users.* from users''' % (party_id,)),
|
||||||
|
step('''drop index if exists users_username_idx'''),
|
||||||
|
step('''create unique index users_username_idx on users2(party_id, username)'''),
|
||||||
|
|
||||||
|
step('''drop table users'''),
|
||||||
|
step('''alter table users2 rename to users'''),
|
||||||
|
|
||||||
|
step('''drop table loot'''),
|
||||||
|
step('''alter table loot2 rename to loot'''),
|
||||||
|
|
||||||
|
step('''drop table bis'''),
|
||||||
|
step('''alter table bis2 rename to bis'''),
|
||||||
|
|
||||||
|
step('''drop table players'''),
|
||||||
|
step('''alter table players2 rename to players''')
|
||||||
|
]
|
@ -7,4 +7,4 @@ priority = is_required loot_count_bis loot_priority loot_count loot_count_total
|
|||||||
[web]
|
[web]
|
||||||
host = 0.0.0.0
|
host = 0.0.0.0
|
||||||
port = 8000
|
port = 8000
|
||||||
templates = templates
|
templates = /home/arcanis/Documents/github/ffxivbis/templates
|
@ -8,7 +8,7 @@
|
|||||||
#
|
#
|
||||||
from aiohttp.web import middleware, Request, Response
|
from aiohttp.web import middleware, Request, Response
|
||||||
from aiohttp_security import AbstractAuthorizationPolicy, check_permission
|
from aiohttp_security import AbstractAuthorizationPolicy, check_permission
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional, Tuple
|
||||||
|
|
||||||
from ffxivbis.core.database import Database
|
from ffxivbis.core.database import Database
|
||||||
|
|
||||||
@ -18,12 +18,19 @@ class AuthorizationPolicy(AbstractAuthorizationPolicy):
|
|||||||
def __init__(self, database: Database) -> None:
|
def __init__(self, database: Database) -> None:
|
||||||
self.database = database
|
self.database = database
|
||||||
|
|
||||||
|
def split_identity(self, identity: str) -> Tuple[str, str]:
|
||||||
|
# identity is party_id + username
|
||||||
|
party_id, username = identity.split('+')
|
||||||
|
return party_id, username
|
||||||
|
|
||||||
async def authorized_userid(self, identity: str) -> Optional[str]:
|
async def authorized_userid(self, identity: str) -> Optional[str]:
|
||||||
user = await self.database.get_user(identity)
|
party_id, username = self.split_identity(identity)
|
||||||
return identity if user is not None else None
|
user = await self.database.get_user(party_id, username)
|
||||||
|
return username if user is not None else None
|
||||||
|
|
||||||
async def permits(self, identity: str, permission: str, context: str = None) -> bool:
|
async def permits(self, identity: str, permission: str, context: str = None) -> bool:
|
||||||
user = await self.database.get_user(identity)
|
party_id, username = self.split_identity(identity)
|
||||||
|
user = await self.database.get_user(party_id, username)
|
||||||
if user is None:
|
if user is None:
|
||||||
return False
|
return False
|
||||||
if user.username != identity:
|
if user.username != identity:
|
||||||
|
@ -25,21 +25,21 @@ from .views.html.users import UsersHtmlView
|
|||||||
|
|
||||||
def setup_routes(app: Application) -> None:
|
def setup_routes(app: Application) -> None:
|
||||||
# api routes
|
# api routes
|
||||||
app.router.add_delete('/admin/api/v1/login/{username}', LoginView)
|
app.router.add_delete('/admin/api/v1/{party_id}/login/{username}', LoginView)
|
||||||
app.router.add_post('/api/v1/login', LoginView)
|
app.router.add_post('/api/v1/{party_id}/login', LoginView)
|
||||||
app.router.add_post('/api/v1/logout', LogoutView)
|
app.router.add_post('/api/v1/{party_id}/logout', LogoutView)
|
||||||
app.router.add_put('/admin/api/v1/login', LoginView)
|
app.router.add_put('/admin/api/v1/{party_id}/login', LoginView)
|
||||||
|
|
||||||
app.router.add_get('/api/v1/party', PlayerView)
|
app.router.add_get('/api/v1/party/{party_id}', PlayerView)
|
||||||
app.router.add_post('/api/v1/party', PlayerView)
|
app.router.add_post('/api/v1/party/{party_id}', PlayerView)
|
||||||
|
|
||||||
app.router.add_get('/api/v1/party/bis', BiSView)
|
app.router.add_get('/api/v1/party/{party_id}/bis', BiSView)
|
||||||
app.router.add_post('/api/v1/party/bis', BiSView)
|
app.router.add_post('/api/v1/party/{party_id}/bis', BiSView)
|
||||||
app.router.add_put('/api/v1/party/bis', BiSView)
|
app.router.add_put('/api/v1/party/{party_id}/bis', BiSView)
|
||||||
|
|
||||||
app.router.add_get('/api/v1/party/loot', LootView)
|
app.router.add_get('/api/v1/party/{party_id}/loot', LootView)
|
||||||
app.router.add_post('/api/v1/party/loot', LootView)
|
app.router.add_post('/api/v1/party/{party_id}/loot', LootView)
|
||||||
app.router.add_put('/api/v1/party/loot', LootView)
|
app.router.add_put('/api/v1/party/{party_id}/loot', LootView)
|
||||||
|
|
||||||
# html routes
|
# html routes
|
||||||
app.router.add_get('/', IndexHtmlView)
|
app.router.add_get('/', IndexHtmlView)
|
||||||
@ -48,19 +48,19 @@ def setup_routes(app: Application) -> None:
|
|||||||
app.router.add_get('/api-docs', ApiHtmlView)
|
app.router.add_get('/api-docs', ApiHtmlView)
|
||||||
app.router.add_get('/api-docs/swagger.json', ApiDocVIew)
|
app.router.add_get('/api-docs/swagger.json', ApiDocVIew)
|
||||||
|
|
||||||
app.router.add_get('/party', PlayerHtmlView)
|
app.router.add_get('/party/{party_id}', PlayerHtmlView)
|
||||||
app.router.add_post('/party', PlayerHtmlView)
|
app.router.add_post('/party/{party_id}', PlayerHtmlView)
|
||||||
|
|
||||||
app.router.add_get('/bis', BiSHtmlView)
|
app.router.add_get('/bis/{party_id}', BiSHtmlView)
|
||||||
app.router.add_post('/bis', BiSHtmlView)
|
app.router.add_post('/bis/{party_id}', BiSHtmlView)
|
||||||
|
|
||||||
app.router.add_get('/loot', LootHtmlView)
|
app.router.add_get('/loot/{party_id}', LootHtmlView)
|
||||||
app.router.add_post('/loot', LootHtmlView)
|
app.router.add_post('/loot/{party_id}', LootHtmlView)
|
||||||
|
|
||||||
app.router.add_get('/suggest', LootSuggestHtmlView)
|
app.router.add_get('/suggest/{party_id}', LootSuggestHtmlView)
|
||||||
app.router.add_post('/suggest', LootSuggestHtmlView)
|
app.router.add_post('/suggest/{party_id}', LootSuggestHtmlView)
|
||||||
|
|
||||||
app.router.add_get('/admin/users', UsersHtmlView)
|
app.router.add_get('/admin/users/{party_id}', UsersHtmlView)
|
||||||
app.router.add_post('/admin/users', UsersHtmlView)
|
app.router.add_post('/admin/users/{party_id}', UsersHtmlView)
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,12 +126,13 @@ class LoginView(LoginBaseView, OpenApi):
|
|||||||
except Exception:
|
except Exception:
|
||||||
data = dict(await self.request.post())
|
data = dict(await self.request.post())
|
||||||
|
|
||||||
required = ['username', 'password']
|
required = ['username', 'password', 'party_id']
|
||||||
if any(param not in data for param in required):
|
if any(param not in data for param in required):
|
||||||
return wrap_invalid_param(required, data)
|
return wrap_invalid_param(required, data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.create_user(data['username'], data['password'], data.get('permission', 'get'))
|
await self.create_user(data['party_id'], data['username'],
|
||||||
|
data['password'], data.get('permission', 'get'))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.request.app.logger.exception('cannot create user')
|
self.request.app.logger.exception('cannot create user')
|
||||||
return wrap_exception(e, data)
|
return wrap_exception(e, data)
|
||||||
|
@ -21,8 +21,8 @@ class LoginBaseView(View):
|
|||||||
return False
|
return False
|
||||||
return md5_crypt.verify(password, user.password)
|
return md5_crypt.verify(password, user.password)
|
||||||
|
|
||||||
async def create_user(self, username: str, password: str, permission: str) -> None:
|
async def create_user(self, party_id: str, username: str, password: str, permission: str) -> None:
|
||||||
await self.request.app['database'].insert_user(User(username, password, permission), False)
|
await self.request.app['database'].insert_user(party_id, User(username, password, permission), False)
|
||||||
|
|
||||||
async def login(self, username: str, password: str) -> None:
|
async def login(self, username: str, password: str) -> None:
|
||||||
if await self.check_credentials(username, password):
|
if await self.check_credentials(username, password):
|
||||||
|
@ -16,8 +16,7 @@ from aiohttp_security import CookiesIdentityPolicy
|
|||||||
|
|
||||||
from ffxivbis.core.config import Configuration
|
from ffxivbis.core.config import Configuration
|
||||||
from ffxivbis.core.database import Database
|
from ffxivbis.core.database import Database
|
||||||
from ffxivbis.core.loot_selector import LootSelector
|
from ffxivbis.core.party_aggregator import PartyAggregator
|
||||||
from ffxivbis.core.party import Party
|
|
||||||
|
|
||||||
from .auth import AuthorizationPolicy, authorize_factory
|
from .auth import AuthorizationPolicy, authorize_factory
|
||||||
from .routes import setup_routes
|
from .routes import setup_routes
|
||||||
@ -35,7 +34,7 @@ def run_server(app: web.Application) -> None:
|
|||||||
port=app['config'].getint('web', 'port'),
|
port=app['config'].getint('web', 'port'),
|
||||||
handle_signals=False)
|
handle_signals=False)
|
||||||
|
|
||||||
def setup_service(config: Configuration, database: Database, loot: LootSelector, party: Party) -> web.Application:
|
def setup_service(config: Configuration, database: Database, aggregator: PartyAggregator) -> web.Application:
|
||||||
app = web.Application(logger=logging.getLogger('http'))
|
app = web.Application(logger=logging.getLogger('http'))
|
||||||
app.on_shutdown.append(on_shutdown)
|
app.on_shutdown.append(on_shutdown)
|
||||||
|
|
||||||
@ -62,10 +61,7 @@ def setup_service(config: Configuration, database: Database, loot: LootSelector,
|
|||||||
app.logger.info('setup database')
|
app.logger.info('setup database')
|
||||||
app['database'] = database
|
app['database'] = database
|
||||||
|
|
||||||
app.logger.info('setup loot selector')
|
app.logger.info('setup aggregator')
|
||||||
app['loot'] = loot
|
app['aggregator'] = aggregator
|
||||||
|
|
||||||
app.logger.info('setup party worker')
|
|
||||||
app['party'] = party
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
@ -12,9 +12,7 @@ import logging
|
|||||||
from ffxivbis.api.web import run_server, setup_service
|
from ffxivbis.api.web import run_server, setup_service
|
||||||
from ffxivbis.core.config import Configuration
|
from ffxivbis.core.config import Configuration
|
||||||
from ffxivbis.core.database import Database
|
from ffxivbis.core.database import Database
|
||||||
from ffxivbis.core.loot_selector import LootSelector
|
from ffxivbis.core.party_aggregator import PartyAggregator
|
||||||
from ffxivbis.core.party import Party
|
|
||||||
from ffxivbis.models.user import User
|
|
||||||
|
|
||||||
|
|
||||||
class Application:
|
class Application:
|
||||||
@ -29,13 +27,7 @@ class Application:
|
|||||||
database = loop.run_until_complete(Database.get(self.config))
|
database = loop.run_until_complete(Database.get(self.config))
|
||||||
database.migration()
|
database.migration()
|
||||||
|
|
||||||
party = loop.run_until_complete(Party.get(database))
|
aggregator = PartyAggregator(self.config, database)
|
||||||
|
|
||||||
admin = User(self.config.get('auth', 'root_username'), self.config.get('auth', 'root_password'), 'admin')
|
web = setup_service(self.config, database, aggregator)
|
||||||
loop.run_until_complete(database.insert_user(admin, True))
|
|
||||||
|
|
||||||
priority = self.config.get('settings', 'priority').split()
|
|
||||||
loot_selector = LootSelector(party, priority)
|
|
||||||
|
|
||||||
web = setup_service(self.config, database, loot_selector, party)
|
|
||||||
run_server(web)
|
run_server(web)
|
@ -59,40 +59,43 @@ class Database:
|
|||||||
async def init(self) -> None:
|
async def init(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def delete_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def delete_piece(self, party_id: str, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def delete_piece_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def delete_piece_bis(self, party_id: str, player_id: PlayerId, piece: Piece) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def delete_player(self, player_id: PlayerId) -> None:
|
async def delete_player(self, party_id: str, player_id: PlayerId) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def delete_user(self, username: str) -> None:
|
async def delete_user(self, party_id: str, username: str) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def get_party(self) -> List[Player]:
|
async def get_party(self, party_id: str) -> List[Player]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def get_player(self, player_id: PlayerId) -> Optional[int]:
|
async def get_player(self, party_id: str, player_id: PlayerId) -> Optional[int]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def get_user(self, username: str) -> Optional[User]:
|
async def get_players(self, party_id: str) -> List[int]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def get_users(self) -> List[User]:
|
async def get_user(self, party_id: str, username: str) -> Optional[User]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def insert_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def get_users(self, party_id: str) -> List[User]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def insert_piece_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def insert_piece(self, party_id: str, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def insert_player(self, player: Player) -> None:
|
async def insert_piece_bis(self, party_id: str, player_id: PlayerId, piece: Piece) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def insert_user(self, user: User, hashed_password: bool) -> None:
|
async def insert_player(self, party_id: str, player: Player) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def insert_user(self, party_id: str, user: User, hashed_password: bool) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def migration(self) -> None:
|
def migration(self) -> None:
|
||||||
|
@ -20,8 +20,9 @@ from .database import Database
|
|||||||
|
|
||||||
class Party:
|
class Party:
|
||||||
|
|
||||||
def __init__(self, database: Database) -> None:
|
def __init__(self, party_id: str, database: Database) -> None:
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
|
self.party_id = party_id
|
||||||
self.players: Dict[PlayerId, Player] = {}
|
self.players: Dict[PlayerId, Player] = {}
|
||||||
self.database = database
|
self.database = database
|
||||||
|
|
||||||
@ -31,9 +32,9 @@ class Party:
|
|||||||
return list(self.players.values())
|
return list(self.players.values())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get(cls: Type[Party], database: Database) -> Party:
|
async def get(cls: Type[Party], party_id: str, database: Database) -> Party:
|
||||||
obj = Party(database)
|
obj = cls(party_id, database)
|
||||||
players = await database.get_party()
|
players = await database.get_party(party_id)
|
||||||
for player in players:
|
for player in players:
|
||||||
obj.players[player.player_id] = player
|
obj.players[player.player_id] = player
|
||||||
return obj
|
return obj
|
||||||
@ -42,28 +43,28 @@ class Party:
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
player = self.players[player_id]
|
player = self.players[player_id]
|
||||||
player.link = link
|
player.link = link
|
||||||
await self.database.insert_player(player)
|
await self.database.insert_player(self.party_id, player)
|
||||||
|
|
||||||
async def remove_player(self, player_id: PlayerId) -> Optional[Player]:
|
async def remove_player(self, player_id: PlayerId) -> Optional[Player]:
|
||||||
await self.database.delete_player(player_id)
|
await self.database.delete_player(self.party_id, player_id)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
player = self.players.pop(player_id, None)
|
player = self.players.pop(player_id, None)
|
||||||
return player
|
return player
|
||||||
|
|
||||||
async def set_player(self, player: Player) -> PlayerId:
|
async def set_player(self, player: Player) -> PlayerId:
|
||||||
player_id = player.player_id
|
player_id = player.player_id
|
||||||
await self.database.insert_player(player)
|
await self.database.insert_player(self.party_id, player)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.players[player_id] = player
|
self.players[player_id] = player
|
||||||
return player_id
|
return player_id
|
||||||
|
|
||||||
async def set_item(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def set_item(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
await self.database.insert_piece(player_id, piece)
|
await self.database.insert_piece(self.party_id, player_id, piece)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.players[player_id].loot.append(piece)
|
self.players[player_id].loot.append(piece)
|
||||||
|
|
||||||
async def remove_item(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def remove_item(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
await self.database.delete_piece(player_id, piece)
|
await self.database.delete_piece(self.party_id, player_id, piece)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
try:
|
try:
|
||||||
self.players[player_id].loot.remove(piece)
|
self.players[player_id].loot.remove(piece)
|
||||||
@ -71,11 +72,11 @@ class Party:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def set_item_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def set_item_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
||||||
await self.database.insert_piece_bis(player_id, piece)
|
await self.database.insert_piece_bis(self.party_id, player_id, piece)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.players[player_id].bis.set_item(piece)
|
self.players[player_id].bis.set_item(piece)
|
||||||
|
|
||||||
async def remove_item_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def remove_item_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
||||||
await self.database.delete_piece_bis(player_id, piece)
|
await self.database.delete_piece_bis(self.party_id, player_id, piece)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.players[player_id].bis.remove_item(piece)
|
self.players[player_id].bis.remove_item(piece)
|
||||||
|
26
src/ffxivbis/core/party_aggregator.py
Normal file
26
src/ffxivbis/core/party_aggregator.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2019 Evgeniy Alekseev.
|
||||||
|
#
|
||||||
|
# This file is part of ffxivbis
|
||||||
|
# (see https://github.com/arcan1s/ffxivbis).
|
||||||
|
#
|
||||||
|
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
#
|
||||||
|
from .config import Configuration
|
||||||
|
from .database import Database
|
||||||
|
from .loot_selector import LootSelector
|
||||||
|
from .party import Party
|
||||||
|
|
||||||
|
|
||||||
|
class PartyAggregator:
|
||||||
|
|
||||||
|
def __init__(self, config: Configuration, database: Database) -> None:
|
||||||
|
self.config = config
|
||||||
|
self.database = database
|
||||||
|
|
||||||
|
async def get_party(self, party_id: str) -> Party:
|
||||||
|
return await Party.get(party_id, self.database)
|
||||||
|
|
||||||
|
async def get_loot_selector(self, party: Party) -> LootSelector:
|
||||||
|
priority = self.config.get('settings', 'priority').split()
|
||||||
|
return LootSelector(party, priority)
|
@ -42,8 +42,8 @@ class PostgresDatabase(Database):
|
|||||||
self.pool = await asyncpg.create_pool(host=self.host, port=self.port, username=self.username,
|
self.pool = await asyncpg.create_pool(host=self.host, port=self.port, username=self.username,
|
||||||
password=self.password, database=self.database)
|
password=self.password, database=self.database)
|
||||||
|
|
||||||
async def delete_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def delete_piece(self, party_id: str, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -57,8 +57,8 @@ class PostgresDatabase(Database):
|
|||||||
player, piece.name, getattr(piece, 'is_tome', True)
|
player, piece.name, getattr(piece, 'is_tome', True)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def delete_piece_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def delete_piece_bis(self, party_id: str, player_id: PlayerId, piece: Piece) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -67,24 +67,29 @@ class PostgresDatabase(Database):
|
|||||||
'''delete from bis where player_id = $1 and piece = $2''',
|
'''delete from bis where player_id = $1 and piece = $2''',
|
||||||
player, piece.name)
|
player, piece.name)
|
||||||
|
|
||||||
async def delete_player(self, player_id: PlayerId) -> None:
|
async def delete_player(self, party_id: str, player_id: PlayerId) -> None:
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
await conn.execute('''delete from players where nick = $1 and job = $2''',
|
await conn.execute('''delete from players where nick = $1 and job = $2 and party_id = $3''',
|
||||||
player_id.nick, player_id.job.name)
|
player_id.nick, player_id.job.name, party_id)
|
||||||
|
|
||||||
async def delete_user(self, username: str) -> None:
|
async def delete_user(self, party_id: str, username: str) -> None:
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
await conn.execute('''delete from users where username = $1''', username)
|
await conn.execute('''delete from users where username = $1 and party_id = $2''',
|
||||||
|
(username, party_id))
|
||||||
|
|
||||||
|
async def get_party(self, party_id: str) -> List[Player]:
|
||||||
|
players = await self.get_players(party_id)
|
||||||
|
if not players:
|
||||||
|
return []
|
||||||
|
|
||||||
async def get_party(self) -> List[Player]:
|
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
rows = await conn.fetch('''select * from bis''')
|
rows = await conn.fetch('''select * from bis where player_id in $1''', players)
|
||||||
bis_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
bis_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
||||||
|
|
||||||
rows = await conn.fetch('''select * from loot''')
|
rows = await conn.fetch('''select * from loot where player_id in $1''', players)
|
||||||
loot_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
loot_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
||||||
|
|
||||||
rows = await conn.fetch('''select * from players''')
|
rows = await conn.fetch('''select * from players where party_id = $1''', party_id)
|
||||||
party = {
|
party = {
|
||||||
row['player_id']: Player(Job[row['job']], row['nick'], BiS(), [], row['bis_link'], row['priority'])
|
row['player_id']: Player(Job[row['job']], row['nick'], BiS(), [], row['bis_link'], row['priority'])
|
||||||
for row in rows
|
for row in rows
|
||||||
@ -92,24 +97,30 @@ class PostgresDatabase(Database):
|
|||||||
|
|
||||||
return self.set_loot(party, bis_pieces, loot_pieces)
|
return self.set_loot(party, bis_pieces, loot_pieces)
|
||||||
|
|
||||||
async def get_player(self, player_id: PlayerId) -> Optional[int]:
|
async def get_player(self, party_id: str, player_id: PlayerId) -> Optional[int]:
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
player = await conn.fetchrow('''select player_id from players where nick = $1 and job = $2''',
|
player = await conn.fetchrow('''select player_id from players where nick = $1 and job = $2 and party_id = $3''',
|
||||||
player_id.nick, player_id.job.name)
|
player_id.nick, player_id.job.name, party_id)
|
||||||
return player['player_id'] if player is not None else None
|
return player['player_id'] if player is not None else None
|
||||||
|
|
||||||
async def get_user(self, username: str) -> Optional[User]:
|
async def get_players(self, party_id: str) -> List[int]:
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
user = await conn.fetchrow('''select * from users where username = $1''', username)
|
players = await conn.fetch('''select player_id from players where party_id = $1''', (party_id,))
|
||||||
|
return [player['player_id'] for player in players]
|
||||||
|
|
||||||
|
async def get_user(self, party_id: str, username: str) -> Optional[User]:
|
||||||
|
async with self.pool.acquire() as conn:
|
||||||
|
user = await conn.fetchrow('''select * from users where username = $1 and party_id = $2''',
|
||||||
|
username, party_id)
|
||||||
return User(user['username'], user['password'], user['permission']) if user is not None else None
|
return User(user['username'], user['password'], user['permission']) if user is not None else None
|
||||||
|
|
||||||
async def get_users(self) -> List[User]:
|
async def get_users(self, party_id: str) -> List[User]:
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
users = await conn.fetch('''select * from users''')
|
users = await conn.fetch('''select * from users where party_id = $1''', party_id)
|
||||||
return [User(user['username'], user['password'], user['permission']) for user in users]
|
return [User(user['username'], user['password'], user['permission']) for user in users]
|
||||||
|
|
||||||
async def insert_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def insert_piece(self, party_id: str, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -122,8 +133,8 @@ class PostgresDatabase(Database):
|
|||||||
Database.now(), piece.name, getattr(piece, 'is_tome', True), player
|
Database.now(), piece.name, getattr(piece, 'is_tome', True), player
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_piece_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def insert_piece_bis(self, party_id: str, player_id: PlayerId, piece: Piece) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -138,27 +149,27 @@ class PostgresDatabase(Database):
|
|||||||
Database.now(), piece.name, piece.is_tome, player
|
Database.now(), piece.name, piece.is_tome, player
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_player(self, player: Player) -> None:
|
async def insert_player(self, party_id: str, player: Player) -> None:
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
'''insert into players
|
'''insert into players
|
||||||
(created, nick, job, bis_link, priority)
|
(party_id, created, nick, job, bis_link, priority)
|
||||||
values
|
values
|
||||||
($1, $2, $3, $4, $5)
|
($1, $2, $3, $4, $5, $6)
|
||||||
on conflict on constraint players_nick_job_idx do update set
|
on conflict on constraint players_nick_job_idx do update set
|
||||||
created = $1, bis_link = $4, priority = $5''',
|
created = $1, bis_link = $4, priority = $5''',
|
||||||
Database.now(), player.nick, player.job.name, player.link, player.priority
|
Database.now(), player.nick, player.job.name, player.link, player.priority, party_id
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_user(self, user: User, hashed_password: bool) -> None:
|
async def insert_user(self, party_id: str, user: User, hashed_password: bool) -> None:
|
||||||
password = user.password if hashed_password else md5_crypt.hash(user.password)
|
password = user.password if hashed_password else md5_crypt.hash(user.password)
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
'''insert into users
|
'''insert into users
|
||||||
(username, password, permission)
|
(party_id, username, password, permission)
|
||||||
values
|
values
|
||||||
($1, $2, $3)
|
($1, $2, $3, $4)
|
||||||
on conflict on constraint users_username_idx do update set
|
on conflict on constraint users_username_idx do update set
|
||||||
password = $2, permission = $3''',
|
password = $2, permission = $3''',
|
||||||
user.username, password, user.permission
|
party_id, user.username, password, user.permission
|
||||||
)
|
)
|
@ -31,8 +31,8 @@ class SQLiteDatabase(Database):
|
|||||||
def connection(self) -> str:
|
def connection(self) -> str:
|
||||||
return f'sqlite:///{self.database_path}'
|
return f'sqlite:///{self.database_path}'
|
||||||
|
|
||||||
async def delete_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def delete_piece(self, party_id: str, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ class SQLiteDatabase(Database):
|
|||||||
)''',
|
)''',
|
||||||
(player, piece.name, getattr(piece, 'is_tome', True)))
|
(player, piece.name, getattr(piece, 'is_tome', True)))
|
||||||
|
|
||||||
async def delete_piece_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def delete_piece_bis(self, party_id: str, player_id: PlayerId, piece: Piece) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -55,26 +55,32 @@ class SQLiteDatabase(Database):
|
|||||||
'''delete from bis where player_id = ? and piece = ?''',
|
'''delete from bis where player_id = ? and piece = ?''',
|
||||||
(player, piece.name))
|
(player, piece.name))
|
||||||
|
|
||||||
async def delete_player(self, player_id: PlayerId) -> None:
|
async def delete_player(self, party_id: str, player_id: PlayerId) -> None:
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute('''delete from players where nick = ? and job = ?''',
|
await cursor.execute('''delete from players where nick = ? and job = ? and party_id = ?''',
|
||||||
(player_id.nick, player_id.job.name))
|
(player_id.nick, player_id.job.name, party_id))
|
||||||
|
|
||||||
async def delete_user(self, username: str) -> None:
|
async def delete_user(self, party_id: str, username: str) -> None:
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute('''delete from users where username = ?''', (username,))
|
await cursor.execute('''delete from users where username = ? and party_id = ?''',
|
||||||
|
(username, party_id))
|
||||||
|
|
||||||
|
async def get_party(self, party_id: str) -> List[Player]:
|
||||||
|
players = await self.get_players(party_id)
|
||||||
|
if not players:
|
||||||
|
return []
|
||||||
|
placeholder = ', '.join(['?'] * len(players))
|
||||||
|
|
||||||
async def get_party(self) -> List[Player]:
|
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute('''select * from bis''')
|
await cursor.execute('''select * from bis where player_id in ({})'''.format(placeholder), players)
|
||||||
rows = await cursor.fetchall()
|
rows = await cursor.fetchall()
|
||||||
bis_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
bis_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
||||||
|
|
||||||
await cursor.execute('''select * from loot''')
|
await cursor.execute('''select * from loot where player_id in ({})'''.format(placeholder), players)
|
||||||
rows = await cursor.fetchall()
|
rows = await cursor.fetchall()
|
||||||
loot_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
loot_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows]
|
||||||
|
|
||||||
await cursor.execute('''select * from players''')
|
await cursor.execute('''select * from players where party_id = ?''', (party_id,))
|
||||||
rows = await cursor.fetchall()
|
rows = await cursor.fetchall()
|
||||||
party = {
|
party = {
|
||||||
row['player_id']: Player(Job[row['job']], row['nick'], BiS(), [], row['bis_link'], row['priority'])
|
row['player_id']: Player(Job[row['job']], row['nick'], BiS(), [], row['bis_link'], row['priority'])
|
||||||
@ -83,27 +89,34 @@ class SQLiteDatabase(Database):
|
|||||||
|
|
||||||
return self.set_loot(party, bis_pieces, loot_pieces)
|
return self.set_loot(party, bis_pieces, loot_pieces)
|
||||||
|
|
||||||
async def get_player(self, player_id: PlayerId) -> Optional[int]:
|
async def get_player(self, party_id: str, player_id: PlayerId) -> Optional[int]:
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute('''select player_id from players where nick = ? and job = ?''',
|
await cursor.execute('''select player_id from players where nick = ? and job = ? and party_id = ?''',
|
||||||
(player_id.nick, player_id.job.name))
|
(player_id.nick, player_id.job.name, party_id))
|
||||||
player = await cursor.fetchone()
|
player = await cursor.fetchone()
|
||||||
return player['player_id'] if player is not None else None
|
return player['player_id'] if player is not None else None
|
||||||
|
|
||||||
async def get_user(self, username: str) -> Optional[User]:
|
async def get_players(self, party_id: str) -> List[int]:
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute('''select * from users where username = ?''', (username,))
|
await cursor.execute('''select player_id from players where party_id = ?''', (party_id,))
|
||||||
|
players = await cursor.fetchall()
|
||||||
|
return [player['player_id'] for player in players]
|
||||||
|
|
||||||
|
async def get_user(self, party_id: str, username: str) -> Optional[User]:
|
||||||
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
|
await cursor.execute('''select * from users where username = ? and party_id = ?''',
|
||||||
|
(username, party_id))
|
||||||
user = await cursor.fetchone()
|
user = await cursor.fetchone()
|
||||||
return User(user['username'], user['password'], user['permission']) if user is not None else None
|
return User(user['username'], user['password'], user['permission']) if user is not None else None
|
||||||
|
|
||||||
async def get_users(self) -> List[User]:
|
async def get_users(self, party_id: str) -> List[User]:
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute('''select * from users''')
|
await cursor.execute('''select * from users where party_id = ?''', (party_id,))
|
||||||
users = await cursor.fetchall()
|
users = await cursor.fetchall()
|
||||||
return [User(user['username'], user['password'], user['permission']) for user in users]
|
return [User(user['username'], user['password'], user['permission']) for user in users]
|
||||||
|
|
||||||
async def insert_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
async def insert_piece(self, party_id: str, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -116,8 +129,8 @@ class SQLiteDatabase(Database):
|
|||||||
(Database.now(), piece.name, getattr(piece, 'is_tome', True), player)
|
(Database.now(), piece.name, getattr(piece, 'is_tome', True), player)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_piece_bis(self, player_id: PlayerId, piece: Piece) -> None:
|
async def insert_piece_bis(self, party_id: str, player_id: PlayerId, piece: Piece) -> None:
|
||||||
player = await self.get_player(player_id)
|
player = await self.get_player(party_id, player_id)
|
||||||
if player is None:
|
if player is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -130,23 +143,23 @@ class SQLiteDatabase(Database):
|
|||||||
(Database.now(), piece.name, piece.is_tome, player)
|
(Database.now(), piece.name, piece.is_tome, player)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_player(self, player: Player) -> None:
|
async def insert_player(self, party_id: str, player: Player) -> None:
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute(
|
await cursor.execute(
|
||||||
'''replace into players
|
'''replace into players
|
||||||
(created, nick, job, bis_link, priority)
|
(party_id, created, nick, job, bis_link, priority)
|
||||||
values
|
values
|
||||||
(?, ?, ?, ?, ?)''',
|
(?, ?, ?, ?, ?, ?)''',
|
||||||
(Database.now(), player.nick, player.job.name, player.link, player.priority)
|
(party_id, Database.now(), player.nick, player.job.name, player.link, player.priority)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_user(self, user: User, hashed_password: bool) -> None:
|
async def insert_user(self, party_id: str, user: User, hashed_password: bool) -> None:
|
||||||
password = user.password if hashed_password else md5_crypt.hash(user.password)
|
password = user.password if hashed_password else md5_crypt.hash(user.password)
|
||||||
async with SQLiteHelper(self.database_path) as cursor:
|
async with SQLiteHelper(self.database_path) as cursor:
|
||||||
await cursor.execute(
|
await cursor.execute(
|
||||||
'''replace into users
|
'''replace into users
|
||||||
(username, password, permission)
|
(party_id, username, password, permission)
|
||||||
values
|
values
|
||||||
(?, ?, ?)''',
|
(?, ?, ?, ?)''',
|
||||||
(user.username, password, user.permission)
|
(party_id, user.username, password, user.permission)
|
||||||
)
|
)
|
Loading…
Reference in New Issue
Block a user