-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
763 lines (665 loc) · 47.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
# Импорт библиотек
import json
import time
import traceback
import sqlite3
import datetime
import threading
import requests
import telebot
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, InputMediaPhoto
# Импорт данных из конфига
from config import API_TOKEN, CHANNEL_ID, CHANNEL_INVITE_LINK, ADMIN_PASSWORD
# Инициализация бота
bot = telebot.TeleBot(API_TOKEN)
# Путь к JSON-файлу с администраторами
JSON_FILE = "jsons/admins.json"
# Чтение JSON-файла
def load_program_data():
# Чтение данных из JSON-файла
with open('jsons/program_data.json', 'r', encoding='utf-8') as file:
# Возвращение данных из JSON-файла
return json.load(file)
# Сохранение данных в JSON-файл
def save_program_data(data):
# Сохранение данных в JSON-файл
with open('jsons/program_data.json', 'w', encoding='utf-8') as file:
# Запись данных в JSON-файл
json.dump(data, file, ensure_ascii=False, indent=4)
# Хранилище для временных данных загрузки программы администратором (фото и текст) во время обновления программы в чате
pending_data = {}
# Загрузка данных администраторов из JSON
def load_admins():
# Попытка чтения данных из JSON-файла
try:
# Чтение данных из JSON-файла
with open(JSON_FILE, "r", encoding="utf-8") as file:
# Возвращение данных из JSON-файла
return json.load(file).get("admins", [])
# Обработка ошибки отсутствия файла
except FileNotFoundError:
# Возвращение пустого списка
return []
# Обработка ошибки некорректного формата JSON
except json.JSONDecodeError:
return []
# Сохранение списка администраторов в JSON
def save_admins(admins):
# Сохранение данных в JSON-файл
with open(JSON_FILE, "w", encoding="utf-8") as file:
# Запись данных в JSON-файл
json.dump({"admins": admins}, file, ensure_ascii=False, indent=4)
# Инициализация списка администраторов
ADMIN_ID = load_admins()
# Сохранение данных о подписке в базе данных SQLite (subscriptions.db)
def save_subscription(user_id, subscription_type=None, payment_status=None, subscription_start=None,
subscription_end=None):
# Попытка подключения к базе данных
try:
# Подключение к базе данных SQLite (subscriptions.db)
conn = sqlite3.connect('subscriptions.db')
# Создание курсора для работы с базой данных (выполнение запросов)
cursor = conn.cursor()
# Попытка выполнения запроса к базе данных
cursor.execute('SELECT * FROM users WHERE user_id = ?', (user_id,))
# Проверка наличия пользователя в базе данных по ID пользователя (user_id)
if cursor.fetchone():
# Обновление данных о подписке пользователя в базе данных
cursor.execute('''
UPDATE users
SET subscription_type = COALESCE(?, subscription_type),
payment_status = COALESCE(?, payment_status),
subscription_start = COALESCE(?, subscription_start),
subscription_end = COALESCE(?, subscription_end)
WHERE user_id = ?''', (subscription_type, payment_status, subscription_start, subscription_end, user_id))
# Если пользователя нет в базе данных
else:
# Добавление данных о подписке пользователя в базу данных
cursor.execute('''
INSERT INTO users (user_id, subscription_type, payment_status, subscription_start, subscription_end)
VALUES (?, ?, ?, ?, ?)''',
(user_id, subscription_type, payment_status, subscription_start, subscription_end))
# Сохранение изменений в базе данных (обязательно для обновления данных)
conn.commit()
# Обработка ошибки подключения к базе данных
except sqlite3.Error as e:
# Вывод сообщения об ошибке в консоль
print(f"Database error: {e}")
# Завершение работы с базой данных (закрытие подключения)
finally:
# Закрытие подключения к базе данных
conn.close()
# Обработчик команды /start (начало работы с ботом)
@bot.message_handler(commands=['start'])
# Функция-обработчик команды /start
def start_handler(message):
# Попытка отправки видео-сообщения и кнопок пользователю
try:
# Создание кнопок для выбора программы на 1 или 2 месяца
markup = InlineKeyboardMarkup()
# Добавление кнопок в разметку (markup)
markup.add(InlineKeyboardButton('Программа на 1 месяц', callback_data='program_1_month'))
markup.add(InlineKeyboardButton('Программа на 2 месяца', callback_data='program_2_months'))
# Открытие видео-файла для отправки пользователю
video = open('start.mp4', 'rb')
# Отправка видео-сообщения и кнопок пользователю
bot.send_video(message.chat.id, video,
caption="Сообщество «Áмити» - для тех, кто хочет расти и выйти на новый уровень!"
"\n\nУникальность нашего подхода заключается в объединении многолетнего стилистического"
" опыта и коучинговых методик для всестороннего развития резидентов сообщества! "
"\n\nЧто тебя ждет?\n"
"\n📌Встречи на темы продвижения, профессионального роста, стратегии жизни и бизнеса от"
" основателей сообщества и приглашенных экспертов."
"\n\n📌Менторинги/воркшопы/разборы кейсов участников."
"\n\n📌 Пространство, где вы найдете поддержку и ответы на любые свои профессиональные"
" и личные вопросы от основателей и других участников сообщества. "
"\n\n📌 Специальные бонусы от основателей сообщества "
"\n\nИзучай программу ниже, выбирай формат участия и до встречи в комьюнити!🫂🤍",
reply_markup=markup)
# Обработка ошибки при отправке сообщения
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in start_handler: {e}")
# Обработчик команды /admin_commands (список команд для администратора)
@bot.message_handler(commands=['admin_commands'])
# Функция-обработчик команды /admin_commands
def admin_commands(message):
# Проверка наличия ID пользователя в списке администраторов
if str(message.chat.id) not in ADMIN_ID:
# Отправка сообщения о недостатке прав для выполнения команды
bot.send_message(message.chat.id, "У вас нет прав для выполнения этой команды.")
# Прерывание выполнения функции (выход из функции)
return
# Отправка сообщения со списком команд для администратора
bot.send_message(
message.chat.id,
"Список команд для администратора:\n"
"/check_users_subscription - Проверить подписки пользователей\n"
"/im_admin - Добавить себя в администраторы\n"
"/update_program - Обновить программу"
)
# Обработчик команды /check_users_subscription (проверка подписок пользователей)
@bot.message_handler(commands=['check_users_subscription'])
# Функция-обработчик команды /check_users_subscription
def check_users_subscription(message):
# Проверка наличия ID пользователя в списке администраторов
if str(message.chat.id) not in ADMIN_ID:
# Отправка сообщения о недостатке прав для выполнения команды
bot.send_message(message.chat.id, "У вас нет прав для выполнения этой команды.")
# Прерывание выполнения функции (выход из функции)
return
# Попытка проверки подписок пользователей в базе данных
try:
# Подключение к базе данных SQLite (subscriptions.db)
conn = sqlite3.connect('subscriptions.db')
# Создание курсора для работы с базой данных (выполнение запросов)
cursor = conn.cursor()
# Выполнение запроса к базе данных для выборки данных о пользователях с подтвержденным статусом оплаты
cursor.execute(
'SELECT user_id, subscription_start, subscription_end FROM users WHERE payment_status = "confirmed"')
# Получение результатов запроса (всех строк)
rows = cursor.fetchall()
# Закрытие подключения к базе данных
conn.close()
# Проверка наличия подписок пользователей в базе данных
if len(rows) == 0:
# Отправка сообщения об отсутствии подписок
bot.send_message(message.chat.id, "Подписок не найдено.")
# Прерывание выполнения функции (выход из функции)
return
# Отправка сообщения с информацией о подписках пользователей
for row in rows:
# Разделение данных о пользователе на ID, дату начала и окончания подписки
user_id, subscription_start, subscription_end = row
# Преобразование строковых данных в формат даты и времени
subscription_start = datetime.datetime.fromisoformat(subscription_start)
subscription_end = datetime.datetime.fromisoformat(subscription_end)
# Отправка сообщения с информацией о подписке пользователей
bot.send_message(
message.chat.id,
f"User ID: {user_id}\nSubscription start: {subscription_start}\nSubscription end: {subscription_end}"
)
# Задержка в 1 секунду (для предотвращения блокировки бота)
time.sleep(1)
# Обработка ошибки подключения к базе данных
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in check_users_subscription: {e}")
# Команда для администратора добавления себя в список администраторов по паролю
@bot.message_handler(commands=['im_admin'])
# Функция-обработчик команды /im_admin
def check_password_admin_and_add_admin_id(message):
# Проверка наличия пароля в сообщении пользователя
if len(message.text.split(" ")) != 2:
# Отправка сообщения с просьбой ввести пароль
bot.send_message(message.chat.id, "Введите команду в формате `/im_admin <пароль>`", parse_mode="Markdown")
# Прерывание выполнения функции (выход из функции)
return
# Получение пароля из сообщения пользователя
password = message.text.split(" ")[1]
# Проверка правильности введенного пароля
if password == ADMIN_PASSWORD:
# Получение ID чата пользователя
chat_id = str(message.chat.id)
# Проверка наличия ID пользователя в списке администраторов
if chat_id in ADMIN_ID:
# Отправка сообщения о наличии пользователя в списке администраторов
bot.send_message(message.chat.id, "Вы уже добавлены в администраторы.")
# Добавление ID пользователя в список администраторов
else:
# Добавление ID пользователя в список администраторов
ADMIN_ID.append(chat_id)
# Сохранение обновленного списка администраторов в JSON
save_admins(ADMIN_ID)
# Отправка сообщения об успешном добавлении пользователя в список администраторов
bot.send_message(message.chat.id, "Вы добавлены в администраторы.")
# Отправка сообщения о неверном пароле
else:
bot.send_message(message.chat.id, "Неверный пароль.")
# Обработчик команды /update_program (обновление программы)
@bot.message_handler(commands=['update_program'])
# Функция-обработчик команды /update_program
def update_program(message):
# Проверка наличия ID пользователя в списке администраторов
if str(message.chat.id) not in ADMIN_ID:
bot.send_message(message.chat.id, "У вас нет прав для выполнения этой команды.")
return
# Отправка сообщения с инструкциями для обновления программы
msg = bot.send_message(
message.chat.id,
"Введите тип программы (`program_1_month` или `program_2_months`):",
parse_mode="Markdown"
)
# Регистрация следующего шага для обработки введенного типа программы
bot.register_next_step_handler(msg, get_program_type)
# Следующие функции обработки шагов обновления программы администратором
def get_program_type(message):
# Попытка обработки типа программы из сообщения пользователя
try:
# Получение типа программы из сообщения пользователя (удаление лишних пробелов)
program_type = message.text.strip()
# Проверка корректности введенного типа программы (должен быть одним из двух вариантов)
if program_type not in ['program_1_month', 'program_2_months']:
# Отправка сообщения об ошибке ввода типа программы (если введен неверный тип)
bot.send_message(message.chat.id, "Неверный тип программы. Попробуйте снова.")
# Завершение выполнения функции (выход из функции)
return
# Отправка сообщения с запросом нового текста программы
bot.send_message(message.chat.id, f"Вы выбрали {program_type}. Введите новый текст:")
# Регистрация следующего шага для обработки введенного текста программы
bot.register_next_step_handler(message, get_program_text, program_type)
# Обработка ошибки при обработке типа прдаограммы
except Exception as e:
# Отправка сообщения об ошибке при обработке типа программы
bot.send_message(message.chat.id, "Произошла ошибка при обработке типа программы. Попробуйте снова.")
# Регистрация того же шага для обработки введенного типа программы
bot.register_next_step_handler(message, get_program_type)
# Следующие функции обработки шагов обновления программы администратором
def get_program_text(message, program_type):
# Попытка обработки текста программы из сообщения пользователя
try:
# Получение нового текста программы из сообщения пользователя (удаление лишних пробелов)
new_text = message.text.strip()
# Инициализация временного хранилища для текущей сессии обновления
pending_data[message.chat.id] = {
"program_type": program_type,
"new_text": new_text,
"photos": []
}
# Отправка сообщения с запросом фотографий программы
bot.send_message(message.chat.id,
"Отправьте фотографии для программы по одной. Напишите `Готово`, когда закончите.",
parse_mode="Markdown")
# Регистрация следующего шага для обработки фотографий программы
bot.register_next_step_handler(message, collect_photos)
# Обработка ошибки при обработке текста программы
except Exception as e:
# Отправка сообщения об ошибке при обработке текста программы
bot.send_message(message.chat.id, "Произошла ошибка при обработке текста. Попробуйте снова.")
# Регистрация того же шага для обработки введенного текста программы
bot.register_next_step_handler(message, get_program_text, program_type)
# Следующие функции обработки шагов обновления программы администратором
def collect_photos(message):
# Попытка обработки фотографий программы из сообщения пользователя
try:
# Получение ID чата пользователя
chat_id = message.chat.id
# Проверка завершения загрузки фотографий
if message.text and message.text.lower() == "готово":
# Проверка наличия фотографий программы
if not pending_data[chat_id]["photos"]:
# Отправка сообщения об ошибке при отсутствии фотографий
bot.send_message(chat_id, "Вы не добавили ни одной фотографии. Попробуйте снова.")
# Регистрация того же шага для обработки фотографий программы
bot.register_next_step_handler(message, collect_photos)
# Переход к завершению обновления программы
else:
# Завершение обновления программы
finalize_program_update(chat_id)
# Завершение выполнения функции (выход из функции)
return
# Обработка фото
if not message.photo:
# Отправка сообщения об ошибке при отсутствии фотографии
bot.send_message(chat_id, "Это не фотография. Отправьте фото или напишите `Готово`, чтобы завершить.",
parse_mode="Markdown")
# Регистрация того же шага для обработки фотографий программы
bot.register_next_step_handler(message, collect_photos)
# Завершение выполнения функции (выход из функции)
return
# Добавление фото в список фотографий программы (последнее фото в сообщении)
photo_id = message.photo[-1].file_id
# Добавление ID фото в список фотографий программы
pending_data[chat_id]["photos"].append(photo_id)
# Отправка сообщения об успешной загрузке фотографии
bot.send_message(chat_id,
"Фото добавлено! Отправьте следующее фото или напишите `Готово`, чтобы завершить.",
parse_mode="Markdown")
# Регистрация следующего шага для обработки фотографий программы
bot.register_next_step_handler(message, collect_photos)
# Обработка ошибки при обработке фотографий программы
except Exception as e:
# Отправка сообщения об ошибке при обработке фотографий
bot.send_message(chat_id, "Произошла ошибка при загрузке фотографии. Попробуйте снова.")
# Регистрация того же шага для обработки фотографий программы
bot.register_next_step_handler(message, collect_photos)
# Следующие функции обработки шагов обновления программы администратором
def finalize_program_update(chat_id):
# Попытка завершения обновления программы
try:
# Получение данных о программе из временного хранилища
program_type = pending_data[chat_id]["program_type"]
new_text = pending_data[chat_id]["new_text"]
photos = pending_data[chat_id]["photos"]
# Обновление данных программы
program_data = load_program_data()
# Обновление текста и фотографий программы
program_data[program_type] = {
"text": new_text,
"images": photos
}
# Сохранение обновленных данных программы в JSON-файл
save_program_data(program_data)
# Очистка временных данных
del pending_data[chat_id]
# Отправка сообщения об успешном обновлении программы
bot.send_message(chat_id, "Программа успешно обновлена!")
# Обработка ошибки при обновлении программы
except Exception as e:
# Вывод сообщения об ошибке в консоль
bot.send_message(chat_id, "Произошла ошибка при обновлении программы. Попробуйте снова.")
# Повторная попытка обновления программы
bot.register_next_step_handler(chat_id, finalize_program_update, chat_id)
# Коллбэк-обработчик для кнопок меню
@bot.callback_query_handler(func=lambda call: call.data in ['program_1_month', 'program_2_months'])
# Функция-обработчик для кнопок меню
def program_handler(call):
# Попытка отправки программы пользователю
try:
# Загрузка данных программы из JSON-файла
program_data = load_program_data()
# Получение типа программы из коллбэка
program_type = call.data
# Получение текста и фотографий программы по типу программы
program_text = program_data[program_type]['text']
program_images = program_data[program_type]['images']
# Отправка медиа-группы (только если есть фотографии)
if program_images:
# Создание медиа-группы из фотографий программы
media_group = [InputMediaPhoto(photo_id) for photo_id in program_images]
# Первый элемент с текстом (если есть текст)
media_group[0].caption = program_text
# Отправка медиа-группы пользователю
bot.send_media_group(call.message.chat.id, media_group)
# Отправка текста программы (если нет фотографий)
else:
# Отправка текста программы пользователю
bot.send_message(call.message.chat.id, program_text)
# Кнопки переключения программ
markup = InlineKeyboardMarkup()
# Добавление кнопок в разметку (markup)
if program_type == 'program_1_month':
markup.add(InlineKeyboardButton('Программа на 2 месяца', callback_data='program_2_months'))
else:
markup.add(InlineKeyboardButton('Программа на 1 месяц', callback_data='program_1_month'))
# Отправка кнопок переключения программ
markup.add(InlineKeyboardButton('Хочу стать резидентом', callback_data='become_resident'))
# Отправка сообщения с кнопками переключения программ
bot.send_message(call.message.chat.id, "Выберите действие:", reply_markup=markup)
# Обработка ошибки при отправке программы
except Exception as e:
# Вывод сообщения об ошибке
bot.send_message(call.message.chat.id, f"Ошибка загрузки программы, обратитесь к администратору")
print(f"Error in program_handler: {e}")
# Коллбэк-обработчик для кнопок стать резидентом
@bot.callback_query_handler(func=lambda call: call.data == 'become_resident')
# Функция-обработчик для кнопок стать резидентом
def become_resident_handler(call):
# Попытка отправки сообщения с тарифами
try:
# Создание кнопок для выбора тарифа
markup = InlineKeyboardMarkup()
# Добавление кнопок в разметку (markup)
markup.add(InlineKeyboardButton('на 1 месяц за 2990₽', callback_data='tariff_1_month'))
markup.add(InlineKeyboardButton('На 2 месяца за 5590₽', callback_data='tariff_2_months'))
# Отправка сообщения с кнопками тарифов
bot.send_message(call.message.chat.id, "Чтобы стать резидентом, выберите подходящий срок подписки:",
reply_markup=markup)
# Обработка ошибки при отправке сообщения с тарифами
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in become_resident_handler: {e}")
# Коллбэк-обработчик для кнопок тарифов
@bot.callback_query_handler(func=lambda call: call.data in ['tariff_1_month', 'tariff_2_months'])
# Функция-обработчик для кнопок тарифов
def tariff_handler(call):
# Попытка отправки сообщения с информацией о тарифе
try:
# Определение типа подписки по коллбэку
subscription_type = '1 месяц' if call.data == 'tariff_1_month' else '2 месяца'
# Определение суммы оплаты по типу подписки
amount = "2990₽" if subscription_type == '1 месяц' else "5590₽"
# Определение реквизитов для оплаты по типу подписки
requisites = "Реквизиты для оплаты: `1234 5678 9012 3456` (Тбанк)"
# Создание кнопки для отправки чека
markup = InlineKeyboardMarkup()
# Добавление кнопок в разметку (markup)
markup.add(InlineKeyboardButton('Отправить чек', callback_data='send_receipt'))
# Сохранение данных о подписке в базе данных
save_subscription(call.message.chat.id, subscription_type, 'pending')
# Отправка сообщения с информацией о тарифе и кнопкой отправки чека
bot.send_message(
call.message.chat.id,
f"Вы выбрали тариф \"{subscription_type}\". \nСумма: {amount}\n{requisites}",
reply_markup=markup, parse_mode='Markdown'
)
# Обработка ошибки при отправке сообщения с информацией о тарифе
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in tariff_handler: {e}")
# Коллбэк-обработчик для кнопок отправки чека
@bot.callback_query_handler(func=lambda call: call.data == 'send_receipt')
# Функция-обработчик для кнопок отправки чека
def send_receipt_handler(call):
# Попытка отправки сообщения с инструкциями по отправке чека
try:
# Отправка сообщения с инструкциями по отправке чека
bot.send_message(
call.message.chat.id,
"Отправьте фото или скан чека в этот чат. Администратор проверит вашу оплату."
)
# Сохранение статуса оплаты в базе данных (ожидание чека)
save_subscription(call.message.chat.id, payment_status='check_receipt')
# Обработка ошибки при отправке сообщения с инструкциями по отправке чека
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in send_receipt_handler: {e}")
# Обработчик для получения чека (фото или документа)
@bot.message_handler(content_types=['photo', 'document'])
# Функция-обработчик для получения чека (фото или документа)
def handle_receipt(message):
# Попытка обработки полученного чека
try:
# Подключение к базе данных SQLite (subscriptions.db)
con = sqlite3.connect('subscriptions.db')
# Создание курсора для работы с базой данных (выполнение запросов)
cursor = con.cursor()
# Проверка статуса оплаты пользователя из базы данных (ожидание чека)
check_status = cursor.execute('SELECT payment_status FROM users WHERE user_id = ?',
(message.chat.id,)).fetchone()
# Проверка статуса оплаты пользователя (ожидание чека) (если не ожидается, выход из функции)
if not check_status or check_status[0] != 'check_receipt':
return
# Сохранение статуса оплаты в базе данных (ожидание подтверждения)
save_subscription(message.chat.id, payment_status='waiting_for_confirmation')
# Отправка сообщения о получении чека
bot.send_message(message.chat.id, "Спасибо! Ваш чек получен и ожидает подтверждения.")
# Отправка чека администраторам
for chat_id in ADMIN_ID:
# Отправка сообщения с чеком администраторам
bot.forward_message(chat_id, message.chat.id, message.message_id)
# Кнопка для подтверждения оплаты
markup = InlineKeyboardMarkup()
# Добавление кнопок в разметку (markup)
markup.add(InlineKeyboardButton('Подтвердить оплату', callback_data=f'confirm_payment_{message.chat.id}'))
# Отправка сообщения администраторам о получении чека (цикл по ID администраторов)
for chat_id in ADMIN_ID:
# Отправка сообщения администраторам о получении чека
bot.send_message(
chat_id,
f"Пользователь {message.chat.id} отправил чек. Подтвердите оплату.",
reply_markup=markup
)
# Обработка ошибки при обработке чека
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in handle_receipt: {e}")
# Коллбэк-обработчик для кнопок подтверждения оплаты
@bot.callback_query_handler(func=lambda call: call.data.startswith('confirm_payment_'))
# Функция-обработчик для кнопок подтверждения оплаты
def confirm_payment_handler(call):
# Попытка подтверждения оплаты
try:
# Получение ID пользователя из коллбэка
user_id = int(call.data.split('_')[-1])
# Запрашиваем у администратора дату начала доступа
msg = bot.send_message(
call.message.chat.id,
"Введите дату начала доступа в формате ГГГГ-ММ-ДД:"
)
# Обработчик введённой даты
@bot.message_handler(func=lambda message: True)
# Функция-обработчик для введённой даты
def set_access_date(message):
# Попытка установки даты доступа
try:
# Проверяем, что дата введена в корректном формате
access_start = datetime.datetime.strptime(message.text, '%Y-%m-%d')
# Устанавливаем дату окончания доступа
access_end = access_start + datetime.timedelta(days=30 if '1 месяц' else 60)
# Сохраняем данные о подписке в базе данных
save_subscription(
user_id,
payment_status='confirmed',
subscription_start=access_start.isoformat(),
subscription_end=access_end.isoformat()
)
# Уведомляем администратора об успешной операции
bot.send_message(
call.message.chat.id,
f"Доступ пользователю {user_id} установлен с {access_start.date()} по {access_end.date()}."
)
# Уведомляем пользователя о подтверждении оплаты
bot.send_message(
user_id,
f"Оплата подтверждена! Ваш доступ будет открыт {access_start.date()}. "
f"Вы получите уведомление в день начала доступа."
)
# Немедленно отправляем ссылку на канал, если доступ начинается сегодня
current_date = datetime.datetime.now().date()
if access_start.date() == current_date:
bot.send_message(
user_id,
f"Ваш доступ к каналу открыт! Ссылка на канал: {CHANNEL_INVITE_LINK}"
)
# Выход из функции
return
# Обработка ошибки при установке даты доступа
except ValueError:
# Отправка сообщения об ошибке ввода даты
bot.send_message(call.message.chat.id, "Неверный формат даты. Попробуйте ещё раз.")
# Регистрируем следующий шаг для обработки введённой даты
bot.register_next_step_handler(msg, set_access_date)
# Обработка ошибки при установке даты доступа
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in confirm_payment_handler: {e}")
# Функция для проверки и управления доступом пользователей
def check_and_manage_access():
# Бесконечный цикл проверки и управления доступом
while True:
# Попытка проверки и управления доступом
try:
# Подключение к базе данных SQLite (subscriptions.db)
conn = sqlite3.connect('subscriptions.db')
# Создание курсора для работы с базой данных (выполнение запросов)
cursor = conn.cursor()
# Выборка данных о пользователях с подтвержденным статусом оплаты и датами подписок
cursor.execute('''SELECT user_id, subscription_start, subscription_end FROM users
WHERE payment_status = 'confirmed' AND subscription_start IS NOT NULL
AND subscription_end IS NOT NULL''')
# Получение результатов запроса (всех строк)
rows = cursor.fetchall()
# Получение текущего времени
current_time = datetime.datetime.now()
# Проверка и управление доступом для каждого пользователя
for row in rows:
# Разделение данных о пользователе на ID, дату начала и окончания подписки
user_id, subscription_start, subscription_end = row
# Преобразование строковых данных в формат даты и времени
subscription_start = datetime.datetime.fromisoformat(subscription_start)
subscription_end = datetime.datetime.fromisoformat(subscription_end)
# Открыть доступ к каналу, если подписка начинается сегодня
if subscription_start.date() == current_time.date():
bot.send_message(
user_id,
f"Ваш доступ к каналу открыт! Ссылка на канал: {CHANNEL_INVITE_LINK}"
)
# Закрыть доступ к каналу, если подписка заканчивается сегодня
if subscription_end.date() == current_time.date():
# Уведомляем пользователя о завершении доступа
bot.send_message(
user_id,
"Ваш доступ к каналу завершен. Спасибо, что были с нами! Если вы хотите продлить подписку, свяжитесь с администратором."
)
# Удалить пользователя из канала
try:
bot.ban_chat_member(CHANNEL_ID, user_id, until_date=current_time)
except Exception as e:
print(f"Error removing user {user_id}: {e}")
# Удалить данные о подписке из базы данных
cursor.execute('''
UPDATE users
SET subscription_end = NULL, subscription_type = NULL, payment_status = 'expired'
WHERE user_id = ?
''', (user_id,))
# Сохранение изменений в базе данных
conn.commit()
# Обработка ошибки при проверке и управлении доступом
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error in check_and_manage_access: {e}")
# Закрытие подключения к базе данных
finally:
conn.close()
# Задержка на 24 часа (86400 секунд) чтобы не нагружать сервер
threading.Event().wait(43200 * 2)
# Функция для запуска бота с обработкой исключений
def telegram_polling():
# Бесконечный цикл polling с обработкой исключений
while True:
# Попытка запуска polling
try:
# Запуск polling бота
print(f"Starting bot polling at {time.strftime('%Y-%m-%d %H:%M:%S')}")
bot.polling(none_stop=True, interval=1, timeout=20)
# Обработка исключения ReadTimeout (тайм-аут)
except requests.exceptions.ReadTimeout:
# Обработка тайм-аута
traceback_error_string = traceback.format_exc()
# Запись ошибки в файл Error.Log
with open("Error.Log", "a") as myfile:
# Запись времени и текста ошибки в файл
myfile.write("\r\n\r\n" + time.strftime("%c") + "\r\n<<ERROR polling>>\r\n" + traceback_error_string
+ "\r\n<<ERROR polling>>")
# Вывод сообщения об ошибке в консоль
print(f"ReadTimeout encountered. Restarting polling in 10 seconds...")
# Задержка на 10 секунд
time.sleep(10)
# Обработка исключения Exception (все остальные исключения)
except Exception as e:
# Обработка всех других исключений
traceback_error_string = traceback.format_exc()
# Запись ошибки в файл Error.Log
with open("Error.Log", "a") as myfile:
myfile.write("\r\n\r\n" + time.strftime("%c") + "\r\n<<UNEXPECTED ERROR>>\r\n" + traceback_error_string
+ "\r\n<<UNEXPECTED ERROR>>")
# Вывод сообщения об ошибке в консоль
print(f"Unexpected error: {e}. Restarting polling in 5 seconds...")
# Задержка на 5 секунд
time.sleep(5)
# Завершение работы бота
finally:
bot.stop_polling()
# Запуск бота при запуске скрипта (если скрипт запущен как основной)
if __name__ == '__main__':
# Запуск потока для проверки и управления доступом пользователей (демон-поток)
try:
# Запуск потока для проверки и управления доступом пользователей
threading.Thread(target=check_and_manage_access, daemon=True).start()
# Обработка ошибки при запуске потока
except Exception as e:
# Вывод сообщения об ошибке в консоль
print(f"Error starting access management thread: {e}")
# Запуск бота с обработкой исключений
telegram_polling()