second one

This commit is contained in:
2023-11-14 16:15:21 +03:00
parent 2ebc03606d
commit 8b9298b34c
6 changed files with 199 additions and 46 deletions

1
.gitignore vendored
View File

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

View File

@ -35,22 +35,18 @@ session: Session = None
class States(StatesGroup): class States(StatesGroup):
default = State() default = State()
newfund_amount = State() newfund_amount = State()
close_fund = State()
# Utils # Utils
def get_fund_text(fund: Fund): def get_fund_text(fund: Fund):
count = len(fund.members) count = fund.members.count()
contributors = len(list(filter(lambda m: m.contributed, fund.members))) contributors = fund.members.filter(FundMember.contributed).count()
personal_amount = math.ceil(fund.amount / count) personal_amount = math.ceil(fund.amount / count)
return ( return textbook.fund.format(
"🟢 {name}\n\n", active="🟢" if fund.active else "🔴",
"💵 Сумма: {amount}р\n\n",
"<b>Каждый скидывает по {personal_amount}</b>\n",
"Уже собрано: {collected_amount}\n\n",
"👥 Скинули: {contributors}/{count} чел.",
).format(
name=fund.name, name=fund.name,
amount=fund.amount, amount=fund.amount,
personal_amount=personal_amount, personal_amount=personal_amount,
@ -74,7 +70,7 @@ def get_user(tg_user: TgUser) -> User:
def get_group(tg_chat: TgChat) -> Group: def get_group(tg_chat: TgChat) -> Group:
if group := session.query(User).filter(User.id == tg_chat.id).first(): if group := session.query(Group).filter(Group.id == tg_chat.id).first():
return group return group
group = Group( group = Group(
id=tg_chat.id, id=tg_chat.id,
@ -86,6 +82,7 @@ def get_group(tg_chat: TgChat) -> Group:
# Bot logic # Bot logic
@bot.message_handler(commands=["start"]) @bot.message_handler(commands=["start"])
async def start(msg: Message): async def start(msg: Message):
if msg.chat.type == "private": if msg.chat.type == "private":
@ -103,7 +100,10 @@ async def start(msg: Message):
# session.add(user) # session.add(user)
# session.commit() # session.commit()
user = get_user(msg.from_user) user = get_user(msg.from_user)
await bot.send_message(chat_id=msg.chat.id, text=textbook.start_private.format(count=len(user.fund_members))) await bot.send_message(
chat_id=msg.chat.id,
text=textbook.start_private.format(count=user.fund_members.count()),
)
elif msg.chat.type in ("group", "supergroup"): elif msg.chat.type in ("group", "supergroup"):
get_group(msg.chat) get_group(msg.chat)
@ -122,7 +122,7 @@ async def setup(msg: Message):
@bot.callback_query_handler(func=lambda c: c.data == "register_group_member") @bot.callback_query_handler(func=lambda c: c.data == "register_group_member")
async def register_group_member(call: types.CallbackQuery): async def register_group_member(call: types.CallbackQuery):
new = False new = False
user = get_user(call.from_user.id) get_user(call.from_user)
if ( if (
group_member := session.query(GroupMember) group_member := session.query(GroupMember)
.filter( .filter(
@ -150,10 +150,11 @@ async def register_group_member(call: types.CallbackQuery):
@bot.message_handler(commands=["newfund"]) @bot.message_handler(commands=["newfund"])
async def newfund(msg: Message): async def newfund(msg: Message):
if msg.chat.type in ("group", "supergroup"): if msg.chat.type in ("group", "supergroup"):
if session.query(Group).filter(Group.id == msg.chat.id).first(): if group := get_group(msg.chat):
if group.group_members.count():
if ( if (
session.query(Fund) session.query(Fund)
.filter(Fund.group_id == msg.chat.id, Fund.active == True) .filter(Fund.group_id == msg.chat.id, Fund.active)
.first() .first()
): ):
await bot.send_message( await bot.send_message(
@ -169,14 +170,15 @@ async def newfund(msg: Message):
await bot.send_message( await bot.send_message(
chat_id=msg.chat.id, chat_id=msg.chat.id,
text=textbook.newfund_amount.format( text=textbook.newfund_amount.format(
user=user_link(msg.from_user)), user=user_link(msg.from_user)
),
reply_markup=keyboards.cancel(), reply_markup=keyboards.cancel(),
parse_mode="html", parse_mode="html",
) )
else: else:
await bot.send_message( await bot.send_message(
chat_id=msg.chat.id, chat_id=msg.chat.id,
text=textbook.not_initialized text=textbook.not_set_up,
) )
@ -187,6 +189,7 @@ async def cancel_newfund_amount(call: types.CallbackQuery):
await bot.set_state( await bot.set_state(
user_id=call.from_user.id, chat_id=call.message.chat.id, state=States.default user_id=call.from_user.id, chat_id=call.message.chat.id, state=States.default
) )
await bot.delete_message(chat_id=call.message.chat.id, message_id=call.message.id)
await bot.answer_callback_query( await bot.answer_callback_query(
callback_query_id=call.id, callback_query_id=call.id,
text=textbook.cancel, text=textbook.cancel,
@ -208,15 +211,13 @@ async def newfund_amount(msg: Message):
user_id=msg.from_user.id, chat_id=msg.chat.id, state=States.default user_id=msg.from_user.id, chat_id=msg.chat.id, state=States.default
) )
fund = Fund( fund = Fund(
owner_id=msg.from_user.id, group_id=msg.chat.id, amount=int( owner_id=msg.from_user.id, group_id=msg.chat.id, amount=int(msg.text)
msg.text)
) )
session.add(fund) session.add(fund)
for group_member in session.query(GroupMember).filter( for group_member in session.query(GroupMember).filter(
GroupMember.group_id == msg.chat.id GroupMember.group_id == msg.chat.id
): ):
fund_member = FundMember( fund_member = FundMember(user_id=group_member.user.id, fund_id=fund.id)
user_id=group_member.user.id, fund_id=fund.id)
session.add(fund_member) session.add(fund_member)
session.commit() session.commit()
await bot.send_message( await bot.send_message(
@ -228,6 +229,7 @@ async def newfund_amount(msg: Message):
chat_id=msg.chat.id, chat_id=msg.chat.id,
text=get_fund_text(fund), text=get_fund_text(fund),
reply_markup=keyboards.fund_markup(), reply_markup=keyboards.fund_markup(),
parse_mode="html",
) )
else: else:
@ -241,6 +243,132 @@ async def newfund_amount(msg: Message):
@bot.message_handler(commands=["fund"]) @bot.message_handler(commands=["fund"])
async def fund(msg: Message): async def fund(msg: Message):
if msg.chat.type in ("group", "supergroup"):
group = get_group(msg.chat)
if (
fund := session.query(Fund)
.filter(Fund.group_id == group.id, Fund.active == True)
.first()
):
await bot.send_message(
chat_id=msg.chat.id,
text=get_fund_text(fund),
reply_markup=keyboards.fund_markup(),
parse_mode="html",
)
else:
await bot.send_message(
chat_id=msg.chat.id,
text=textbook.fund_not_found,
)
@bot.callback_query_handler(func=lambda c: c.data == "close_fund")
async def close_fund_prompt(call: types.CallbackQuery):
if group := get_group(call.message.chat):
if fund := group.funds.filter(Fund.active).first():
if call.from_user.id == fund.owner_id:
await bot.edit_message_text(
text=textbook.close_fund_prompt,
chat_id=call.message.chat.id,
message_id=call.message.id,
reply_markup=keyboards.yes_no(),
parse_mode="html",
)
await bot.set_state(
user_id=call.from_user.id,
chat_id=call.message.chat.id,
state=States.close_fund,
)
else:
await bot.answer_callback_query(
callback_query_id=call.id,
text=textbook.not_owner.format(owner_name=fund.owner.name),
)
else:
await bot.send_message(
chat_id=call.message.chat.id,
text=textbook.fund_not_found,
)
@bot.callback_query_handler(
func=lambda c: c.data in ("yes", "no"), state=States.close_fund
)
async def close_fund(call: types.CallbackQuery):
if group := get_group(call.message.chat):
if fund := group.funds.filter(Fund.active).first():
if call.from_user.id == fund.owner_id:
if call.data == "yes":
setattr(fund, "active", False)
session.commit()
await bot.edit_message_text(
text=textbook.fund_closed.format(
name=fund.name, fund_text=get_fund_text(fund)
),
chat_id=call.message.chat.id,
message_id=call.message.id,
parse_mode="html",
)
elif call.data == "no":
await bot.edit_message_text(
text=get_fund_text(fund),
chat_id=call.message.chat.id,
message_id=call.message.id,
reply_markup=keyboards.fund_markup(),
parse_mode="html",
)
else:
await bot.answer_callback_query(
callback_query_id=call.id,
text=textbook.not_owner.format(owner_name=fund.owner.name),
)
else:
await bot.send_message(
chat_id=call.message.chat.id,
text=textbook.fund_not_found,
)
@bot.callback_query_handler(func=lambda c: c.data == "contributed")
async def contributed(call: types.CallbackQuery):
group = get_group(call.message.chat)
user = get_user(call.from_user)
if fund := group.funds.filter(Fund.active).first():
if fund_user := fund.members.filter(
FundMember.user_id == call.from_user.id
).first():
if not fund_user.contributed:
setattr(fund_user, "contributed", True)
session.commit()
await bot.answer_callback_query(
callback_query_id=call.id,
text=textbook.contributed,
)
await bot.edit_message_text(
text=get_fund_text(fund),
chat_id=call.message.chat.id,
message_id=call.message.id,
reply_markup=keyboards.fund_markup(),
parse_mode="html",
)
else:
await bot.answer_callback_query(
callback_query_id=call.id,
text=textbook.already_contributed,
show_alert=True,
)
else:
await bot.answer_callback_query(
callback_query_id=call.id,
text=textbook.not_fund_member,
show_alert=True,
)
else:
await bot.answer_callback_query(
callback_query_id=call.id, text=textbook.fund_not_found, show_alert=True
)
@bot.message_handler(commands=["mystate"]) @bot.message_handler(commands=["mystate"])

View File

@ -16,9 +16,9 @@ class User(Base):
name = Column(String(64)) name = Column(String(64))
username = Column(String(32)) username = Column(String(32))
owns_funds = relationship("Fund", backref="owner") owns_funds = relationship("Fund", backref="owner", lazy="dynamic")
fund_members = relationship("FundMember", backref="user") fund_members = relationship("FundMember", backref="user", lazy="dynamic")
group_members = relationship("GroupMember", backref="user") group_members = relationship("GroupMember", backref="user", lazy="dynamic")
class Group(Base): class Group(Base):
@ -26,9 +26,9 @@ class Group(Base):
id = Column(BigInteger, primary_key=True) id = Column(BigInteger, primary_key=True)
funds = relationship("Fund", backref="group") funds = relationship("Fund", backref="group", lazy="dynamic")
group_members = relationship("GroupMember", backref="group") group_members = relationship("GroupMember", backref="group", lazy="dynamic")
class GroupMember(Base): class GroupMember(Base):
@ -52,7 +52,7 @@ class Fund(Base):
amount = Column(Integer) amount = Column(Integer)
active = Column(Boolean, default=True) active = Column(Boolean, default=True)
members = relationship("FundMember", backref="fund") members = relationship("FundMember", backref="fund", lazy="dynamic")
class FundMember(Base): class FundMember(Base):

View File

@ -23,6 +23,16 @@ def cancel() -> keyboard:
def fund_markup() -> keyboard: def fund_markup() -> keyboard:
return keyboard( return keyboard(
keyboard=[ keyboard=[
[button(text="✅ Я скинул", callback_data="contributed")],
[button(text="🏁 Завершить сбор", callback_data="close_fund")], [button(text="🏁 Завершить сбор", callback_data="close_fund")],
] ]
) )
def yes_no() -> keyboard:
return keyboard(
keyboard=[
[button(text="✅ Да", callback_data="yes")],
[button(text="❌ Нет", callback_data="no")],
]
)

View File

@ -15,3 +15,15 @@ not_number = 'Вы ввели не число. {user}, напишите сумм
fund_created = "Создан новый сбор: {fund}" fund_created = "Создан новый сбор: {fund}"
fund_not_found = "На данный момент в этом чате сборов нет! Создать новый - /newfund" fund_not_found = "На данный момент в этом чате сборов нет! Создать новый - /newfund"
fund = "{active} {name}\n\n💵 Сумма: {amount}р\n\n<b>Каждый скидывает по {personal_amount}р</b>\nУже собрано: {collected_amount}р\n👥 Скинули: {contributors}/{count} чел."
close_fund_prompt = "Вы уверены? Это завершит <b>активный в данный момент сбор</b>!"
fund_closed = 'Сбор "<b>{name}</b>" закрыт! Вот его данные:\n\n{fund_text}'
not_owner = "Вы не являетесь создателем этого сбора, обратитесь к {owner_name}, если хотите его закрыть"
not_set_up = "В группе меценатов нет ни одного участника! Вы точно прописывали /setup и все желающие участвовать в сборах приняли участие?"
not_fund_member = "Вы не являетесь участником этого сбора! Пропишите /setup и попросите создать новый сбор!"
contributed = "Вы отметились!"
already_contributed = "Вы уже отмечались ранее. Не забудьте уведомить создателя сбора, если вы решили отказаться от участия!"

View File

@ -23,6 +23,8 @@ services:
DB_NAME: db DB_NAME: db
HOST: postgres HOST: postgres
PORT: 5432 PORT: 5432
volumes:
- ./.state-save:/app/.state-save/:rw
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy