Files
mypointvpn/src/App/bot/texts.py
T

164 lines
9.1 KiB
Python

"""Кнопки и тексты бота. Сообщения размечены HTML, данные пользователя экранируются."""
from html import escape
from ..models import Point, Server
# главное меню
MAIN_MENU = 'главное меню'
FAQ = 'ФАК'
ADD_SERVER = 'добавить сервер'
HELP = 'помощь'
MANAGE = 'управление сервером'
# общие
CANCEL = 'отмена'
DELETE = 'удалить'
YES = 'Да'
NO = 'Нет'
# меню добавления сервера
SERVER = 'сервер'
MASK = 'маскировка'
DOMAIN = 'домен'
CLOUDFLARE = 'cloudflare'
PROXY = 'прокси'
INSTALL = 'установить'
ENABLE = 'включить'
DISABLE = 'выключить'
CF_WARP = 'cf_warp'
# меню сервера
SERVER_MENU = 'меню сервера'
RELEASE = 'Вернуть'
REINSTALL = 'Переустановить'
POINT_DOMAIN = 'Домен'
POINT_PROXY = 'Прокси'
POINT_CLOUDFLARE = 'Cloudflare'
CLIENTS = 'Клиенты'
USE_OLD = 'Использовать старый'
MAIN_KEYBOARD = [[FAQ, ADD_SERVER, HELP]]
MAIN_KEYBOARD_WITH_POINTS = [[FAQ, ADD_SERVER, HELP], [MANAGE]]
DRAFT_KEYBOARD = [[DOMAIN, CLOUDFLARE, PROXY, MASK], [CANCEL, INSTALL, SERVER, HELP]]
POINT_KEYBOARD = [[RELEASE, REINSTALL], [POINT_DOMAIN, POINT_PROXY, POINT_CLOUDFLARE], [CLIENTS], [MAIN_MENU]]
CANCEL_KEYBOARD = [[CANCEL]]
EMPTY = '-'
GREETING = 'привет. выбери действие'
FAQ_TEXT = '''<b>Что это за бот?</b>
Бот настраивает ваш собственный VPN на ваших серверах: панель 3x-ui с подключениями vless + tls + reality и сервер маскировки перед ней.
<b>Что нужно от меня?</b>
Два арендованных сервера с Debian 13 (от 2 ядер, 2 Гб памяти, 20 Гб диска): ip и пароль root каждого. По желанию - домен и прокси на выходе.
<b>Зачем два сервера?</b>
Клиенты подключаются к серверу маскировки, он пересылает трафик на панель. Адрес панели никто не видит.
<b>Что бот делает с сервером?</b>
Прописывает свой ssh ключ, меняет пароль root, обновляет систему, переносит ssh на случайный порт с входом только по ключу, включает фаервол и ставит нужные сервисы.
<b>Как забрать сервер назад?</b>
Меню сервера - "Вернуть". Бот уберет свои доступы, откатит ssh и фаервол, поставит новый пароль root и пришлет его. Данные на сервере останутся.
<b>Сервер переустановили у провайдера</b>
Меню сервера - "Переустановить" и новый пароль root.'''
HELP_TEXT = '''<b>Добавить сервер</b>
1. "добавить сервер"
2. "сервер" - пришлите <code>ip:пароль</code> сервера для панели
3. "маскировка" - пришлите <code>ip:пароль</code> сервера маскировки
4. по желанию "домен", "cloudflare", "прокси"
5. "установить" - установка идет в фоне, по окончании придет уведомление
<b>Управлять сервером</b>
"управление сервером" и имя сервера из главного меню.
"главное меню" или /start всегда возвращают в начало.'''
CREDENTIALS_PROMPT = 'отправь данные для подключения к серверу\n<code>ip:root_password</code>'
CREDENTIALS_INVALID = 'Не похоже на <code>ip:root_password</code>. Попробуй еще раз.'
ADDRESS_TAKEN = 'Сервер {address} уже подключен.'
ADDRESS_DUPLICATE = 'Панель и маскировка должны быть разными серверами.'
DOMAIN_PROMPT = 'Пришли доменное имя. Доменное имя лучше подходит для маскировки вашей точки доступа.'
DOMAIN_INVALID = 'Не похоже на доменное имя. Попробуй еще раз.'
CLOUDFLARE_PROMPT = 'Если вы спрятали домен за клаудом, то нужно включить опцию тут.'
PROXY_PROMPT = 'Можно указать свои в формате <code>protocol://username:password@host:port</code> или включить cf_warp.'
PROXY_INVALID = 'Не похоже на <code>protocol://username:password@host:port</code>. Попробуй еще раз.'
DRAFT_INCOMPLETE = 'Нужны оба сервера: "сервер" и "маскировка".'
CLOUDFLARE_WITHOUT_DOMAIN = 'Cloudflare работает только с доменом. Укажи домен или выключи cloudflare.'
INSTALL_STARTED = 'Запускаю установку сервера. Отправлю уведомление когда все будет готово.'
SELECT_PROMPT = 'Пришлите server_name'
POINT_NOT_FOUND = 'Сервер не найден. Пришлите server_name из списка.'
POINT_BUSY = 'Сервер сейчас {status}. Дождитесь окончания.'
RELEASE_CONFIRM = '''Сервер будет освобожден из-под моего контроля,
будет установлен случайный пароль рута,
мои доступы будут убраны, данные на сервере сохранены.
Будет отправлено уведомление с новым паролем. Продолжить?'''
RELEASE_STARTED = 'Освобождаю серверы. Пришлю новые пароли, когда закончу.'
REINSTALL_PROMPT = '''Будет произведена повторная установка всех компонентов.
Вызывайте эту функцию, только после того, как в панели управления
сервером в аккаунте хостинг провайдера вызовите переустановку сервера.
Если уверены, то пришлите <code>ip:новый_пароль</code> переустановленного сервера.
"Использовать старый" переустановит оба сервера с последними известными паролями.'''
REINSTALL_UNKNOWN_ADDRESS = 'Сервер {address} не входит в эту точку.'
REINSTALL_STARTED = 'Запускаю переустановку. Отправлю уведомление когда все будет готово.'
NOT_IMPLEMENTED = 'Пока не реализовано.'
FALLBACK = 'Не понял. Воспользуйся кнопками меню.'
def value(text: str) -> str:
return escape(text) if text else EMPTY
def draft_card(draft: dict) -> str:
return '\n'.join([
f"Сервер: {value(draft.get('server', {}).get('address', ''))}",
f"Сервер маскировки: {value(draft.get('mask', {}).get('address', ''))}",
f"Домен: {value(draft.get('domain', ''))}",
f"Cloudflare: {'Да' if draft.get('cloudflare') else EMPTY}",
f"Прокси: {value(draft.get('proxy', ''))}",
])
def points_list(points) -> str:
return '\n'.join(f'<code>{escape(point.name)}</code>\t{point.get_status_display()}' for point in points)
def point_card(point: Point) -> str:
servers = {server.role: server for server in point.servers.all()}
lines = [
f'Сервер: <code>{escape(point.name)}</code>',
f'Состояние: {point.get_status_display()}',
]
for role in Server.Role:
server = servers.get(role)
lines.append(f'{role.label.capitalize()}: {escape(server.address) if server else EMPTY}')
lines += [
# TODO: онлайн/всего ключей из API панели
f'Ключи: {EMPTY}',
f'Домен: {value(point.domain)}',
f"Cloudflare: {'Да' if point.cloudflare else EMPTY}",
f'Прокси: {value(point.outbound_proxy)}',
]
if point.error:
lines.append(f'Ошибка: {escape(point.error)}')
return '\n'.join(lines)
def installed(point: Point) -> str:
return f'сервер {escape(point.mask.address)} - проименован как <code>{escape(point.name)}</code>, готов к работе.'
def install_failed(point: Point, error: Exception) -> str:
return f'Не удалось настроить <code>{escape(point.name)}</code>:\n{escape(str(error))}'
def released(name: str, passwords: dict[str, str], errors: dict[str, str]) -> str:
lines = [f'Серверы <code>{escape(name)}</code> возвращены.' if not errors else f'<code>{escape(name)}</code>:']
lines += [f'{escape(address)} - пароль root <code>{escape(password)}</code>' for address, password in passwords.items()]
lines += [f'{escape(address)} - не удалось вернуть: {escape(error)}' for address, error in errors.items()]
return '\n'.join(lines)