Stopped using db in bot

This commit is contained in:
2023-11-22 20:57:20 +03:00
parent abf365f2f9
commit 642c347bba
15 changed files with 45 additions and 505 deletions

View File

@@ -1 +0,0 @@
Generic single-database configuration.

View File

@@ -1,84 +0,0 @@
import os
from logging.config import fileConfig
from os import getenv
from os.path import dirname, join
from dotenv import load_dotenv
from sqlalchemy import create_engine
from alembic import context
from db.data import Base
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
load_dotenv(join(dirname(__file__), '..', '.env'))
DATABASE_PASSWORD=os.getenv('DATABASE_PASSWORD')
DATABASE_NAME=os.getenv('DATABASE_NAME')
DATABASE_USER=os.getenv('DATABASE_USER')
DATABASE_PORT=os.getenv('DATABASE_PORT')
DATABASE_HOST=os.getenv('DATABASE_HOST')
URL=f"postgresql+psycopg2://{DATABASE_USER}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}"
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = URL
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = create_engine(URL)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@@ -1,26 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

View File

@@ -1,30 +0,0 @@
"""Added file_id to image table
Revision ID: 1d040fbb74ce
Revises: 8c2a92134271
Create Date: 2023-11-04 03:35:28.011123
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '1d040fbb74ce'
down_revision: Union[str, None] = '8c2a92134271'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('photo', sa.Column('file_id', sa.String(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('photo', 'file_id')
# ### end Alembic commands ###

View File

@@ -1,32 +0,0 @@
"""Moved media group from image to post
Revision ID: 278f7650482f
Revises: ca01506184b5
Create Date: 2023-11-04 02:32:22.398760
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '278f7650482f'
down_revision: Union[str, None] = 'ca01506184b5'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('photo', 'media_group_id')
op.add_column('post', sa.Column('media_group_id', sa.Integer(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('post', 'media_group_id')
op.add_column('photo', sa.Column('media_group_id', sa.INTEGER(), autoincrement=False, nullable=False))
# ### end Alembic commands ###

View File

@@ -1,30 +0,0 @@
"""Added timestamp and posted
Revision ID: 2af6a0df717b
Revises: 1d040fbb74ce
Create Date: 2023-11-04 04:09:12.689628
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '2af6a0df717b'
down_revision: Union[str, None] = '1d040fbb74ce'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('post', sa.Column('timestamp', sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('post', 'timestamp')
# ### end Alembic commands ###

View File

@@ -1,36 +0,0 @@
"""Change to str mediag_group_id
Revision ID: 8c2a92134271
Revises: 278f7650482f
Create Date: 2023-11-04 03:06:58.339894
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '8c2a92134271'
down_revision: Union[str, None] = '278f7650482f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('post', 'media_group_id',
existing_type=sa.INTEGER(),
type_=sa.String(),
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('post', 'media_group_id',
existing_type=sa.String(),
type_=sa.INTEGER(),
existing_nullable=False)
# ### end Alembic commands ###

View File

@@ -1,30 +0,0 @@
"""Initial migration
Revision ID: 9de2db27ca6e
Revises:
Create Date: 2023-10-29 01:23:30.890347
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '9de2db27ca6e'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View File

@@ -1,30 +0,0 @@
"""Added spoiler to photo
Revision ID: c9872bd4d4b5
Revises: f0ed48a3ded3
Create Date: 2023-11-15 23:38:03.886970
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'c9872bd4d4b5'
down_revision: Union[str, None] = 'f0ed48a3ded3'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('photo', sa.Column('has_spoiler', sa.Boolean(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('photo', 'has_spoiler')
# ### end Alembic commands ###

View File

@@ -1,38 +0,0 @@
"""Added image table
Revision ID: ca01506184b5
Revises: 9de2db27ca6e
Create Date: 2023-11-04 02:21:28.077631
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'ca01506184b5'
down_revision: Union[str, None] = '9de2db27ca6e'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('photo',
sa.Column('message_id', sa.Integer(), nullable=False),
sa.Column('media_group_id', sa.Integer(), nullable=False),
sa.Column('post_id', sa.Uuid(), nullable=False),
sa.ForeignKeyConstraint(['post_id'], ['post.uuid'], ),
sa.PrimaryKeyConstraint('message_id')
)
op.drop_column('post', 'images')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('post', sa.Column('images', sa.VARCHAR(), autoincrement=False, nullable=False))
op.drop_table('photo')
# ### end Alembic commands ###

View File

@@ -1,30 +0,0 @@
"""Added posted
Revision ID: f0ed48a3ded3
Revises: 2af6a0df717b
Create Date: 2023-11-04 04:12:46.479474
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'f0ed48a3ded3'
down_revision: Union[str, None] = '2af6a0df717b'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('post', sa.Column('posted', sa.Boolean(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('post', 'posted')
# ### end Alembic commands ###

View File

@@ -1,78 +0,0 @@
import os
from datetime import datetime
from os.path import dirname, join
from typing import List, Optional
from uuid import UUID
from dotenv import load_dotenv
from sqlalchemy import Column, DateTime, ForeignKey, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
load_dotenv(join(dirname(__file__), '..', '.env'))
DATABASE_PASSWORD = os.getenv('DATABASE_PASSWORD')
DATABASE_NAME = os.getenv('DATABASE_NAME')
DATABASE_USER = os.getenv('DATABASE_USER')
DATABASE_PORT = os.getenv('DATABASE_PORT')
DATABASE_HOST = os.getenv('DATABASE_HOST')
engine = create_engine(
f"postgresql+psycopg2://{DATABASE_USER}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}", echo=True)
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = 'user'
id: Mapped[int] = mapped_column(primary_key=True)
user_name: Mapped[Optional[str]]
admin: Mapped['Admin'] = relationship(
back_populates='user', cascade='all, delete-orphan')
def __repr__(self) -> str:
return f'User(id={self.id!r}, user_name={self.user_name!r})'
def __str__(self) -> str:
return f'User(id={self.id!r}, user_name={self.user_name!r})'
class Admin(Base):
__tablename__ = 'admin'
user_id: Mapped[int] = mapped_column(
ForeignKey('user.id'), primary_key=True)
user: Mapped['User'] = relationship(back_populates='admin')
posts: Mapped[List['Post']] = relationship(
back_populates='user', cascade='all, delete')
class Post(Base):
__tablename__ = 'post'
uuid: Mapped[UUID] = mapped_column(primary_key=True)
posted: Mapped[bool] = mapped_column(default=False)
from_user_id: Mapped[int] = mapped_column(ForeignKey('admin.user_id'))
user: Mapped['Admin'] = relationship(back_populates='posts')
text: Mapped[str]
media_group_id: Mapped[str]
images: Mapped[List['Image']] = relationship(
back_populates='post', cascade='all, delete')
timestamp = Column(DateTime, default=datetime.utcnow)
def __repr__(self) -> str:
return f"UUID: {self.uuid}. From_user_id: {self.from_user_id}. media group id: {self.media_group_id}. Text: {self.text}"
class Image(Base):
__tablename__ = 'photo'
message_id: Mapped[int] = mapped_column(primary_key=True)
post: Mapped['Post'] = relationship(back_populates='images')
post_id: Mapped[int] = mapped_column(ForeignKey('post.uuid'))
file_id: Mapped[str]
has_spoiler: Mapped[bool]
if __name__ == '__main__':
Base.metadata.create_all(engine)

View File

@@ -1,14 +1,10 @@
from datetime import datetime
from typing import Any from typing import Any
from uuid import uuid4
from aiogram import Bot, F, Router, types from aiogram import Bot, F, Router, types
from aiogram.filters import Command from aiogram.filters import Command
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from aiogram.utils.media_group import MediaGroupBuilder from aiogram.utils.media_group import MediaGroupBuilder
from sqlalchemy.orm import Session
from db.data import Admin, Image, Post, User, engine
from handlers.filters.new_post import (ChangePosts, NewPostFilter, from handlers.filters.new_post import (ChangePosts, NewPostFilter,
NewSoloPostFilter) NewSoloPostFilter)
from handlers.filters.reply_to_user import ReplyToUser from handlers.filters.reply_to_user import ReplyToUser
@@ -17,10 +13,10 @@ from handlers.states.change_post import ChangePost
from neuroapi import neuroapi from neuroapi import neuroapi
def get_post_info(post: Post, post_id: int) -> str: def get_post_info(post: dict, post_id: int) -> str:
text = post.text text = post["text"]
time = post.timestamp time = post["timestamp"]
from_user = post.from_user_id from_user = post["from_user_id"]
s = f"""Индекс: {post_id}\nТекст: {text}\nВремя отправки: {time}\nОт: [id{from_user}](tg://user?id={from_user})""".replace('#', '\#').replace( s = f"""Индекс: {post_id}\nТекст: {text}\nВремя отправки: {time}\nОт: [id{from_user}](tg://user?id={from_user})""".replace('#', '\#').replace(
"_", "\_").replace('.', '\.').replace(',', '\,').replace('!', '\!').replace('-', '\-').replace(':', '\:') "_", "\_").replace('.', '\.').replace(',', '\,').replace('!', '\!').replace('-', '\-').replace(':', '\:')
return s return s
@@ -51,38 +47,34 @@ class Admin_commands:
post_c[post['from_user_id']] += 1 post_c[post['from_user_id']] += 1
await message.answer(str(post_c)) await message.answer(str(post_c))
#TODO: Post changing with backend
######################################3
@self.router.message(ChangePosts()) @self.router.message(ChangePosts())
async def change_post(message: types.Message, state: FSMContext): async def change_post(message: types.Message, state: FSMContext):
with Session(engine) as session: posts = await neuroapi.post.get_will_post()
posts = session.query(Post).filter( if (posts):
Post.posted == False).order_by(Post.timestamp.asc()).all() await state.update_data(posts=posts, id=0)
if len(posts): select_btns = []
await state.update_data(posts=posts, id=0) if len(posts) > 1:
select_btns = [] select_btns.append(types.InlineKeyboardButton(
if len(posts) > 1: text='->', callback_data='next_post'))
select_btns.append(types.InlineKeyboardButton( kb = [
text='->', callback_data='next_post')) select_btns,
kb = [ [types.InlineKeyboardButton(
select_btns, callback_data='change_post_text', text='Текст')],
[types.InlineKeyboardButton( [types.InlineKeyboardButton(
callback_data='change_post_text', text='Текст')], text='Отмена', callback_data='cancel')]
[types.InlineKeyboardButton( ]
text='Отмена', callback_data='cancel')] keyboard = types.InlineKeyboardMarkup(inline_keyboard=kb)
] post = await neuroapi.post.get(posts[0]['uuid'])
keyboard = types.InlineKeyboardMarkup(inline_keyboard=kb) images = MediaGroupBuilder(
images = MediaGroupBuilder( caption=get_post_info(post, 1))
caption=get_post_info(posts[0], 1)) for image in sorted(post['images'], key=lambda x: x['message_id']):
for image in posts[0].images: images.add_photo(image['file_id'],
images.add_photo( has_spoiler=image['has_spoiler'], parse_mode='markdownv2')
image.file_id, parse_mode='markdownv2') mes = await message.answer_media_group(images.build())
mes = await message.answer_media_group(media=images.build()) await state.update_data(edit_msg=mes[0].message_id)
await state.update_data(edit_msg=mes[0].message_id) await message.answer('Действия', reply_markup=keyboard)
await message.answer('Действия', reply_markup=keyboard) else:
# await message.answer(get_post_info(posts[0]), reply_markup=keyboard, parse_mode='markdownv2') await message.answer('Нет постов')
else:
await message.answer('Нет постов')
@self.router.callback_query(F.data == 'next_post') @self.router.callback_query(F.data == 'next_post')
async def next_post_changing(callback: types.CallbackQuery, state: FSMContext): async def next_post_changing(callback: types.CallbackQuery, state: FSMContext):
@@ -108,7 +100,8 @@ class Admin_commands:
] ]
keyboard = types.InlineKeyboardMarkup(inline_keyboard=kb) keyboard = types.InlineKeyboardMarkup(inline_keyboard=kb)
await state.update_data(id=post_id) await state.update_data(id=post_id)
await bot.edit_message_caption(caption=get_post_info(posts[post_id], post_id+1), chat_id=callback.message.chat.id, message_id=data['edit_msg'], parse_mode='markdownv2') post = await neuroapi.post.get(posts[post_id]['uuid'])
await bot.edit_message_caption(caption=get_post_info(post, post_id+1), chat_id=callback.message.chat.id, message_id=data['edit_msg'], parse_mode='markdownv2')
await callback.message.edit_reply_markup(reply_markup=keyboard) await callback.message.edit_reply_markup(reply_markup=keyboard)
await callback.answer() await callback.answer()
@@ -138,13 +131,13 @@ class Admin_commands:
return return
posts = data['posts'] posts = data['posts']
post_id = data['id'] post_id = data['id']
post: Post = posts[post_id] post_uuid = posts[post_id]['uuid']
with Session(engine) as session: try:
p = session.get(Post, post.uuid) await neuroapi.post.edit_text(post_uuid, message.text)
p.text = message.text await message.answer(f'Текст поста изменен на: {message.text}')
session.commit() except:
await message.answer('Ошибка')
await state.clear() await state.clear()
await message.answer(f'Текст поста изменен на: {message.text}')
@self.router.callback_query(F.data == 'prev_post') @self.router.callback_query(F.data == 'prev_post')
async def prev_post_changing(callback: types.CallbackQuery, state: FSMContext): async def prev_post_changing(callback: types.CallbackQuery, state: FSMContext):
@@ -170,7 +163,8 @@ class Admin_commands:
] ]
keyboard = types.InlineKeyboardMarkup(inline_keyboard=kb) keyboard = types.InlineKeyboardMarkup(inline_keyboard=kb)
await state.update_data(id=post_id) await state.update_data(id=post_id)
await bot.edit_message_caption(caption=get_post_info(posts[post_id], post_id), chat_id=callback.message.chat.id, message_id=data['edit_msg'], parse_mode='markdownv2') post = await neuroapi.post.get(posts[post_id]['uuid'])
await bot.edit_message_caption(caption=get_post_info(post, post_id), chat_id=callback.message.chat.id, message_id=data['edit_msg'], parse_mode='markdownv2')
await callback.message.edit_reply_markup(reply_markup=keyboard) await callback.message.edit_reply_markup(reply_markup=keyboard)
await callback.answer() await callback.answer()
@@ -182,8 +176,6 @@ class Admin_commands:
data = await state.get_data() data = await state.get_data()
if 'edit_msg' in data: if 'edit_msg' in data:
await bot.delete_message(message_id=data['edit_msg'], chat_id=callback.message.chat.id) await bot.delete_message(message_id=data['edit_msg'], chat_id=callback.message.chat.id)
##########################################################
@self.router.message(Command('post')) @self.router.message(Command('post'))
async def post(message: types.Message): async def post(message: types.Message):
@@ -192,7 +184,8 @@ class Admin_commands:
post = await neuroapi.post.get(posts[0]['uuid']) post = await neuroapi.post.get(posts[0]['uuid'])
images = MediaGroupBuilder(caption=post['text']) images = MediaGroupBuilder(caption=post['text'])
for image in sorted(post['images'], key=lambda x: x['message_id']): for image in sorted(post['images'], key=lambda x: x['message_id']):
images.add_photo(image['file_id'], has_spoiler=image['has_spoiler']) images.add_photo(image['file_id'],
has_spoiler=image['has_spoiler'])
await message.answer_media_group(images.build()) await message.answer_media_group(images.build())
else: else:
await message.answer('Нет постов') await message.answer('Нет постов')
@@ -202,7 +195,7 @@ class Admin_commands:
post = await neuroapi.post.new(message.caption.replace('/newpost ', ''), message.from_user.id) post = await neuroapi.post.new(message.caption.replace('/newpost ', ''), message.from_user.id)
await neuroapi.image.add(post['uuid'], message.photo[-1].file_id, message.has_media_spoiler, message.message_id) await neuroapi.image.add(post['uuid'], message.photo[-1].file_id, message.has_media_spoiler, message.message_id)
await message.answer('Пост успешно добавлен!') await message.answer('Пост успешно добавлен!')
@self.router.message(ReplyToUser()) @self.router.message(ReplyToUser())
async def reply_user(message: types.Message): async def reply_user(message: types.Message):
if message.reply_to_message.forward_from is None: if message.reply_to_message.forward_from is None:

View File

@@ -1,13 +1,6 @@
from asyncio import create_task
from time import sleep
from typing import Any
from uuid import uuid4
from aiogram import types from aiogram import types
from aiogram.filters import Filter from aiogram.filters import Filter
from sqlalchemy.orm import Session
from db.data import Admin, Image, Post, User, engine
from neuroapi import neuroapi from neuroapi import neuroapi
@@ -19,9 +12,9 @@ class NewPostFilter(Filter):
await neuroapi.post.get_by_media_group_id(message.media_group_id) await neuroapi.post.get_by_media_group_id(message.media_group_id)
except: except:
if not (message.caption.startswith('/newpost ') if message.caption else False): if not (message.caption.startswith('/newpost ') if message.caption else False):
return False return False
await neuroapi.post.new(message.caption.replace( await neuroapi.post.new(message.caption.replace(
'/newpost ', ''), str(message.from_user.id), str(message.media_group_id)) '/newpost ', ''), str(message.from_user.id), str(message.media_group_id))
await message.answer('Пост успешно добавлен!') await message.answer('Пост успешно добавлен!')
return True return True

View File

@@ -1,4 +1,3 @@
from typing import Any
from aiogram import types from aiogram import types
from aiogram.filters import Filter from aiogram.filters import Filter