diff options
| author | Andrew Guschin <guschin.drew@gmail.com> | 2022-06-29 17:45:07 +0400 |
|---|---|---|
| committer | Andrew Guschin <guschin.drew@gmail.com> | 2022-06-29 17:45:07 +0400 |
| commit | fc8fa5a30bf464557051ac22a75ca83de3a29f7b (patch) | |
| tree | 39af088ddf259deb080f2f21f31d89c2206e6ab2 | |
| -rw-r--r-- | bot.py | 52 | ||||
| -rw-r--r-- | config_sample.py | 1 | ||||
| -rw-r--r-- | enums.py | 13 | ||||
| -rw-r--r-- | keyboards.py | 23 | ||||
| -rw-r--r-- | logger_config.py | 27 | ||||
| -rw-r--r-- | requirements.txt | 11 | ||||
| -rw-r--r-- | spec.org | 49 | ||||
| -rw-r--r-- | utils.py | 18 | ||||
| -rw-r--r-- | views.py | 71 |
9 files changed, 265 insertions, 0 deletions
@@ -0,0 +1,52 @@ +from telegram.ext import Updater, CommandHandler, ConversationHandler, MessageHandler, Filters +from telegram import Update, User, Bot # Typing + +from config import TG_TOKEN + +from enums import UtilStates, MainStates +from views import UtilViews, MainStatesView +from keyboards import MenuKeyboard, BACK + +from logger_config import logger + + +def error(bot, update, error): + logger.warning('Update "%s" caused error "%s"', update, error) + + +def main(): + conversation = ConversationHandler( + entry_points=[ + CommandHandler('start', UtilViews.start), + ], + + states={ + UtilStates.WAIT_FOR_USERNAME: [MessageHandler(Filters.text, UtilViews.wait_for_username)], + MainStates.MAIN_MENU: [ + MessageHandler(Filters.regex(MenuKeyboard.NEW_CLIENT), MainStatesView.new_client_name), + MessageHandler(Filters.regex(MenuKeyboard.LIST_CLIENTS), MainStatesView.list_clients), + ], + MainStates.ENTERING_NAME: [ + MessageHandler(Filters.regex(BACK), MainStatesView.main_menu), + MessageHandler(Filters.text, MainStatesView.new_client), + ], + }, + + fallbacks=[ + MessageHandler(Filters.text, UtilViews.fallback), + ] + ) + + + updater = Updater(TG_TOKEN) + + dp = updater.dispatcher + dp.add_error_handler(error) + dp.add_handler(conversation) + + updater.start_polling() + updater.idle() + + +if __name__ == "__main__": + main() diff --git a/config_sample.py b/config_sample.py new file mode 100644 index 0000000..90d0772 --- /dev/null +++ b/config_sample.py @@ -0,0 +1 @@ +TG_TOKEN = "" diff --git a/enums.py b/enums.py new file mode 100644 index 0000000..be70024 --- /dev/null +++ b/enums.py @@ -0,0 +1,13 @@ +class UtilStates: + ( + WAIT_FOR_USERNAME, + *_ + ) = range(100) + + +class MainStates: + ( + MAIN_MENU, + ENTERING_NAME, + *_ + ) = range(100) diff --git a/keyboards.py b/keyboards.py new file mode 100644 index 0000000..4f73c55 --- /dev/null +++ b/keyboards.py @@ -0,0 +1,23 @@ +from telegram import ReplyKeyboardMarkup +from abc import ABC + + +BACK = "↩️ Назад" + + +class Keyboard(ABC): + @classmethod + def get_keyboard(cls, telegram_id=None): + pass + + +class MenuKeyboard(Keyboard): + NEW_CLIENT = "📝 Добавить новый клиент" + LIST_CLIENTS = "📚 Вывести список клиентов" + + @classmethod + def get_keyboard(cls, telegram_id=None): + return ReplyKeyboardMarkup([ + [cls.NEW_CLIENT], + [cls.LIST_CLIENTS], + ]) diff --git a/logger_config.py b/logger_config.py new file mode 100644 index 0000000..1dbea4c --- /dev/null +++ b/logger_config.py @@ -0,0 +1,27 @@ +import logging +import os + +try: + os.mkdir("logs") +except FileExistsError: + pass + +fh = logging.FileHandler("logs/log.log") +fh.setLevel(logging.DEBUG) + +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) + +log_format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +fh.setFormatter(log_format) +ch.setFormatter(log_format) + +logging.basicConfig( + level=logging.DEBUG, handlers=[fh, ch], + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +logger.addHandler(fh) +logger.addHandler(ch) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ba803e2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +APScheduler==3.6.3 +backports.zoneinfo==0.2.1 +cachetools==4.2.2 +certifi==2022.6.15 +python-telegram-bot==13.13 +pytz==2022.1 +pytz-deprecation-shim==0.1.0.post0 +six==1.16.0 +tornado==6.1 +tzdata==2022.1 +tzlocal==4.2 diff --git a/spec.org b/spec.org new file mode 100644 index 0000000..eea2014 --- /dev/null +++ b/spec.org @@ -0,0 +1,49 @@ +#+TITLE: Bot architecture + +* База данных + +** Таблицы + +*** users +| field | type | foreign | +|-----------+----------+---------| +| id | int PK | no | +| tg_id | int | no | +| tg_handle | varchar | no | +| joined_at | datetime | no | + +*** clients +| field | type | foreign | +|------------+----------+----------| +| id | int PK | no | +| user_id | int | users.id | +| ip | int | no | +| pubkey | varchar | no | +| created_at | datetime | no | + +*** user_perms +| field | type | foreign | +|-----------+--------+----------| +| user_id | int PK | users.id | +| is_admin | bool | no | +| is_client | bool | no | + + +** Представления + +*** ClientsView +| field | type | foreign | +|--------+---------+---------| +| ip | int | no | +| pubkey | varchar | no | +WHERE: user.is_client = True + +* Бот + +** MAIN_MENU +- Создать новый клиент -> CREATE +- Список созданных клиентов + +** NAME_CLIENT +- [Ввод] Название клиента +- Назад -> MAIN_MENU diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..00960d0 --- /dev/null +++ b/utils.py @@ -0,0 +1,18 @@ +import socket +import struct + + +def ip2int(addr): + return struct.unpack("!I", socket.inet_aton(addr))[0] + + +def int2ip(addr): + return socket.inet_ntoa(struct.pack("!I", addr)) + + +a = ip2int('10.13.37.254') +b = a + 1 +while (b & 0xFF) in (0x00, 0xFF): + b += 1 +c = int2ip(b) +print(a, b, c) diff --git a/views.py b/views.py new file mode 100644 index 0000000..692af49 --- /dev/null +++ b/views.py @@ -0,0 +1,71 @@ +from telegram import Update, User, Bot # Typing +from telegram.ext import CallbackContext # Typing +from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove + +from enums import UtilStates, MainStates +from keyboards import MenuKeyboard, BACK + +class UtilViews: + @staticmethod + def start(update: Update, context: CallbackContext): + update.message.reply_text( + "Welcome to The Club.", + reply_markup=ReplyKeyboardRemove() + ) + + user: User = update.message.from_user + if user.username is None: + return UtilViews.wait_for_username(update, context) + else: + return MainStatesView.main_menu(update, context) + + + @staticmethod + def wait_for_username(update: Update, context: CallbackContext): + user: User = update.message.from_user + if user.username is None: + update.message.reply_text( + "Этим ботом можно пользоваться только с указанным именем пользователя." + "Укажи его и возвращайся как только это сделаешь.", + reply_markup=ReplyKeyboardMarkup([['Я указал имя пользователя']]) + ) + return UtilStates.WAIT_FOR_USERNAME + + else: + return MainStatesView.main_menu(update, context) + + @staticmethod + def fallback(update: Update, context: CallbackContext): + update.message.reply_text("Команда не распознана") + + +class MainStatesView: + @staticmethod + def main_menu(update: Update, context: CallbackContext): + update.message.reply_text( + "Выбери следующее действие...", + reply_markup=MenuKeyboard.get_keyboard() + ) + + return MainStates.MAIN_MENU + + @staticmethod + def new_client_name(update: Update, context: CallbackContext): + update.message.reply_text( + "Введи имя для клиента (чтобы ты мог отличить клиентов в списке).", + reply_markup=ReplyKeyboardMarkup([[BACK]]) + ) + return MainStates.ENTERING_NAME + + @staticmethod + def new_client(update: Update, context: CallbackContext): + client_name = update.message.text.strip() + update.message.reply_text(f"Клиент |{client_name}| успешно добавлен") + return MainStatesView.main_menu(update, context) + + + @staticmethod + def list_clients(update: Update, context: CallbackContext): + update.message.reply_text("Список клиентов:") + return MainStatesView.main_menu(update, context) + |