Compare commits

2 Commits
dev ... main

Author SHA1 Message Date
bebd17a3b3 fix telebot ver. 2024-11-08 08:12:54 +00:00
6125743b59 push queue own limit up to 6 2024-11-07 18:50:02 +00:00
17 changed files with 47 additions and 482 deletions

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
config.py
*.pyc
**/__pycache__
**/.state-save

View File

View File

@ -1,105 +0,0 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = postgresql://user:password@postgres:5432/db
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

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

View File

@ -1,84 +0,0 @@
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# 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
import sys
from os.path import abspath, dirname
sys.path.insert(0, '/app')
from db.base import Base
from db.models import *
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.
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 = config.get_main_option("sqlalchemy.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 = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
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,24 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${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 @@
"""add active
Revision ID: 9d20a4ff1eab
Revises:
Create Date: 2023-10-29 12:09:17.958616
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '9d20a4ff1eab'
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('queue', sa.Column('active', sa.Boolean(), nullable=True))
op.add_column('queueuser', sa.Column('active', sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('queueuser', 'active')
op.drop_column('queue', 'active')
# ### end Alembic commands ###

View File

@ -17,11 +17,10 @@ from datetime import datetime
import math
import socket
import os
import json
from typing import Union
# Local imports
from config import token, admins
from config import token
from constants import (
MAX_QUEUES_OWN,
MAX_QUEUES_PARTS_IN,
@ -30,12 +29,10 @@ from constants import (
)
import textbook
import keyboards
from pagination import PaginatedList
# DB
from db.base import Session, engine, Base
from db.models import User, Queue, QueueUser
from sqlalchemy import select
bot = AsyncTeleBot(token, state_storage=StatePickleStorage())
@ -45,17 +42,13 @@ class States(StatesGroup):
default = State()
changing_name = State()
changing_queue_name = State()
admin_broadcasting = State()
# Utils
def get_queue_stats_text(queue: Queue) -> str:
queue_users = queue.users.filter_by(active=True)
s = textbook.queue_stats.format(
name=queue.name, count=len(queue_users)
)
s = textbook.queue_stats.format(name=queue.name, count=len(queue.users))
return s
@ -69,7 +62,7 @@ async def get_queue_from_state_data(call: types.CallbackQuery) -> Queue:
callback_query_id=call.id, text=textbook.queue_operational_error
)
return None
queue = session.query(Queue).filter_by(id=queue_id, active=True).first()
queue = session.query(Queue).filter_by(id=queue_id).first()
if queue.owner.id != call.from_user.id:
await bot.answer_callback_query(
callback_query_id=call.id, text=textbook.queue_operational_error
@ -102,7 +95,7 @@ def get_first_queue_user(queue: Queue) -> Union[QueueUser, None]:
def kick_first(queue: Queue) -> bool:
if len(queue.users):
first_user = get_first_queue_user(queue)
setattr(first_user, "active", False)
session.delete(first_user)
session.commit()
normalize_queue(queue)
return True
@ -125,19 +118,15 @@ def normalize_queue(queue: Queue) -> Queue:
# setattr(first_user, "position", 0)
# session.commit()
# queue = session.query(Queue).filter_by(id=queue.id).first()
queue_users = queue.users.filter_by(active= True)
for i, qu in enumerate(sorted(queue_users, key=lambda qu: qu.position)):
# setattr(qu, "position", i)
pass
for i, qu in enumerate(sorted(queue.users, key=lambda qu: qu.position)):
setattr(qu, "position", i)
session.commit()
return queue
async def update_queue_users_message(msg: Message, queue: Queue):
queue = normalize_queue(queue)
filtered_users = list(filter(lambda qu: qu.active, queue.users))
users = sorted(filtered_users, key=lambda qu: qu.position)
users_str = "\n".join([f"{qu.position}. {qu.user.name}" for qu in users])
users_str = "\n".join([f"{qu.position}. {qu.user.name}" for qu in queue.users])
await bot.edit_message_text(
chat_id=msg.chat.id,
message_id=msg.id,
@ -167,19 +156,15 @@ async def start(msg: Message):
await bot.set_state(user_id=msg.from_user.id, state=States.default)
if len(msg.text.split()) > 1:
command, queue_id = msg.text.split()
if (
queue := session.query(Queue)
.filter_by(id=queue_id, active=True)
.first()
):
if queue := session.query(Queue).filter_by(id=queue_id).first():
if (
not session.query(QueueUser)
.filter_by(queue_id=queue.id, user_id=msg.from_user.id, active=True)
.filter_by(queue_id=queue.id, user_id=msg.from_user.id)
.first()
) and not len(user.takes_part_in_queues) > MAX_QUEUES_PARTS_IN:
last_user = (
session.query(QueueUser)
.filter_by(queue_id=queue.id, active=True)
.filter_by(queue_id=queue.id)
.order_by(QueueUser.position.desc())
.first()
)
@ -230,7 +215,7 @@ async def to_menu_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data[:2] == "p:")
async def proceed_user_handler(call: types.CallbackQuery):
queue_id = call.data[2:]
queue = session.query(Queue).filter_by(id=queue_id, active=True).first()
queue = session.query(Queue).filter_by(id=queue_id).first()
user = session.query(User).filter_by(id=call.from_user.id).first()
if next_queue_user := proceed_queue_user(queue, user):
try:
@ -265,11 +250,6 @@ async def proceed_user_handler(call: types.CallbackQuery):
await bot.answer_callback_query(callback_query_id=call.id)
@bot.callback_query_handler(func=lambda c: c.data == "dummy")
async def dummy_handler(call: types.CallbackQuery):
await bot.answer_callback_query(callback_query_id=call.id)
# Main menu
@ -277,7 +257,7 @@ async def dummy_handler(call: types.CallbackQuery):
async def new_queue_handler(call: types.CallbackQuery):
user = session.query(User).filter_by(id=call.from_user.id).first()
if user:
if len(list(filter(lambda q: q.active, user.owns_queues))) < MAX_QUEUES_OWN:
if len(user.owns_queues) < MAX_QUEUES_OWN:
queue = Queue(owner_id=call.from_user.id)
session.add(queue)
session.commit()
@ -297,7 +277,7 @@ async def new_queue_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data == "my_queues")
async def my_queues_handler(call: types.CallbackQuery):
user = session.query(User).filter_by(id=call.from_user.id).first()
queues = list(filter(lambda q: q.active, user.owns_queues))
queues = user.owns_queues
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
@ -310,9 +290,7 @@ async def my_queues_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data == "parts_queues")
async def parts_queues_handler(call: types.CallbackQuery):
user = session.query(User).filter_by(id=call.from_user.id).first()
queues = [
qu.queue for qu in list(filter(lambda q: q.active, user.takes_part_in_queues))
]
queues = [qu.queue for qu in user.takes_part_in_queues]
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
@ -349,7 +327,7 @@ async def about_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data[:2] == "t:")
async def queue_parts_handler(call: types.CallbackQuery, queue_id: str = None):
queue_id = call.data[2:] if not queue_id else queue_id
queue = session.query(Queue).filter_by(id=queue_id, active=True).first()
queue = session.query(Queue).filter_by(id=queue_id).first()
if not queue:
await bot.answer_callback_query(callback_query_id=call.id, text=textbook.error)
return None
@ -357,12 +335,7 @@ async def queue_parts_handler(call: types.CallbackQuery, queue_id: str = None):
user_id=call.from_user.id, chat_id=call.message.chat.id
) as state_data:
state_data["part_queue_id"] = queue_id
users_str = "\n".join(
[
f"{qu.position}. {qu.user.name}"
for qu in list(filter(lambda qu: qu.active, queue.users))
]
)
users_str = "\n".join([f"{qu.position}. {qu.user.name}" for qu in queue.users])
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
@ -381,10 +354,10 @@ async def leave_queue_handler(call: types.CallbackQuery):
if queue := await get_parting_queue_from_state_data(call):
queueuser = (
session.query(QueueUser)
.filter_by(queue_id=queue.id, user_id=call.from_user.id, active=True)
.filter_by(queue_id=queue.id, user_id=call.from_user.id)
.first()
)
setattr(queueuser, "active", False)
session.delete(queueuser)
session.commit()
normalize_queue(queue)
await bot.answer_callback_query(
@ -399,12 +372,7 @@ async def leave_queue_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data == "refresh_list")
async def refresh_list_handler(call: types.CallbackQuery):
if queue := await get_parting_queue_from_state_data(call):
users_str = "\n".join(
[
f"{qu.position}. {qu.user.name}"
for qu in list(filter(lambda q: q.active, queue.users))
]
)
users_str = "\n".join([f"{qu.position}. {qu.user.name}" for qu in queue.users])
try:
await bot.edit_message_text(
chat_id=call.message.chat.id,
@ -424,24 +392,23 @@ async def refresh_list_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data[:2] == "q:")
async def queue_handler(call: types.CallbackQuery, queue_id: str = None):
queue_id = call.data[2:] if not queue_id else queue_id
queue = session.query(Queue).filter_by(id=queue_id, active=True).first()
if queue:
async with bot.retrieve_data(
user_id=call.from_user.id, chat_id=call.message.chat.id
) as state_data:
state_data["queue_id"] = queue.id
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
text=get_queue_stats_text(queue),
reply_markup=keyboards.queue_menu(),
parse_mode="html",
)
else:
queue = session.query(Queue).filter_by(id=queue_id).first()
if not queue:
await bot.answer_callback_query(callback_query_id=call.id, text=textbook.error)
return None
async with bot.retrieve_data(
user_id=call.from_user.id, chat_id=call.message.chat.id
) as state_data:
state_data["queue_id"] = queue_id
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
text=get_queue_stats_text(queue),
reply_markup=keyboards.queue_menu(),
parse_mode="html",
)
await bot.answer_callback_query(callback_query_id=call.id)
# Queue menu
@ -449,12 +416,7 @@ async def queue_handler(call: types.CallbackQuery, queue_id: str = None):
@bot.callback_query_handler(func=lambda c: c.data == "get_queue_users")
async def get_queue_users_handler(call: types.CallbackQuery):
if queue := await get_queue_from_state_data(call):
users_str = "\n".join(
[
f"{qu.position}. {qu.user.name}"
for qu in list(filter(lambda qu: qu.active, queue.users))
]
)
users_str = "\n".join([f"{qu.position}. {qu.user.name}" for qu in queue.users])
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
@ -547,23 +509,11 @@ async def kick_first_handler(call: types.CallbackQuery):
@bot.callback_query_handler(func=lambda c: c.data == "swap_users")
async def swap_users_handler(call: types.CallbackQuery):
if queue := await get_queue_from_state_data(call):
pl = PaginatedList(queue.users, 8)
async with bot.retrieve_data(
user_id=call.from_user.id, chat_id=call.message.chat.id
) as state_data:
state_data["pl"] = pl
await bot.edit_message_text(
chat_id=call.message.chat.id,
message_id=call.message.id,
text=textbook.swap_users_first,
reply_markup=keyboards.swap_users_list(
queue_users=pl.get_current_page(),
current_page=pl.current_page,
total_pages=len(pl.divided_list),
),
parse_mode="html",
)
await bot.answer_callback_query(
callback_query_id=call.id,
text=textbook.in_development_plug,
show_alert=True,
)
@bot.callback_query_handler(func=lambda c: c.data == "refresh_users")
@ -571,10 +521,7 @@ async def refresh_users_handler(call: types.CallbackQuery):
if queue := await get_queue_from_state_data(call):
try:
users_str = "\n".join(
[
f"{qu.position}. {qu.user.name}"
for qu in list(filter(lambda qu: qu.active, queue.users))
]
[f"{qu.position}. {qu.user.name}" for qu in queue.users]
)
await bot.edit_message_text(
chat_id=call.message.chat.id,
@ -590,14 +537,6 @@ async def refresh_users_handler(call: types.CallbackQuery):
await bot.answer_callback_query(callback_query_id=call.id)
# Swap users
@bot.callback_query_handler(func=lambda c: c.data == "swap_users_page_up")
async def swap_users_page_up_handler(call: types.CallbackQuery):
pass
# Queue settings
@ -629,7 +568,7 @@ async def update_queue_name(msg: Message):
user_id=msg.from_user.id, chat_id=msg.chat.id
) as state_data:
queue_id = state_data.get("queue_id", None)
queue = session.query(Queue).filter_by(id=queue_id, active=True).first()
queue = session.query(Queue).filter_by(id=queue_id).first()
if not queue:
await bot.send_message(chat_id=msg.chat.id, text=textbook.edit_name_error)
return None
@ -670,10 +609,8 @@ async def delete_queue_approve_handler(call: types.CallbackQuery):
async def delete_queue_handler(call: types.CallbackQuery):
if queue := await get_queue_from_state_data(call):
for qu in queue.users:
setattr(
qu, "active", False
) # TODO: Use SQLAlchemy to cascade-delete all users
setattr(queue, "active", False)
session.delete(qu) # TODO: Use SQLAlchemy to cascade-delete all users
session.delete(queue)
session.commit()
await bot.answer_callback_query(
callback_query_id=call.id, text=textbook.queue_deleted
@ -751,53 +688,6 @@ async def changelog(msg: Message):
await bot.send_message(chat_id=msg.chat.id, text=file.read(), parse_mode="html")
@bot.message_handler(commands=["mydata"])
async def mydata(msg: Message):
async with bot.retrieve_data(
user_id=msg.from_user.id, chat_id=msg.chat.id
) as state_data:
await bot.send_message(chat_id=msg.chat.id, text=str(state_data))
@bot.message_handler(commands=["broadcast"])
async def broadcast(msg: Message):
if msg.from_user.id in admins:
await bot.set_state(user_id=msg.from_user.id, state=States.admin_broadcasting)
await bot.send_message(
chat_id=msg.chat.id,
text=textbook.admin_broadcasting,
reply_markup=keyboards.edit_name(),
)
@bot.callback_query_handler(
func=lambda c: c.data == "cancel", state=States.admin_broadcasting
)
async def cancel_broadcast(call: types.CallbackQuery):
await to_menu_handler(call)
@bot.message_handler(content_types=["text"], state=States.admin_broadcasting)
async def broadcast_message_handler(msg: Message):
if msg.from_user.id in admins:
counter = 0
for user in session.query(User):
try:
await bot.send_message(
chat_id=user.id,
text=msg.text,
)
counter += 1
except Exception as e:
continue
await bot.send_message(
chat_id=msg.chat.id,
text=textbook.broadcast_completed.format(count=counter),
)
await asyncio.sleep(1)
await start(msg)
# Launch

View File

@ -1,8 +1,3 @@
<b>v0.2.1-beta</b>
- Добавлена возможность миграции базы данных с помощью alembic
- Небольшая персистентность данных
- Теперь я могу броадкастить сообщения всем юзерам бота
<b>v0.1.8-beta</b>
- Исправлен баг, при котором после кика первого юзера очередь отображалась наоборот
- При создании очереди сразу генерится ссылка и создателю предлагается в нее вступить

View File

@ -1,4 +1,4 @@
MAX_QUEUES_OWN = 4
MAX_QUEUES_OWN = 6
MAX_QUEUES_PARTS_IN = 8
MAX_NAME_LENGTH = 40
MAX_QUEUE_NAME_LENGTH = 40

View File

View File

@ -1,5 +1,5 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, BigInteger, ForeignKey, Boolean
from sqlalchemy import Column, Integer, String, BigInteger, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship, backref
@ -26,7 +26,6 @@ class Queue(Base):
name = Column(String(40), default="Новая очередь")
description = Column(String(120), default=None)
owner_id = Column(BigInteger, ForeignKey("user.id"))
active = Column(Boolean, default=True)
users = relationship(
"QueueUser", backref="queue"
@ -40,4 +39,3 @@ class QueueUser(Base):
user_id = Column(BigInteger, ForeignKey("user.id"))
queue_id = Column(UUID(as_uuid=True), ForeignKey("queue.id"))
position = Column(Integer)
active = Column(Boolean, default=True)

View File

@ -2,7 +2,7 @@ from telebot.types import (
InlineKeyboardButton as button,
InlineKeyboardMarkup as keyboard,
)
from db.models import Queue, QueueUser
from db.models import Queue
def menu() -> keyboard:
@ -117,28 +117,3 @@ def to_menu_keyboard() -> keyboard:
[button(text="⬅️ В меню", callback_data="to_menu")],
]
)
def pagination_footer(current_page: int, total_pages: int) -> list:
return [
button(text="◀️", callback_data="page_down"),
button(text=f"{current_page}/{total_pages}", callback_data="dummy"),
button(text="▶️", callback_data="page_up"),
]
def swap_users_list(
queue_users: list[QueueUser], current_page: int, total_pages: int
) -> keyboard:
kb = [
[
button(
text=f"{qu.position}. {qu.user.name}",
callback_data=f"action:{qu.id}",
)
]
for qu in queue_users
]
kb.append(pagination_footer(current_page, total_pages))
kb.append([button(text="⬅️ Назад", callback_data="get_queue_users")])
return keyboard(kb)

View File

@ -1,34 +0,0 @@
from typing import Union
class PaginatedList:
def __init__(self, data: list, page_size: int, current_page:int=0):
self.data = data
self.page_size = page_size
self.current_page = current_page
@property
def divided_list(self):
return [
self.data[x : x + self.page_size]
for x in range(0, len(self.data), self.page_size)
]
def get_page(self, page: int) -> Union[list, None]:
if page < len(self.divided_list):
return self.divided_list[page]
return None
def get_current_page(self) -> list:
return self.get_page(self.current_page)
def page_up(self) -> Union[list, None]:
if self.current_page + 1 < len(self.divided_list):
self.current_page += 1
return self.get_page(self.current_page)
return None
def page_down(self) -> Union[list, None]:
if self.current_page - 1 >= 0:
self.current_page -= 1
return self.get_page(self.current_page)
return None

View File

@ -28,9 +28,6 @@ kick_first_error = (
"Действие не выполнено, возможно вы уже вышли из очереди, или очередь пуста?"
)
swap_users_first = "Выбери первого пользователя"
swap_users_second = "Выбран: {name}. Поменять с:"
parts_queues_menu = "Ты принимаешь участие в {count} очереди/ей"
part_queue = "Очередь <b>{name}</b>\n\nУчастники:\n{users_str}"
leaved_queue = "Ты вышел из очереди {name}"
@ -46,6 +43,3 @@ stats = "Количество пользователей: {users_count}\nКол
about = "Бот для очередей.\n\nРазработчик - ollyhearn.\nЯ всегда открыт для вопросов и предложений: @OllyHearn\n\nv0.1.8-beta"
groups_plug = "Всем привет, я бот для очередей! В настоящее время идет активная разработка, так что я пока не могу полностью функционировать в группах, но вы всегда можете запустить меня в личном диалоге, создать очередь, и отправить ссылку на очередь сюда. Функционал будет доработан, а пока пользуйтесь мной в личке:\n\nhttps://t.me/queue_senko_bot"
in_development_plug = "Функция в разработке ¯\_(ツ)_/¯"
admin_broadcasting = "Введите сообщение, или нажмите Отменить"
broadcast_completed = "Сообщение разослано {count} юзерам!"

View File

@ -1,8 +1,7 @@
pytelegrambotapi
pytelegrambotapi==4.14.1
asyncio
aiohttp
psycopg-binary
pydantic
sqlalchemy
psycopg2-binary
alembic

View File

@ -13,8 +13,6 @@ services:
interval: 2s
timeout: 2s
retries: 5
ports:
- 5432
bot:
build:
context: bot
@ -27,11 +25,6 @@ services:
HOST: postgres
PORT: 5432
restart: always
volumes:
- ./persistent_data/.state-save:/app/.state-save:rw
- ./bot/app:/app:z
ports:
- "4444:4444" # debugger port
depends_on:
postgres:
condition: service_healthy