mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 11:34:18 +00:00
5.9.2
This commit is contained in:
parent
ada3fb1643
commit
2160aca582
19 changed files with 850 additions and 605 deletions
800
poetry.lock
generated
800
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -2,10 +2,10 @@
|
|||
|
||||
[tool.poetry]
|
||||
name = "rpgpack"
|
||||
version = "5.9.1"
|
||||
version = "5.9.2"
|
||||
description = "A Dungeons & Dragons utilities pack for Royalnet"
|
||||
authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"]
|
||||
license = "AGPL-3.0+"
|
||||
license = "AGPL-3.0-or-later"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/Steffo99/rpgpack"
|
||||
classifiers = [
|
||||
|
@ -22,11 +22,18 @@
|
|||
sortedcontainers = "^2.1.0"
|
||||
aiohttp = "^3.5"
|
||||
|
||||
# Required by poetry?!
|
||||
bcrypt = "3.2.0"
|
||||
sqlalchemy = "1.3.19"
|
||||
|
||||
[tool.poetry.dependencies.royalnet]
|
||||
version = "~5.10.0"
|
||||
version = "~5.11.12"
|
||||
extras = [
|
||||
"telegram",
|
||||
"alchemy_easy",
|
||||
"herald",
|
||||
"telegram",
|
||||
"discord",
|
||||
"constellation",
|
||||
]
|
||||
|
||||
# Development dependencies
|
||||
|
|
|
@ -10,18 +10,20 @@ from ...utils import get_targets
|
|||
class DndBattleTargetCommand(rc.Command, abc.ABC):
|
||||
@abc.abstractmethod
|
||||
async def _change(self, unit: DndBattleUnit, args: List[str]):
|
||||
...
|
||||
raise NotImplementedError()
|
||||
|
||||
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None:
|
||||
target = args[0]
|
||||
units = await get_targets(data, target)
|
||||
|
||||
async with data.session_acm() as session:
|
||||
units = await get_targets(target, data=data, session=session)
|
||||
if len(units) == 0:
|
||||
raise rc.InvalidInputError(f"No targets found matching [c]{target}[/c].")
|
||||
|
||||
for unit in units:
|
||||
await self._change(unit, args[1:])
|
||||
|
||||
await data.session_commit()
|
||||
await session.commit()
|
||||
|
||||
message = []
|
||||
for unit in units:
|
||||
|
|
|
@ -16,8 +16,10 @@ class DndactiveCommand(Command):
|
|||
|
||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
identifier = args.optional(0)
|
||||
author = await data.get_author(error_if_none=True)
|
||||
active_character = await get_active_character(data)
|
||||
|
||||
async with data.session_acm() as session:
|
||||
author = await data.find_author(session=session, required=True)
|
||||
active_character = await get_active_character(session=session, data=data)
|
||||
|
||||
DndCharacterT = self.alchemy.get(DndCharacter)
|
||||
DndActiveCharacterT = self.alchemy.get(DndActiveCharacter)
|
||||
|
@ -34,9 +36,11 @@ class DndactiveCommand(Command):
|
|||
try:
|
||||
identifier = int(identifier)
|
||||
except ValueError:
|
||||
chars = await asyncify(data.session.query(DndCharacterT).filter_by(name=identifier).all)
|
||||
chars = await asyncify(session.query(DndCharacterT).filter_by(name=identifier).all)
|
||||
if len(chars) >= 2:
|
||||
char_string = "\n".join([f"[c]{char.character_id}[/c] (LV {char.level}) by {char.creator})" for char in chars])
|
||||
char_string = "\n".join(
|
||||
[f"[c]{char.character_id}[/c] (LV {char.level}) by {char.creator})" for char in chars]
|
||||
)
|
||||
raise CommandError(f"Multiple characters share the name {identifier}, "
|
||||
f"please activate them using their id:\n{char_string}")
|
||||
elif len(chars) == 1:
|
||||
|
@ -45,7 +49,7 @@ class DndactiveCommand(Command):
|
|||
char = None
|
||||
else:
|
||||
# Find the character by id
|
||||
char = await asyncify(data.session.query(DndCharacterT)
|
||||
char = await asyncify(session.query(DndCharacterT)
|
||||
.filter_by(character_id=identifier)
|
||||
.one_or_none)
|
||||
if char is None:
|
||||
|
@ -56,11 +60,11 @@ class DndactiveCommand(Command):
|
|||
achar = DndActiveCharacterT(
|
||||
character=char,
|
||||
user=author,
|
||||
interface_name=self.interface.name,
|
||||
interface_name=data.command.serf.__class__.__name__,
|
||||
interface_data=pickle.dumps(get_interface_data(data)))
|
||||
data.session.add(achar)
|
||||
session.add(achar)
|
||||
else:
|
||||
# Change the active character
|
||||
active_character.character = char
|
||||
await data.session_commit()
|
||||
await asyncify(session.commit)
|
||||
await data.reply(f"✅ Active character set to [b]{char}[/b]!")
|
||||
|
|
|
@ -18,8 +18,9 @@ class DndactivebattleCommand(Command):
|
|||
BattleT = self.alchemy.get(DndBattle)
|
||||
ABattleT = self.alchemy.get(DndActiveBattle)
|
||||
|
||||
async with data.session_acm() as session:
|
||||
identifier = args.joined()
|
||||
active_battle = await get_active_battle(data)
|
||||
active_battle = await get_active_battle(session=session, data=data)
|
||||
|
||||
# Display the active character
|
||||
if identifier == "":
|
||||
|
@ -34,7 +35,7 @@ class DndactivebattleCommand(Command):
|
|||
identifier = int(identifier)
|
||||
except ValueError:
|
||||
# Find the battle by name
|
||||
battles = await asyncify(data.session.query(BattleT).filter_by(name=identifier).all)
|
||||
battles = await asyncify(session.query(BattleT).filter_by(name=identifier).all)
|
||||
if len(battles) >= 2:
|
||||
char_string = "\n".join([f"[c]{battle.id}[/c]" for battle in battles])
|
||||
raise CommandError(f"Multiple battles share the name [b]{identifier}[/b], "
|
||||
|
@ -45,7 +46,7 @@ class DndactivebattleCommand(Command):
|
|||
battle = None
|
||||
else:
|
||||
# Find the battle by id
|
||||
battle = await asyncify(data.session.query(BattleT)
|
||||
battle = await asyncify(session.query(BattleT)
|
||||
.filter_by(id=identifier)
|
||||
.one_or_none)
|
||||
if battle is None:
|
||||
|
@ -55,11 +56,11 @@ class DndactivebattleCommand(Command):
|
|||
# Create a new active battle
|
||||
active_battle = ABattleT(
|
||||
battle=battle,
|
||||
interface_name=self.interface.name,
|
||||
interface_name=data.command.serf.__class__.__name__,
|
||||
interface_data=pickle.dumps(get_interface_data(data)))
|
||||
data.session.add(active_battle)
|
||||
session.add(active_battle)
|
||||
else:
|
||||
# Change the active character
|
||||
active_battle.battle = battle
|
||||
await data.session_commit()
|
||||
await asyncify(session.commit)
|
||||
await data.reply(f"⚔️ [b]{battle}[/b]! Roll initiative!")
|
||||
|
|
|
@ -26,11 +26,12 @@ class DndaddunitCommand(rc.Command):
|
|||
|
||||
DndBattleUnitT = self.alchemy.get(DndBattleUnit)
|
||||
|
||||
active_battle = await get_active_battle(data)
|
||||
async with data.session_acm() as session:
|
||||
active_battle = await get_active_battle(session=session, data=data)
|
||||
if active_battle is None:
|
||||
raise rc.CommandError("No battle is active in this chat.")
|
||||
|
||||
units_with_same_name = await ru.asyncify(data.session.query(DndBattleUnitT).filter_by(
|
||||
units_with_same_name = await ru.asyncify(session.query(DndBattleUnitT).filter_by(
|
||||
name=name,
|
||||
battle=active_battle.battle
|
||||
).all)
|
||||
|
@ -53,8 +54,8 @@ class DndaddunitCommand(rc.Command):
|
|||
battle=active_battle.battle
|
||||
)
|
||||
|
||||
data.session.add(dbu)
|
||||
await data.session_commit()
|
||||
session.add(dbu)
|
||||
await ru.asyncify(session.commit)
|
||||
|
||||
await data.reply(f"{dbu}\n"
|
||||
f"joins the battle!")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import re
|
||||
from royalnet.commands import *
|
||||
import royalnet.utils as ru
|
||||
from .dndnew import DndnewCommand
|
||||
from ..tables import DndCharacter
|
||||
from ..utils import get_active_character
|
||||
|
@ -15,7 +16,8 @@ class DndeditCommand(DndnewCommand):
|
|||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
character_sheet = args.joined()
|
||||
|
||||
active_character = await get_active_character(data)
|
||||
async with data.session_acm() as session:
|
||||
active_character = await get_active_character(session=session, data=data)
|
||||
|
||||
if active_character is None:
|
||||
raise CommandError("You don't have an active character.")
|
||||
|
@ -29,6 +31,6 @@ class DndeditCommand(DndnewCommand):
|
|||
for key in arguments:
|
||||
char.__setattr__(key, arguments[key])
|
||||
|
||||
await data.session_commit()
|
||||
await ru.asyncify(session.commit)
|
||||
|
||||
await data.reply(f"✅ Edit successful!")
|
||||
|
|
|
@ -20,7 +20,8 @@ class DndinfoCommand(Command):
|
|||
}
|
||||
|
||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
active_character = await get_active_character(data)
|
||||
async with data.session_acm() as session:
|
||||
active_character = await get_active_character(data=data, session=session)
|
||||
|
||||
if active_character is None:
|
||||
raise CommandError("You don't have an active character.")
|
||||
|
|
|
@ -2,6 +2,7 @@ import aiohttp
|
|||
import sortedcontainers
|
||||
import logging
|
||||
from royalnet.commands import *
|
||||
import royalnet.serf as rs
|
||||
from royalnet.utils import sentry_exc
|
||||
from ..utils import parse_5etools_entry
|
||||
|
||||
|
@ -20,9 +21,9 @@ class DnditemCommand(Command):
|
|||
|
||||
_dnddata: sortedcontainers.SortedKeyList = None
|
||||
|
||||
def __init__(self, interface: CommandInterface):
|
||||
super().__init__(interface)
|
||||
self.loop.create_task(self._fetch_dnddata())
|
||||
def __init__(self, serf: rs.Serf, config: "ConfigDict"):
|
||||
super().__init__(serf, config)
|
||||
self.serf.tasks.add(self._fetch_dnddata())
|
||||
|
||||
async def _fetch_dnddata(self):
|
||||
self._dnddata = self._dnddata = sortedcontainers.SortedKeyList([], key=lambda i: i["name"].lower())
|
||||
|
|
|
@ -21,19 +21,20 @@ class DndjoinbattleCommand(rc.Command):
|
|||
faction = Faction[args[0].upper()]
|
||||
initiative_mod = int(args.optional(1, default="0"))
|
||||
|
||||
async with data.session_acm() as session:
|
||||
DndBattleUnitT = self.alchemy.get(DndBattleUnit)
|
||||
|
||||
active_battle = await get_active_battle(data)
|
||||
active_battle = await get_active_battle(data=data, session=session)
|
||||
if active_battle is None:
|
||||
raise rc.CommandError("No battle is active in this chat.")
|
||||
|
||||
active_character = await get_active_character(data)
|
||||
active_character = await get_active_character(data=data, session=session)
|
||||
if active_character is None:
|
||||
raise rc.CommandError("You don't have an active character.")
|
||||
|
||||
char: DndCharacter = active_character.character
|
||||
|
||||
units_with_same_name = await ru.asyncify(data.session.query(DndBattleUnitT).filter_by(
|
||||
units_with_same_name = await ru.asyncify(session.query(DndBattleUnitT).filter_by(
|
||||
name=char.name,
|
||||
battle=active_battle.battle
|
||||
).all)
|
||||
|
@ -56,8 +57,8 @@ class DndjoinbattleCommand(rc.Command):
|
|||
battle=active_battle.battle
|
||||
)
|
||||
|
||||
data.session.add(dbu)
|
||||
await data.session_commit()
|
||||
session.add(dbu)
|
||||
await ru.asyncify(session.commit)
|
||||
|
||||
await data.reply(f"{dbu}\n"
|
||||
f"joins the battle!\n"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import re
|
||||
# noinspection PyUnresolvedReferences
|
||||
from royalnet.commands import *
|
||||
import royalnet.utils as ru
|
||||
from ..tables import DndCharacter
|
||||
from ..types import DndProficiencyType
|
||||
|
||||
|
@ -14,7 +15,8 @@ class DndnewCommand(Command):
|
|||
|
||||
syntax = "{name}\n{character_sheet}"
|
||||
|
||||
def _search_value(self, name: str, string: str):
|
||||
@staticmethod
|
||||
def _search_value(name: str, string: str):
|
||||
return re.search(r"\s*" + name + r"\s*([0-9.]+)\s*", string, re.IGNORECASE)
|
||||
|
||||
def _parse(self, character_sheet: str) -> dict:
|
||||
|
@ -50,15 +52,16 @@ class DndnewCommand(Command):
|
|||
await data.reply(self._syntax())
|
||||
return
|
||||
|
||||
creator = await data.get_author()
|
||||
async with data.session_acm() as session:
|
||||
creator = await data.find_author(session=session, required=True)
|
||||
|
||||
name, rest = character_sheet.split("\n", 1)
|
||||
|
||||
character = self.alchemy.get(DndCharacter)(name=name, creator=creator, **self._parse(rest))
|
||||
data.session.add(character)
|
||||
session.add(character)
|
||||
|
||||
try:
|
||||
await data.session_commit()
|
||||
await ru.asyncify(session.commit)
|
||||
except Exception as err:
|
||||
# THIS IS INTENDED
|
||||
if err.__class__.__name__ == "IntegrityError":
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import *
|
||||
import royalnet
|
||||
import royalnet.commands as rc
|
||||
import royalnet.utils as ru
|
||||
from ..tables import DndBattle
|
||||
|
||||
|
||||
|
@ -18,12 +19,13 @@ class DndnewbattleCommand(rc.Command):
|
|||
name = line_args[0]
|
||||
description = line_args[1] if len(line_args) > 1 else None
|
||||
|
||||
async with data.session_acm() as session:
|
||||
battle = BattleT(
|
||||
name=name,
|
||||
description=description
|
||||
)
|
||||
|
||||
data.session.add(battle)
|
||||
await data.session_commit()
|
||||
session.add(battle)
|
||||
await ru.asyncify(session.commit)
|
||||
|
||||
await data.reply(f"✅ Battle [b]{battle.name}[/b] (ID: {battle.id}) created!")
|
||||
|
|
|
@ -78,7 +78,8 @@ class DndrollCommand(Command):
|
|||
}
|
||||
|
||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
active_character = await get_active_character(data)
|
||||
async with data.session_acm() as session:
|
||||
active_character = await get_active_character(session=session, data=data)
|
||||
if active_character is None:
|
||||
raise CommandError("You don't have an active character.")
|
||||
char = active_character.character
|
||||
|
@ -131,14 +132,14 @@ class DndrollCommand(Command):
|
|||
roll_b = random.randrange(1, 21)
|
||||
roll = max([roll_a, roll_b])
|
||||
total = roll + modifier
|
||||
await data.reply(f"🎲 2d20h1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]")
|
||||
await data.reply(f"🎲 [i]{skill_name.capitalize()}[/i]: 2d20h1{modifier_str} = ({roll_a}|{roll_b} ){modifier_str} = [b]{total}[/b]")
|
||||
elif disadvantage:
|
||||
roll_a = random.randrange(1, 21)
|
||||
roll_b = random.randrange(1, 21)
|
||||
roll = min([roll_a, roll_b])
|
||||
total = roll + modifier
|
||||
await data.reply(f"🎲 2d20l1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]")
|
||||
await data.reply(f"🎲 [i]{skill_name.capitalize()}[/i]: 2d20l1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]")
|
||||
else:
|
||||
roll = random.randrange(1, 21)
|
||||
total = roll + modifier
|
||||
await data.reply(f"🎲 1d20{modifier_str} = {roll}{modifier_str} = [b]{total}[/b]")
|
||||
await data.reply(f"🎲 [i]{skill_name.capitalize()}[/i]: 1d20{modifier_str} = {roll}{modifier_str} = [b]{total}[/b]")
|
||||
|
|
|
@ -3,6 +3,7 @@ import sortedcontainers
|
|||
import logging
|
||||
from royalnet.commands import *
|
||||
from royalnet.utils import ordinalformat, andformat, sentry_exc
|
||||
import royalnet.serf as rs
|
||||
from ..utils import parse_5etools_entry
|
||||
|
||||
|
||||
|
@ -20,9 +21,9 @@ class DndspellCommand(Command):
|
|||
|
||||
_dnddata: sortedcontainers.SortedKeyList = None
|
||||
|
||||
def __init__(self, interface: CommandInterface):
|
||||
super().__init__(interface)
|
||||
interface.loop.create_task(self._fetch_dnddata())
|
||||
def __init__(self, serf: rs.Serf, config: "ConfigDict"):
|
||||
super().__init__(serf, config)
|
||||
self.serf.tasks.add(self._fetch_dnddata())
|
||||
|
||||
async def _fetch_dnddata(self):
|
||||
self._dnddata = self._dnddata = sortedcontainers.SortedKeyList([], key=lambda i: i["name"].lower())
|
||||
|
|
|
@ -28,7 +28,7 @@ class RollCommand(Command):
|
|||
except ValueError:
|
||||
if isinstance(first, str) and "d20" in first:
|
||||
raise InvalidInputError(f"Invalid value specified.\n"
|
||||
f"Were you looking for [c]{self.interface.prefix}dice[/c]?")
|
||||
f"Were you looking for [c]{self.serf.prefix}dice[/c]?")
|
||||
else:
|
||||
raise InvalidInputError("Invalid value specified.")
|
||||
result = random.randrange(minimum, maximum+1)
|
||||
|
|
|
@ -6,26 +6,25 @@ import royalnet.commands as rc
|
|||
import pickle
|
||||
|
||||
|
||||
async def get_active_battle(data: rc.CommandData) -> Optional[DndActiveBattle]:
|
||||
interface = data._interface
|
||||
alchemy = interface.alchemy
|
||||
async def get_active_battle(*, data: rc.CommandData, session) -> Optional[DndActiveBattle]:
|
||||
alchemy = data.alchemy
|
||||
idata = get_interface_data(data)
|
||||
|
||||
DndAcBaT = alchemy.get(DndActiveBattle)
|
||||
active_battles: List[DndActiveBattle] = await ru.asyncify(
|
||||
data.session
|
||||
session
|
||||
.query(DndAcBaT)
|
||||
.filter_by(interface_name=interface.name)
|
||||
.filter_by(interface_name=data.command.serf.__class__.__name__)
|
||||
.all
|
||||
)
|
||||
|
||||
for active_battle in active_battles:
|
||||
if interface.name == "telegram":
|
||||
if data.command.serf.__class__.__name__ == "TelegramSerf":
|
||||
# interface_data is chat id
|
||||
chat_id = pickle.loads(active_battle.interface_data)
|
||||
if chat_id == idata:
|
||||
return active_battle
|
||||
elif interface.name == "discord":
|
||||
elif data.command.serf.__class__.__name__ == "DiscordSerf":
|
||||
# interface_data is channel id
|
||||
chat_id = pickle.loads(active_battle.interface_data)
|
||||
if chat_id == idata:
|
||||
|
|
|
@ -6,27 +6,26 @@ import royalnet.commands as rc
|
|||
import pickle
|
||||
|
||||
|
||||
async def get_active_character(data: rc.CommandData) -> Optional[DndActiveCharacter]:
|
||||
interface = data._interface
|
||||
alchemy = interface.alchemy
|
||||
user = await data.get_author(error_if_none=True)
|
||||
async def get_active_character(*, data: rc.CommandData, session) -> Optional[DndActiveCharacter]:
|
||||
alchemy = data.alchemy
|
||||
user = await data.find_author(session=session, required=True)
|
||||
idata = get_interface_data(data)
|
||||
|
||||
DndAcChT = alchemy.get(DndActiveCharacter)
|
||||
active_characters: List[DndActiveCharacter] = await ru.asyncify(
|
||||
data.session
|
||||
session
|
||||
.query(DndAcChT)
|
||||
.filter_by(interface_name=interface.name, user=user)
|
||||
.filter_by(interface_name=data.command.serf.__class__.__name__, user=user)
|
||||
.all
|
||||
)
|
||||
|
||||
for active_character in active_characters:
|
||||
if interface.name == "telegram":
|
||||
if data.command.serf.__class__.__name__ == "TelegramSerf":
|
||||
# interface_data is chat id
|
||||
chat_id = pickle.loads(active_character.interface_data)
|
||||
if chat_id == idata:
|
||||
return active_character
|
||||
elif interface.name == "discord":
|
||||
elif data.command.serf.__class__.__name__ == "DiscordSerf":
|
||||
# interface_data is channel id
|
||||
chat_id = pickle.loads(active_character.interface_data)
|
||||
if chat_id == idata:
|
||||
|
|
|
@ -2,9 +2,9 @@ import royalnet.commands as rc
|
|||
|
||||
|
||||
def get_interface_data(data: rc.CommandData):
|
||||
if data._interface.name == "telegram":
|
||||
if data.command.serf.__class__.__name__ == "TelegramSerf":
|
||||
return data.message.chat.id
|
||||
elif data._interface.name == "discord":
|
||||
if data.command.serf.__class__.__name__ == "DiscordSerf":
|
||||
return data.message.channel.id
|
||||
else:
|
||||
raise rc.UnsupportedError("This interface isn't supported yet.")
|
||||
|
|
|
@ -8,8 +8,8 @@ from ..types.faction import Faction
|
|||
from sqlalchemy import and_
|
||||
|
||||
|
||||
async def get_targets(data: rc.CommandData, target: Optional[str]) -> List[DndBattleUnit]:
|
||||
DndBattleUnitT = data._interface.alchemy.get(DndBattleUnit)
|
||||
async def get_targets(target: Optional[str], *, data: rc.CommandData, session) -> List[DndBattleUnit]:
|
||||
DndBattleUnitT = data.alchemy.get(DndBattleUnit)
|
||||
|
||||
active_battle = await get_active_battle(data)
|
||||
if active_battle is None:
|
||||
|
@ -23,14 +23,14 @@ async def get_targets(data: rc.CommandData, target: Optional[str]) -> List[DndBa
|
|||
return []
|
||||
char = active_character.character
|
||||
|
||||
return await ru.asyncify(data.session.query(DndBattleUnitT).filter_by(
|
||||
return await ru.asyncify(session.query(DndBattleUnitT).filter_by(
|
||||
linked_character=char,
|
||||
battle=battle
|
||||
).all)
|
||||
|
||||
# Get all
|
||||
if target.upper() == "ALL":
|
||||
return await ru.asyncify(data.session.query(DndBattleUnitT).filter_by(
|
||||
return await ru.asyncify(session.query(DndBattleUnitT).filter_by(
|
||||
battle=battle
|
||||
).all)
|
||||
|
||||
|
@ -40,13 +40,13 @@ async def get_targets(data: rc.CommandData, target: Optional[str]) -> List[DndBa
|
|||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return await ru.asyncify(data.session.query(DndBattleUnitT).filter_by(
|
||||
return await ru.asyncify(session.query(DndBattleUnitT).filter_by(
|
||||
faction=faction,
|
||||
battle=battle
|
||||
).all)
|
||||
|
||||
# Get by ilike
|
||||
return await ru.asyncify(data.session.query(DndBattleUnitT).filter(and_(
|
||||
return await ru.asyncify(session.query(DndBattleUnitT).filter(and_(
|
||||
DndBattleUnitT.name.ilike(target),
|
||||
DndBattleUnitT.battle == battle
|
||||
)).all)
|
||||
|
|
Loading…
Reference in a new issue