bot_tg/config.py
zovos 5b025a7cc9 fix: derive /break timing table from the actual lesson schedule
The old SCHEDULE dict was hand-written for a previous timetable and
never got updated when LESSON_SCHEDULE changed, so Wednesday (day=2)
was left empty ("Сегодня кайфуем дома, перекур не нужен") even though
the new schedule has 4 lessons that day. SCHEDULE is now generated
from the pairs actually present in _LESSON_WEEK plus PAIR_TIMES, so it
can't drift out of sync again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 11:35:10 +00:00

205 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
from pathlib import Path
from datetime import timedelta, time
from zoneinfo import ZoneInfo
# --- Базовые пути ---
BASE_DIR = Path(__file__).parent
FONT_PATH = BASE_DIR / "impact.ttf"
DEFAULT_TZ = os.getenv("TZ", "Europe/Moscow")
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://bot:botpass@postgres:5432/botdb")
# --- Настройки /penis ---
PENIS_START_LENGTH = 10.0
PENIS_MIN_DELTA = 0.1
PENIS_MAX_DELTA = 7.0
PENIS_COOLDOWN = timedelta(hours=24)
PENIS_COOLDOWN_SECONDS = int(PENIS_COOLDOWN.total_seconds())
PENIS_TOP_LIMIT = 10
# --- Настройки "получается" ---
POLYCHAETSI_STATS_PATH = Path(os.getenv("POLYCHAETSI_STATS_PATH", "/db/polychaetsi_stats.json"))
POLYCHAETSI_TARGET = "получается"
POLYCHAETSI_TOP_LIMIT = 10
POLYCHAETSI_IMAGE_PATH = BASE_DIR / "maket" / "cvetok.jpg"
# --- Казино ---
CASINO_MAX_BET = 1.0
# --- E621 API ---
E621_LOGIN = os.getenv("E621_LOGIN", "")
E621_API_KEY = os.getenv("E621_API_KEY", "")
# --- Ставки на матчи ---
ODDS_API_KEY = os.getenv("ODDS_API_KEY", "")
ODDS_API_BASE = "https://api.the-odds-api.com/v4"
BET_MAX_AMOUNT = 3.0
ODDS_SPORTS = {
"dota2": "esports_dota2",
}
ODDS_CACHE_TTL = 86400 # 24 часа — экономим запросы (500/мес бесплатно)
# --- Nude ---
NUDE_HISTORY_PATH = Path(os.getenv("NUDE_HISTORY_PATH", "/db/nude_history.json"))
NUDE_POSTS_PER_PAGE = int(os.getenv("NUDE_POSTS_PER_PAGE", "100"))
NUDE_POSTS_POOL_TARGET = int(os.getenv("NUDE_POSTS_POOL_TARGET", "1200"))
NUDE_MAX_POSTS_TO_SCAN = int(os.getenv("NUDE_MAX_POSTS_TO_SCAN", "2000"))
NUDE_MIN_UNSEEN_POOL = int(os.getenv("NUDE_MIN_UNSEEN_POOL", "100"))
NUDE_HISTORY_MAX_SIZE = int(os.getenv("NUDE_HISTORY_MAX_SIZE", "5000"))
# --- Magnit API ---
MAGNIT_API_BASE_URL = os.getenv("MAGNIT_API_BASE_URL", "https://mirror.porno4free.ru/magnit")
MAGNIT_STORE_CODE = os.getenv("MAGNIT_STORE_CODE", "618224")
MAGNIT_REQUEST_TIMEOUT = int(os.getenv("MAGNIT_REQUEST_TIMEOUT", "15"))
# Можно указывать просто строку с названием или словарь с query/label/store_code.
MAGNIT_PRODUCTS = [
"RedBull",
]
# --- Конфигурация макетов ---
TEMPLATES = {
"1": {
"path": BASE_DIR / "maket" / "1.png",
"anchor": (104, 240),
"padding": 20,
"label": "Владимир Владимирович Путин самый лучший президент",
},
"2": {
"path": BASE_DIR / "maket" / "2.png",
"anchor": (40, 40),
"box_size": (582, 346),
"label": "Макет 2",
},
}
# --- Расписание ---
def make_time(h: int, m: int) -> time:
return time(hour=h, minute=m)
# Если у тебя наоборот, просто поменяй на False.
UPPER_WEEK_IS_ODD = False
PAIR_TIMES = {
1: (make_time(8, 0), make_time(9, 35)),
2: (make_time(9, 50), make_time(11, 25)),
3: (make_time(11, 55), make_time(13, 30)),
4: (make_time(13, 45), make_time(15, 20)),
5: (make_time(15, 50), make_time(17, 25)),
}
# Расписание 2 курс, гр. 27 (25ВТ-09.03.03.01-о2), осень 2026-27.
# Источник: Raspis_osen_2_kurs_2026-27_g.pdf (черновик, водяной знак "Проект", возможны изменения).
# Разделения на верхнюю/нижнюю неделю в источнике нет — обе недели одинаковые.
_MON = [
{"pair": 2, "title": "Экономика и право (практика), Индустриев М.А. до 06.11 / Васильева Е.В. с 07.11, ауд. 504"},
{"pair": 3, "title": "Экономика и право (лекция), Туманян Ю.Р. до 06.11 / Васильева Е.В. с 07.11, ауд. 510"},
{"pair": 4, "title": "Геоинформационные системы (лекция), Ушканова Е.В., ауд. 403а (НИИМ и ПМ)"},
]
_TUE = [
{"pair": 3, "title": "Специальные разделы математики (практика), Вакулов Б.Г., ауд. 504"},
{"pair": 4, "title": "Геоинформационные системы (лаб), Ушканова Е.В., ауд. 403а (НИИМ и ПМ)"},
]
_WED = [
{"pair": 2, "title": "Технология программирования (лекция), Губский Д.С., ауд. 510"},
{"pair": 3, "title": "1 подгруппа: Основы функционирования вычислительной техники (лаб), Дацюк О.В., ауд. 606 | 2 подгруппа: Геоинформационные системы (лаб), Ушканова Е.В., ауд. 403а (НИИМ и ПМ)"},
{"pair": 4, "title": "1 подгруппа: Технология программирования (лаб), Губский Д.С., ауд. 609 | 2 подгруппа: Технология программирования (лаб), Иванова И.Н., ауд. 609а"},
{"pair": 5, "title": "Основы функционирования вычислительной техники (лаб), Дацюк О.В., ауд. 606"},
]
_THU = [
{"pair": 2, "title": "Основы функционирования вычислительной техники (лекция), Дацюк О.В., ауд. 510"},
{"pair": 3, "title": "1 подгруппа: Основы функционирования вычислительной техники (практика), Дацюк О.В., ауд. 818 | 2 подгруппа: Специальные разделы математики (практика), Вакулов Б.Г., ауд. 507"},
{"pair": 4, "title": "Специальные разделы математики (лекция), Каплицкий В.М., ауд. 510"},
]
_FRI = [
{"pair": None, "title": "Иностранный язык (время уточняется)"},
]
_SAT = [
{"pair": 1, "title": "Философия (лекция, совместно с гр. 26), Новохатько А.Г., MS Teams"},
]
_LESSON_WEEK = {
0: _MON,
1: _TUE,
2: _WED,
3: _THU,
4: _FRI,
5: _SAT,
}
LESSON_SCHEDULE = {
"upper": _LESSON_WEEK,
"lower": _LESSON_WEEK,
}
# SCHEDULE — тайминг перекуров для /break, построен из фактических пар в
# _LESSON_WEEK, а не задаётся руками, чтобы не рассинхронизироваться при
# правках расписания (раньше здесь была отдельная захардкоженная таблица,
# которая не обновилась вместе с LESSON_SCHEDULE и врала на днях с занятиями).
# Каждая пара (95 мин) состоит из двух академ. часов по 45 мин с 5-минутным
# перекуром между ними; между парами перекур — фактический зазор по PAIR_TIMES.
def _time_to_minutes(t: time) -> int:
return t.hour * 60 + t.minute
def _minutes_to_time(m: int) -> time:
return make_time(m // 60, m % 60)
def _build_day_schedule(pairs: list[int]) -> list[tuple[time, time, int | None]]:
entries = []
for idx, pair in enumerate(pairs):
start_t, end_t = PAIR_TIMES[pair]
start_m = _time_to_minutes(start_t)
end_m = _time_to_minutes(end_t)
mid_m = start_m + 45
second_start_m = mid_m + 5
entries.append((start_t, _minutes_to_time(mid_m), 5))
if idx == len(pairs) - 1:
entries.append((_minutes_to_time(second_start_m), end_t, None))
else:
next_start_t, _ = PAIR_TIMES[pairs[idx + 1]]
gap = _time_to_minutes(next_start_t) - end_m
entries.append((_minutes_to_time(second_start_m), end_t, gap))
return entries
def _pairs_for_day(day_lessons: list[dict]) -> list[int]:
return sorted({item["pair"] for item in day_lessons if item.get("pair") is not None})
SCHEDULE = {
day: _build_day_schedule(_pairs_for_day(lessons))
for day, lessons in _LESSON_WEEK.items()
}
# --- Остальное ---
KEYWORDS = {'крип', 'радиохэд', 'creep', 'radiohead'}
# --- Авто-ответы ИИ ---
AUTOREPLY_CHANCE = 0.10
AUTOREPLY_COOLDOWN = 120
AUTOREPLY_TRIGGERS = {'бот', 'фраер', 'кент', 'братуха', 'пацаны', 'чё как', 'базар', 'ебать', 'пизда', 'хуй', 'бля', 'сука', 'говно', 'пидор', 'пиздабол', 'пиздатый', 'пиздатая', 'пиздатое', 'пиздатые', 'пиздато', 'ваномас'}
def get_hint_text(templates_str: str) -> str:
return (
"Команды:\n"
f"• /maket «id» «текст» — доступные: {templates_str}.\n"
"• /gen_mem Up:верх Down:низ — отправь с фото.\n"
"• /break — узнать время до перекура.\n"
"• /schedule [пн|mon] — расписание; без аргумента еще текущая и следующая пара.\n"
"• /penis — миниигра 1 раз в 24 часа.\n"
"• /top_penis — топ по длине.\n"
"• /talk [текст] — ИИ базарит на фене.\n"
"• /penis_casino [ставка] — казино на размер.\n"
"• /gadanie [тема] — гадание на фене с мемом.\n"
"• /matches — матчи для ставок.\n"
"• /bet [номер] [исход] [ставка] — поставить.\n"
"• /mybets — мои активные ставки.\n"
"• /bet_history — история ставок за месяц + статистика.\n"
"• /sports_debug — диагностика API матчей.\n"
"• /ebalnik — включить/выключить автоответы в чате.\n"
"• /uwu [теги] — случайная картинка с e621.\n"
"• /polychaetsi — топ по слову «получается».\n"
"• /prices /fetch — цены и остатки Магнита.\n"
"Подсказка: в группах пиши /команда@username_бота."
)