mirror of
https://github.com/Llloooggg/TextSouls.git
synced 2026-03-06 04:26:23 +03:00
backend + telegram: добавлено создание персонажа
This commit is contained in:
44
backend/migrations/versions/02142c549a8c_.py
Normal file
44
backend/migrations/versions/02142c549a8c_.py
Normal 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 ###
|
||||||
36
backend/migrations/versions/07950b032eb7_.py
Normal file
36
backend/migrations/versions/07950b032eb7_.py
Normal 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 ###
|
||||||
@@ -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 item:
|
if data.get("id"):
|
||||||
return "Already exists!", 400
|
item = self._get_item(data["id"])
|
||||||
|
|
||||||
db.session.add(self.model(**request.json))
|
if item:
|
||||||
|
return "Already exists!", 400
|
||||||
|
|
||||||
|
db.session.add(self.model(**data))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return "", 200
|
return "", 200
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
105
telegram/textsouls/handlers/character_creation.py
Normal file
105
telegram/textsouls/handlers/character_creation.py
Normal 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)
|
||||||
38
telegram/textsouls/handlers/control.py
Normal file
38
telegram/textsouls/handlers/control.py
Normal 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)
|
||||||
18
telegram/textsouls/keyboards/common.py
Normal file
18
telegram/textsouls/keyboards/common.py
Normal 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
40
telegram/textsouls/main.py
Executable file → Normal 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())
|
||||||
|
|||||||
Reference in New Issue
Block a user