backend + telegram: добавлено создание персонажа

This commit is contained in:
2022-11-19 01:31:35 +03:00
parent 2bc6931525
commit 194b5aaff1
11 changed files with 291 additions and 51 deletions

View File

@@ -0,0 +1,44 @@
"""empty message
Revision ID: 02142c549a8c
Revises: 07950b032eb7
Create Date: 2022-11-19 00:29:12.011840
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '02142c549a8c'
down_revision = '07950b032eb7'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('character_classes', schema=None) as batch_op:
batch_op.create_unique_constraint(None, ['name'])
with op.batch_alter_table('character_races', schema=None) as batch_op:
batch_op.create_unique_constraint(None, ['name'])
with op.batch_alter_table('characters', schema=None) as batch_op:
batch_op.create_unique_constraint(None, ['name'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('characters', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='unique')
with op.batch_alter_table('character_races', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='unique')
with op.batch_alter_table('character_classes', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='unique')
# ### end Alembic commands ###

View File

@@ -0,0 +1,36 @@
"""empty message
Revision ID: 07950b032eb7
Revises: 5aef83a8a42f
Create Date: 2022-11-18 23:40:33.130706
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '07950b032eb7'
down_revision = '5aef83a8a42f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('characters', schema=None) as batch_op:
batch_op.alter_column('owner',
existing_type=mysql.INTEGER(),
nullable=True)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('characters', schema=None) as batch_op:
batch_op.alter_column('owner',
existing_type=mysql.INTEGER(),
nullable=False)
# ### end Alembic commands ###

View File

@@ -18,17 +18,6 @@ class ItemAPI(MethodView):
item = self._get_item(id) item = self._get_item(id)
return item.to_dict() return item.to_dict()
def patch(self, id):
item = self._get_item(id)
errors = self.validator.validate(item, request.json)
if errors:
return jsonify(errors), 400
item.update_from_json(request.json)
db.session.commit()
return item.to_dict()
def delete(self, id): def delete(self, id):
item = self._get_item(id) item = self._get_item(id)
db.session.delete(item) db.session.delete(item)
@@ -51,12 +40,15 @@ class ListAPI(MethodView):
def post(self): def post(self):
item = self._get_item(request.json["id"]) data = request.json
if data.get("id"):
item = self._get_item(data["id"])
if item: if item:
return "Already exists!", 400 return "Already exists!", 400
db.session.add(self.model(**request.json)) db.session.add(self.model(**data))
db.session.commit() db.session.commit()
return "", 200 return "", 200

View File

@@ -11,6 +11,6 @@ main = Blueprint("main", __name__)
register_api(main, User, "users") register_api(main, User, "users")
register_api(main, CharacterRace, "charachter-races") register_api(main, CharacterRace, "character_races")
register_api(main, CharacterClass, "charachter-classes") register_api(main, CharacterClass, "character_classes")
register_api(main, Character, "character") register_api(main, Character, "characters")

View File

@@ -11,6 +11,8 @@ class User(db.Model, SerializerMixin):
__tablename__ = "users" __tablename__ = "users"
serialize_rules = ("-characters",)
id = db.Column(db.Integer, primary_key=True, autoincrement=False) id = db.Column(db.Integer, primary_key=True, autoincrement=False)
first_name = db.Column(db.String(255), nullable=True) first_name = db.Column(db.String(255), nullable=True)
last_name = db.Column(db.String(255), nullable=True) last_name = db.Column(db.String(255), nullable=True)
@@ -19,7 +21,7 @@ class User(db.Model, SerializerMixin):
db.DateTime, nullable=False, default=datetime.datetime.now() db.DateTime, nullable=False, default=datetime.datetime.now()
) )
is_admin = db.Column(db.Boolean, nullable=False, default=False) is_admin = db.Column(db.Boolean, nullable=False, default=False)
character = db.relationship("Character", backref="user", lazy="dynamic") characters = db.relationship("Character", backref="user", lazy="dynamic")
def __str__(self): def __str__(self):
return f"{self.id}: {self.username}" return f"{self.id}: {self.username}"
@@ -29,9 +31,11 @@ class CharacterRace(db.Model, SerializerMixin):
__tablename__ = "character_races" __tablename__ = "character_races"
serialize_rules = ("-characters",)
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=True) name = db.Column(db.String(255), nullable=True, unique=True)
character = db.relationship("Character", backref="race", lazy="dynamic") characters = db.relationship("Character", backref="race", lazy="dynamic")
def __str__(self): def __str__(self):
return self.name return self.name
@@ -41,9 +45,11 @@ class CharacterClass(db.Model, SerializerMixin):
__tablename__ = "character_classes" __tablename__ = "character_classes"
serialize_rules = ("-characters",)
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=True) name = db.Column(db.String(255), nullable=True, unique=True)
character = db.relationship("Character", backref="class", lazy="dynamic") characters = db.relationship("Character", backref="class", lazy="dynamic")
def __str__(self): def __str__(self):
return self.name return self.name
@@ -59,7 +65,7 @@ class Character(db.Model, SerializerMixin):
db.ForeignKey("users.id"), db.ForeignKey("users.id"),
nullable=True, nullable=True,
) )
name = db.Column(db.String(255), nullable=False) name = db.Column(db.String(255), nullable=False, unique=True)
character_race = db.Column( character_race = db.Column(
db.Integer, db.Integer,
db.ForeignKey("character_races.id"), db.ForeignKey("character_races.id"),

View File

@@ -1,16 +1,22 @@
aiogram==2.23.1 aiofiles==22.1.0
aiogram==3.0.0b6
aiohttp==3.8.3 aiohttp==3.8.3
aiosignal==1.3.1 aiosignal==1.3.1
async-timeout==4.0.2 async-timeout==4.0.2
attrs==22.1.0 attrs==22.1.0
Babel==2.9.1 Babel==2.9.1
cachetools==4.2.4
certifi==2022.9.24 certifi==2022.9.24
charset-normalizer==2.1.1 charset-normalizer==2.1.1
frozenlist==1.3.3 frozenlist==1.3.3
idna==3.4 idna==3.4
Jinja2==3.1.2
magic-filter==1.0.9 magic-filter==1.0.9
MarkupSafe==2.1.1
multidict==6.0.2 multidict==6.0.2
pydantic==1.10.2
pytz==2022.6 pytz==2022.6
requests==2.28.1 requests==2.28.1
typing-extensions==4.4.0
urllib3==1.26.12 urllib3==1.26.12
yarl==1.8.1 yarl==1.8.1

View File

@@ -8,6 +8,13 @@ with open("textsouls/config.json") as config_file:
class Backend: class Backend:
base_url = config_data["BACKEND_SETTINGS"]["BASE_URL"] base_url = config_data["BACKEND_SETTINGS"]["BASE_URL"]
def get(self, relative_url):
try:
response = requests.get(f"{self.base_url}{relative_url}")
return {"error": None, "response": response}
except Exception as err:
return {"error": err}
def post(self, relative_url, data): def post(self, relative_url, data):
try: try:
response = requests.post( response = requests.post(

View File

@@ -0,0 +1,105 @@
from aiogram import Router
from aiogram.filters import Command
from aiogram.fsm.state import StatesGroup, State
from common import backend
from keyboards.common import row_kb
router = Router()
available_races = {}
available_classes = {}
class CharachterCreation(StatesGroup):
choosing_race = State()
choosing_class = State()
choosing_name = State()
creation_ending = State()
@router.message(Command(commands=["createchar"]))
async def character_creation(message, state):
global available_races
result = backend.get("/character_races")
if not result["error"] and result["response"].status_code == 200:
for race in result["response"].json():
available_races[race["name"]] = race["id"]
await message.answer(
text="Выбери расу:",
reply_markup=row_kb(available_races.keys()),
)
await state.set_state(CharachterCreation.choosing_class)
@router.message(CharachterCreation.choosing_class)
async def race_chosen(message, state):
if not available_races.get(message.text):
await message.answer(
text="Хм, такую не знаю.\nПопробуй из вариантов ниже:",
reply_markup=row_kb(available_races),
)
else:
await state.update_data(chosen_race=message.text)
global available_classes
result = backend.get("/character_classes")
if not result["error"] and result["response"].status_code == 200:
for char_class in result["response"].json():
available_classes[char_class["name"]] = char_class["id"]
await message.answer(
text="А теперь выбери класс:",
reply_markup=row_kb(available_classes.keys()),
)
await state.set_state(CharachterCreation.choosing_name)
@router.message(CharachterCreation.choosing_name)
async def class_chosen(message, state):
if not available_classes.get(message.text):
await message.answer(
text="Хм, такой не знаю.\nПопробуй из вариантов ниже:",
reply_markup=row_kb(available_classes),
)
else:
await state.update_data(chosen_class=message.text)
await message.answer(
text=f"Ну, неплохо.\nА зовут-то тебя как, {message.text.lower()}?",
reply_markup="",
)
await state.set_state(CharachterCreation.creation_ending)
@router.message(CharachterCreation.creation_ending)
async def name_chosen(message, state):
name = message.text
user_elections = await state.get_data()
new_character = {
"owner": message.from_user.id,
"name": name,
"character_race": available_races[user_elections["chosen_race"]],
"character_class": available_classes[user_elections["chosen_class"]],
}
result = backend.post("/characters", new_character)
if not result["error"] and result["response"].status_code == 200:
await message.answer(
text="Так и запишем.\nТеперь постарайся не умереть",
reply_markup="",
)
await state.clear()
else:
await message.answer(
text="Что-то пошло не так!\nДавай-ка по-новой",
)
await state.set_state(CharachterCreation.choosing_race)
await character_creation(message, state)

View File

@@ -0,0 +1,38 @@
from aiogram import Router
from aiogram.filters import Command
from common import backend
from handlers.character_creation import CharachterCreation
from handlers.character_creation import character_creation
router = Router()
@router.message(Command(commands=["start"]))
async def start(message, state):
tg_user = message.from_user
ts_user = {
"id": tg_user.id,
"first_name": tg_user.first_name,
"last_name": tg_user.last_name,
"username": tg_user.username,
}
result = backend.post("/users", ts_user)
if not result["error"]:
response_code = result["response"].status_code
if response_code == 200:
await message.reply("Привет, скиталец!", reply_markup="")
await state.set_state(CharachterCreation.choosing_race)
await character_creation(message, state)
elif response_code == 400:
await message.reply("Добро пожаловать! Снова")
else:
await message.reply("Что-то случилось")
else:
await message.reply("Упс! Что-то пошло не так")
await state.set_state(CharachterCreation.choosing_class)

View File

@@ -0,0 +1,18 @@
from aiogram.utils.keyboard import (
ReplyKeyboardBuilder,
ReplyKeyboardMarkup,
KeyboardButton,
)
def yes_no_kb():
kb = ReplyKeyboardBuilder()
kb.button(text="Да")
kb.button(text="Нет")
kb.adjust(2)
return kb.as_markup(resize_keyboard=True)
def row_kb(items):
row = [KeyboardButton(text=item) for item in items]
return ReplyKeyboardMarkup(keyboard=[row], resize_keyboard=True)

40
telegram/textsouls/main.py Executable file → Normal file
View File

@@ -1,39 +1,27 @@
import json import json
from aiogram import Bot, Dispatcher, executor import asyncio
from common import backend from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from handlers import control
from handlers import character_creation
with open("textsouls/config.json") as config_file: with open("textsouls/config.json") as config_file:
config_data = json.load(config_file) config_data = json.load(config_file)
API_TOKEN = config_data["MAIN_SETTINGS"]["BOT_TOKEN"]
bot = Bot(token=API_TOKEN) async def main():
dp = Dispatcher(bot) bot = Bot(token=config_data["MAIN_SETTINGS"]["BOT_TOKEN"])
dp = Dispatcher(storage=MemoryStorage())
dp.include_router(control.router)
dp.include_router(character_creation.router)
@dp.message_handler(commands=["start"]) await bot.delete_webhook(drop_pending_updates=True)
async def start(message): await dp.start_polling(bot)
tg_user = message.from_user
ts_user = {
"id": tg_user.id,
"first_name": tg_user.first_name,
"last_name": tg_user.last_name,
"username": tg_user.username,
}
result = backend.post("/users", ts_user)
if not result["error"]:
response_code = result["response"].status_code
if response_code == 200:
await message.reply("Добро пожаловать!")
elif response_code == 400:
await message.reply("Добро пожаловать! Снова")
else:
await message.reply("Что-то другое")
else:
await message.reply("Упс! Что-то пошло не так")
if __name__ == "__main__": if __name__ == "__main__":
executor.start_polling(dp, skip_updates=True) asyncio.run(main())