diff --git a/bot/app/bot.py b/bot/app/bot.py
index 9555e97..99632a1 100644
--- a/bot/app/bot.py
+++ b/bot/app/bot.py
@@ -26,6 +26,7 @@ import keyboards
# DB
from db.base import Session, engine, Base
from db.models import User, Group, GroupMember, Fund, FundMember
+from sqlalchemy.orm import joinedload
bot = AsyncTeleBot(token, state_storage=StatePickleStorage())
@@ -35,6 +36,7 @@ session: Session = None
class States(StatesGroup):
default = State()
newfund_amount = State()
+ newfund_description = State()
close_fund = State()
@@ -49,6 +51,7 @@ def get_fund_text(fund: Fund):
active="🟢" if fund.active else "🔴",
name=fund.name,
amount=fund.amount,
+ description=fund.description,
personal_amount=personal_amount,
collected_amount=personal_amount * contributors,
contributors=contributors,
@@ -87,9 +90,16 @@ def get_group(tg_chat: TgChat) -> Group:
async def start(msg: Message):
if msg.chat.type == "private":
user = get_user(msg.from_user)
+ fund_members_count = (
+ session.query(FundMember)
+ .join(Fund)
+ .filter(Fund.owner_id == user.id, Fund.active == True)
+ .options(joinedload(FundMember.fund))
+ .count()
+ )
await bot.send_message(
chat_id=msg.chat.id,
- text=textbook.start_private.format(count=user.fund_members.count()),
+ text=textbook.start_private.format(count=fund_members_count),
)
elif msg.chat.type in ("group", "supergroup"):
@@ -183,22 +193,64 @@ async def cancel_newfund_amount(call: types.CallbackQuery):
)
+@bot.callback_query_handler(
+ func=lambda c: c.data == "cancel", state=States.newfund_description
+)
+async def cancel_newfund_description(call: types.CallbackQuery):
+ await bot.set_state(
+ 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(
+ callback_query_id=call.id,
+ text=textbook.cancel,
+ )
+
+
@bot.message_handler(content_types=["text"], state=States.newfund_amount)
async def newfund_amount(msg: Message):
- if not session.query(User).filter(User.id == msg.from_user.id).first():
- user = User(
- id=msg.from_user.id,
- name=msg.from_user.first_name,
- username=msg.from_user.username,
- )
- session.add(user)
- session.commit()
if msg.text.isdigit():
+ async with bot.retrieve_data(
+ user_id=msg.from_user.id, chat_id=msg.chat.id
+ ) as state_data:
+ state_data["newfund_amount"] = int(msg.text)
+ await bot.set_state(
+ user_id=msg.from_user.id,
+ chat_id=msg.chat.id,
+ state=States.newfund_description,
+ )
+ await bot.send_message(
+ chat_id=msg.chat.id,
+ text=textbook.newfund_description,
+ reply_markup=keyboards.cancel(),
+ parse_mode="html",
+ )
+
+ else:
+ await bot.send_message(
+ chat_id=msg.chat.id,
+ text=textbook.not_number.format(user=user_link(msg.from_user)),
+ reply_markup=keyboards.cancel(),
+ parse_mode="html",
+ )
+
+
+@bot.message_handler(content_types=["text"], state=States.newfund_description)
+async def newfund_description(msg: Message):
+ if len(msg.text) < 121:
await bot.set_state(
user_id=msg.from_user.id, chat_id=msg.chat.id, state=States.default
)
+ async with bot.retrieve_data(
+ user_id=msg.from_user.id, chat_id=msg.chat.id
+ ) as state_data:
+ amount = state_data.get("newfund_amount")
fund = Fund(
- owner_id=msg.from_user.id, group_id=msg.chat.id, amount=int(msg.text)
+ owner_id=msg.from_user.id,
+ group_id=msg.chat.id,
+ description=msg.text,
+ amount=amount,
+ name=datetime.today().strftime("%d.%m"),
)
session.add(fund)
for group_member in session.query(GroupMember).filter(
@@ -222,7 +274,7 @@ async def newfund_amount(msg: Message):
else:
await bot.send_message(
chat_id=msg.chat.id,
- text=textbook.not_number.format(user=user_link(msg.from_user)),
+ text=textbook.newfund_description_too_long,
reply_markup=keyboards.cancel(),
parse_mode="html",
)
@@ -411,6 +463,72 @@ async def remind(msg: Message):
)
+@bot.message_handler(commands=["dmremind"])
+async def dmremind(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()
+ ):
+ not_contributed = fund.members.filter(FundMember.contributed == False).all()
+ if len(not_contributed):
+ counter = 0
+ not_sent = []
+ for member in not_contributed:
+ try:
+ await asyncio.sleep(0.1)
+ await bot.send_message(
+ chat_id=member.user.id,
+ text=textbook.dmremind.format(
+ fund_name=fund.name, chat_name=msg.chat.title
+ ),
+ )
+ counter += 1
+ except Exception:
+ not_sent.append(member)
+ s = textbook.dmremind_completed.format(
+ sent_count=counter, members_count=len(not_contributed)
+ )
+
+ if not_sent:
+ s += "\n" + textbook.dmremind_not_sent_list.format(
+ members=", ".join([m.user.name for m in not_sent])
+ )
+ await bot.send_message(
+ chat_id=msg.chat.id,
+ text=s,
+ parse_mode="html",
+ )
+ else:
+ await bot.send_message(
+ chat_id=msg.chat.id,
+ text=textbook.remind_already.format(fund_name=fund.name),
+ parse_mode="html",
+ )
+ else:
+ await bot.send_message(
+ chat_id=msg.chat.id,
+ text=textbook.fund_not_found,
+ )
+
+
+@bot.message_handler(commands=["setup_list"])
+async def setup_list(msg: Message):
+ if msg.chat.type in ("group", "supergroup"):
+ group = get_group(msg.chat)
+ members = group.group_members
+ await bot.send_message(
+ chat_id=msg.chat.id,
+ text=textbook.setup_list.format(
+ count=members.count(),
+ members=", ".join(m.user.name for m in members.all()),
+ ),
+ parse_mode="html",
+ )
+
+
@bot.message_handler(commands=["mystate"])
async def mystate(msg: Message):
state = await bot.get_state(user_id=msg.from_user.id)
diff --git a/bot/app/textbook.py b/bot/app/textbook.py
index 65fc9b1..433a798 100644
--- a/bot/app/textbook.py
+++ b/bot/app/textbook.py
@@ -10,6 +10,10 @@ user_left = "Вы отказались от участия в сборах в э
newfund_already_exists = "Предыдущий сбор все еще активен! Пропишите /fund, чтобы показать его, и завершите его, если необходимо создать новый!"
newfund_amount = 'Отлично, новый сбор. {user}, напишите сумму сбора ответом на это сообщение, или нажмите кнопку "❌ Отменить"'
+newfund_description = "Хорошо, теперь напишите описание сбора ответом на это сообщение (не более 120 символов). Это может быть номер или ссылка куда кидать деньги, к примеру."
+newfund_description_too_long = (
+ "Слишком длинное описание, лимит - 120 символов, попробуйте еще раз!"
+)
cancel = "Хорошо, проехали"
not_number = 'Вы ввели не число. {user}, напишите сумму сбора ответом на это сообщение, или нажмите кнопку "❌ Отменить"'
fund_created = "Создан новый сбор: {fund}"
@@ -17,7 +21,7 @@ fund_created = "Создан новый сбор: {fund}"
fund_not_found = "На данный момент в этом чате сборов нет! Создать новый - /newfund"
-fund = "{active} {name}\n\n💵 Сумма: {amount}р\n\nКаждый скидывает по {personal_amount}р\nУже собрано: {collected_amount}р\n👥 Скинули: {contributors}/{count} чел."
+fund = "{active} {name}\n\n💵 Сумма сбора: {amount}р\n\n{description}\n\nКаждый скидывает по {personal_amount}р\nУже собрано: {collected_amount}р\n👥 Скинули: {contributors}/{count} чел."
close_fund_prompt = "Вы уверены? Это завершит активный в данный момент сбор!"
fund_closed = 'Сбор "{name}" закрыт! Вот его данные:\n\n{fund_text}'
@@ -33,3 +37,8 @@ remind_already = "На сбор {fund_name} уже все скинули
fund_completed = (
"Сбор {fund_name} завершен, поздравляю всех, и в особенности {owner_str} 🎉"
)
+dmremind = "Ты забыл(а) скинуться на {fund_name} в чате {chat_name}!"
+dmremind_completed = "Сообщение разослано {sent_count} из {members_count} юзерам!"
+dmremind_not_sent_list = "Не получилось разослать юзерам:\n\n{members}\n\nМожешь написать им в личку сам(а)? Они забыли активировать меня в лс("
+
+setup_list = "В сборах в этом чате сейчас участвует {count} человек. Вот они, слева направо:\n\n{members}\n\nДля добавления новых пропишите /setup и нажмите на кнопочку"