diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1.pdf" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..a8c482b196eb2808d552bf03b59fae68b13e23af Binary files /dev/null and "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1.pdf" differ diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1.png" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1.png" new file mode 100644 index 0000000000000000000000000000000000000000..28d696da86aa83667147cbc0499c6c48eac530f4 Binary files /dev/null and "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.1.png" differ diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.2.pdf" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.2.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..956c107758d4c8a10b394da74d9a8fa47a9892d5 Binary files /dev/null and "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.2.pdf" differ diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.3.pdf" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.3.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..b4a0183d9f69fce92b4405c9e9e9ee78e868f65f Binary files /dev/null and "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.3.pdf" differ diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.4.pdf" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.4.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..acf01a3a54beb15d84e6b9620faec07f72b31cf6 Binary files /dev/null and "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.4.pdf" differ diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5/main.py" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5/main.py" new file mode 100644 index 0000000000000000000000000000000000000000..d28e10c6a315d078b739489b2ba0984af8f02b85 --- /dev/null +++ "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5/main.py" @@ -0,0 +1,1047 @@ +import asyncio +import logging +import sys +import os +import random +import sqlite3 +from datetime import datetime, UTC +from dataclasses import dataclass +import calendar +from typing import Any, Self +from enum import Enum + +from dateutil.relativedelta import relativedelta + +from apscheduler.schedulers.asyncio import AsyncIOScheduler +from apscheduler.triggers.interval import IntervalTrigger + +from aiogram import Bot, Dispatcher, Router, F +from aiogram.client.default import DefaultBotProperties +from aiogram.filters import CommandStart, Command, CommandObject +from aiogram.filters.callback_data import CallbackData +from aiogram.types import Message, ReplyKeyboardRemove, BotCommand, CallbackQuery +from aiogram.utils.keyboard import InlineKeyboardBuilder, InlineKeyboardMarkup +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import StatesGroup, State + +from pyowm.config import DEFAULT_CONFIG as OWM_DEFAULT_CONFIG +from pyowm import OWM +from pyowm.weatherapi25.weather import Weather +from pyowm.weatherapi25.location import Location +from pyowm.commons.exceptions import NotFoundError as OwmNotFoundError + +with open("./telegram_token.txt", "r") as f: + TELEGRAM_TOKEN = f.read() + +with open("./owm_token.txt", "r") as f: + OWM_TOKEN = f.read() + +OWM_CONFIG = OWM_DEFAULT_CONFIG | { + 'language': 'ru' +} + +COUNTRY_CODE_TO_COUNTRY_NAME = { + 'AF': 'Афганистан', + 'AL': 'Албания', + 'DZ': 'Алжир', + 'AS': 'Американское Самоа', + 'AD': 'РђРЅРґРѕСЂСЂР°', + 'AO': 'Ангола', + 'AI': 'Ангилья', + 'AQ': 'Антарктика', + 'AG': 'Антигуа Рё Барбуда', + 'AR': 'Аргентина', + 'AM': 'Армения', + 'AW': 'РђСЂСѓР±Р°', + 'AU': 'Австралия', + 'AT': 'Австрия', + 'AZ': 'Азербайджан', + 'BS': 'Багамы', + 'BH': 'Бахрейн', + 'BD': 'Бангладешь', + 'BB': 'Барбадос', + 'BY': 'Беларусь', + 'BE': 'Бельгия', + 'BZ': 'Белиз', + 'BJ': 'Бенин', + 'BM': 'Бермуды', + 'BT': 'Бутан', + 'BO': 'Bolivia', + 'BQ': 'Бонэйр', + 'BA': 'Босния Рё Герцеговина', + 'BW': 'Ботсвана', + 'BV': 'Остров Буве', + 'BR': 'Бразилия', + 'IO': 'Британская территория РІ РРЅРґРёР№СЃРєРѕРј океане', + 'BN': 'Бруней', + 'BG': 'Болгария', + 'BF': 'Буркина-Фасо', + 'BI': 'Бурунди', + 'KH': 'Камбоджа', + 'CM': 'Камерун', + 'CA': 'Канада', + 'CV': 'Кабо-Верде', + 'KY': 'Острова Кайман', + 'CF': 'ЦАР', + 'TD': 'Чад', + 'CL': 'Чили', + 'CN': 'Китай', + 'CX': 'Остров Рождества', + 'CC': 'Кокосовые острова', + 'CO': 'Колумбия', + 'KM': 'РљРѕРјРѕСЂС‹', + 'CG': 'РљРѕРЅРіРѕ', + 'CD': 'Демократическая республика РљРѕРЅРіРѕ', + 'CK': 'Острова РљСѓРєР°', + 'CR': 'Коста-Р РёРєР°', + 'HR': 'Хорватия', + 'CU': 'РљСѓР±Р°', + 'CW': 'Кюрасао', + 'CY': 'РљРёРїСЂ', + 'CZ': 'Чехия', + 'CI': "РљРѕС‚-Рґ'Рвуар", + 'DK': 'Дания', + 'DJ': 'Джибути', + 'DM': 'Доминика', + 'DO': 'Доминиканская Республика', + 'EC': 'Рквадор', + 'EG': 'Египт', + 'SV': 'Сальвадор', + 'GQ': 'Equatorial Guinea', + 'ER': 'Eritrea', + 'EE': 'Рстония', + 'ET': 'Рфиопия', + 'FK': 'Фолклендские острова', + 'FO': 'Фарерские острова', + 'FJ': 'Фиджи', + 'FI': 'Финляндия', + 'FR': 'Франция', + 'GF': 'Французская Гвиана', + 'PF': 'Французская Полинезия', + 'TF': 'Французские Южные Рё Антарктические Территории', + 'GA': 'Габон', + 'GM': 'Гамбия', + 'GE': 'Грузия', + 'DE': 'Германия', + 'GH': 'Гана', + 'GI': 'Гибралтар', + 'GR': 'Греция', + 'GL': 'Гренландия', + 'GD': 'Гренада', + 'GP': 'Гваделупа', + 'GU': 'Гуам', + 'GT': 'Гватемала', + 'GG': 'Гернси', + 'GN': 'Гвинея', + 'GW': 'Гвинея-Бисау', + 'GY': 'Гайана', + 'HT': 'Гаити', + 'HM': 'Остров Херд Рё остров Макдональд', + 'VA': 'Папский Престол (Государство-РіРѕСЂРѕРґ Ватикан)', + 'HN': 'Гандурас', + 'HK': 'Гонк РљРѕРЅРі', + 'HU': 'Венгрия', + 'IS': 'Рсландия', + 'IN': 'РРЅРґРёСЏ', + 'ID': 'Рндонезия', + 'IR': 'Рран', + 'IQ': 'Ррак', + 'IE': 'Ррландия', + 'IM': 'остров РњСЌРЅ', + 'IL': 'Рзраиль', + 'IT': 'Рталия', + 'JM': 'Ямайка', + 'JP': 'РЇРїРѕРЅРёСЏ', + 'JE': 'Джерси', + 'JO': 'Рордания', + 'KZ': 'Казахстан', + 'KE': 'Кения', + 'KI': 'Кирибати', + 'KP': "КНДР", + 'KR': 'Корея', + 'KW': 'Кувейт', + 'KG': 'Кыргызстан', + 'LA': "Лаос", + 'LV': 'Латвия', + 'LB': 'Ливан', + 'LS': 'Лесото', + 'LR': 'Либерия', + 'LY': 'Ливия', + 'LI': 'Лихтенштейн', + 'LT': 'Литва', + 'LU': 'Люксембург', + 'MO': 'Макао', + 'MK': 'Македония', + 'MG': 'Мадагаскар', + 'MW': 'Малави', + 'MY': 'Малайзия', + 'MV': 'Мальдивы', + 'ML': 'Мали', + 'MT': 'Мальта', + 'MH': 'Маршалловы острова', + 'MQ': 'Мартиника', + 'MR': 'Мавритания', + 'MU': 'Маврикий', + 'YT': 'Майотта', + 'MX': 'Мексика', + 'FM': 'Микронезия', + 'MD': 'Молдова', + 'MC': 'Монако', + 'MN': 'Монголия', + 'ME': 'Черногория', + 'MS': 'Montserrat', + 'MA': 'РњРѕСЂРѕРєРєРѕ', + 'MZ': 'Мозамбик', + 'MM': 'РњСЊСЏРЅРјР° (Бирма)', + 'NA': 'Намибия', + 'NR': 'Науру', + 'NP': 'Непал', + 'NL': 'Нидерланды', + 'NC': 'Новая Каледония', + 'NZ': 'Новая Зеландия', + 'NI': 'Никарагуа', + 'NE': 'Нигер', + 'NG': 'Нигерия', + 'NU': 'РќРёСѓСЌ', + 'NF': 'Острова Норфолк', + 'MP': 'Северные Марианские острова', + 'NO': 'Норвегия', + 'OM': 'Оман', + 'PK': 'Пакистан', + 'PW': 'Палау', + 'PS': 'Палестина', + 'PA': 'Панама', + 'PG': 'Папуа Новая Гвинея', + 'PY': 'Парагвай', + 'PE': 'Перу', + 'PH': 'Филипины', + 'PN': 'острова Питкэрн', + 'PL': 'Польша', + 'PT': 'Португалия', + 'PR': 'Пуэрто-Р РёРєРѕ', + 'QA': 'Катар', + 'RO': 'Румыния', + 'RU': 'Российская Федерация', + 'RW': 'Руанда', + 'RE': 'Реюньон', + 'BL': 'Сан-Бартелеми', + 'SH': 'остров Святой Елены', + 'KN': 'Сент-Китс Рё Невис', + 'LC': 'Сент-Люсия', + 'MF': 'Сен-Мертен', + 'PM': 'Сен-Пьер Рё Микелон', + 'VC': 'Сент-Винсент Рё Гренадины', + 'WS': 'Самоа', + 'SM': 'Сан-Марино', + 'ST': 'Сан-РўРѕРјРµ Рё РџСЂРёРЅСЃРёРїРё', + 'SA': 'Саудовская Арабия', + 'SN': 'Сенегал', + 'RS': 'Сербия', + 'SC': 'Сейшеллы', + 'SL': 'Сьерра-Леоне', + 'SG': 'Сингапур', + 'SX': 'РЎРёРЅС‚-Мартен', + 'SK': 'Словакия', + 'SI': 'Словения', + 'SB': 'Соломоновы острова', + 'SO': 'Сомали', + 'ZA': 'Южная Африка', + 'GS': 'Южная Георгия Рё Южные Сандвичевы острова', + 'SS': 'Южный Судан', + 'ES': 'Рспания', + 'LK': 'РЁСЂРё Ланка', + 'SD': 'Судан', + 'SR': 'Суринам', + 'SJ': 'Шпицберген Рё РЇРЅ-Майен', + 'SZ': 'Рсватини', + 'SE': 'Швеция', + 'CH': 'Швейцария', + 'SY': 'РЎРёСЂРёСЏ', + 'TW': 'Тайвань', + 'TJ': 'Таджикистан', + 'TZ': 'Танзания', + 'TH': 'Тайланд', + 'TL': 'Восточный РўРёРјРѕСЂ', + 'TG': 'РўРѕРіРѕ', + 'TK': 'Токелау', + 'TO': 'РўРѕРЅРіР°', + 'TT': 'Тринидад Рё Тобаго', + 'TN': 'РўСѓРЅРёСЃ', + 'TR': 'Турция', + 'TM': 'Туркменистан', + 'TC': 'Острова Теркс Рё Кайкос', + 'TV': 'Тувалу', + 'UG': 'Уганда', + 'UA': 'Украина', + 'AE': 'РћРђР', + 'GB': 'Великобритания', + 'US': 'РЎРЁРђ', + 'UM': 'Внешние малые Рѕ-РІР° (РЎРЁРђ)', + 'UY': 'Уругвай', + 'UZ': 'Узбекистан', + 'VU': 'Ванауту', + 'VE': 'Венесуэла', + 'VN': 'Вьетнам', + 'VG': 'Британские Р’РёСЂРіРёРЅСЃРєРёРµ острова', + 'VI': 'Американские Р’РёСЂРіРёРЅСЃРєРёРµ острова', + 'WF': 'острова Уоллис Рё Футуна', + 'EH': 'Западная Сахара', + 'YE': 'Йемен', + 'ZM': 'Замбия', + 'ZW': 'Зимбабве', + "AX": "Аландские острова", +} + +COUNTRY_CODE_TO_FLAG = { + 'AF': '🇦🇫', + 'AL': '🇦🇱', + 'DZ': '🇩🇿', + 'AS': '🇦🇸', + 'AD': '🇦🇩', + 'AO': '🇦🇴', + 'AI': '🇦🇮', + 'AQ': '🇦🇶', + 'AG': '🇦🇬', + 'AR': '🇦🇷', + 'AM': '🇦🇲', + 'AW': '🇦🇼', + 'AU': '🇦🇺', + 'AT': '🇦🇹', + 'AZ': '🇦🇿', + 'BS': '🇧🇸', + 'BH': '🇧рџ‡', + 'BD': '🇧🇩', + 'BB': '🇧🇧', + 'BY': '🇧🇾', + 'BE': '🇧🇪', + 'BZ': '🇧🇿', + 'BJ': '🇧🇯', + 'BM': '🇧🇲', + 'BT': '🇧🇹', + 'BO': '🇧🇴', + 'BQ': '🇧🇶', + 'BA': '🇧🇦', + 'BW': '🇧🇼', + 'BV': '🇧🇻', + 'BR': '🇧🇷', + 'IO': '🇮🇴', + 'BN': '🇧🇳', + 'BG': '🇧🇬', + 'BF': '🇧🇫', + 'BI': '🇧🇮', + 'KH': 'рџ‡°рџ‡', + 'CM': 'рџ‡Ёрџ‡І', + 'CA': '🇨🇦', + 'CV': '🇨🇻', + 'KY': 'рџ‡°рџ‡ѕ', + 'CF': '🇨🇫', + 'TD': '🇹🇩', + 'CL': '🇨🇱', + 'CN': 'рџ‡Ёрџ‡і', + 'CX': 'рџ‡Ёрџ‡Ѕ', + 'CC': 'рџ‡Ёрџ‡Ё', + 'CO': '🇨🇴', + 'KM': 'рџ‡°рџ‡І', + 'CG': '🇨🇬', + 'CD': '🇨🇩', + 'CK': 'рџ‡Ёрџ‡°', + 'CR': 'рџ‡Ёрџ‡·', + 'HR': 'рџ‡рџ‡·', + 'CU': 'рџ‡Ёрџ‡є', + 'CW': 'рџ‡Ёрџ‡ј', + 'CY': 'рџ‡Ёрџ‡ѕ', + 'CZ': 'рџ‡Ёрџ‡ї', + 'CI': '🇨🇮', + 'DK': '🇩🇰', + 'DJ': '🇩🇯', + 'DM': '🇩🇲', + 'DO': '🇩🇴', + 'EC': 'рџ‡Єрџ‡Ё', + 'EG': '🇪🇬', + 'SV': '🇸🇻', + 'GQ': '🇬🇶', + 'ER': 'рџ‡Єрџ‡·', + 'EE': 'рџ‡Єрџ‡Є', + 'ET': '🇪🇹', + 'FK': '🇫🇰', + 'FO': '🇫🇴', + 'FJ': '🇫🇯', + 'FI': '🇫🇮', + 'FR': '🇫🇷', + 'GF': '🇬🇫', + 'PF': '🇵🇫', + 'TF': '🇹🇫', + 'GA': '🇬🇦', + 'GM': '🇬🇲', + 'GE': '🇬🇪', + 'DE': '🇩🇪', + 'GH': '🇬рџ‡', + 'GI': '🇬🇮', + 'GR': '🇬🇷', + 'GL': '🇬🇱', + 'GD': '🇬🇩', + 'GP': '🇬🇵', + 'GU': '🇬🇺', + 'GT': '🇬🇹', + 'GG': '🇬🇬', + 'GN': '🇬🇳', + 'GW': '🇬🇼', + 'GY': '🇬🇾', + 'HT': 'рџ‡рџ‡№', + 'HM': 'рџ‡рџ‡І', + 'VA': '🇻🇦', + 'HN': 'рџ‡рџ‡і', + 'HK': 'рџ‡рџ‡°', + 'HU': 'рџ‡рџ‡є', + 'IS': '🇮🇸', + 'IN': '🇮🇳', + 'ID': '🇮🇩', + 'IR': '🇮🇷', + 'IQ': '🇮🇶', + 'IE': '🇮🇪', + 'IM': '🇮🇲', + 'IL': '🇮🇱', + 'IT': '🇮🇹', + 'JM': 'рџ‡Їрџ‡І', + 'JP': '🇯🇵', + 'JE': 'рџ‡Їрџ‡Є', + 'JO': '🇯🇴', + 'KZ': 'рџ‡°рџ‡ї', + 'KE': 'рџ‡°рџ‡Є', + 'KI': '🇰🇮', + 'KP': '🇰🇵', + 'KR': 'рџ‡°рџ‡·', + 'KW': 'рџ‡°рџ‡ј', + 'KG': '🇰🇬', + 'LA': '🇱🇦', + 'LV': '🇱🇻', + 'LB': '🇱🇧', + 'LS': '🇱🇸', + 'LR': '🇱🇷', + 'LY': '🇱🇾', + 'LI': '🇱🇮', + 'LT': '🇱🇹', + 'LU': '🇱🇺', + 'MO': '🇲🇴', + 'MK': 'рџ‡Ірџ‡°', + 'MG': '🇲🇬', + 'MW': 'рџ‡Ірџ‡ј', + 'MY': 'рџ‡Ірџ‡ѕ', + 'MV': '🇲🇻', + 'ML': '🇲🇱', + 'MT': '🇲🇹', + 'MH': 'рџ‡Ірџ‡', + 'MQ': '🇲🇶', + 'MR': 'рџ‡Ірџ‡·', + 'MU': 'рџ‡Ірџ‡є', + 'YT': '🇾🇹', + 'MX': 'рџ‡Ірџ‡Ѕ', + 'FM': '🇫🇲', + 'MD': '🇲🇩', + 'MC': 'рџ‡Ірџ‡Ё', + 'MN': 'рџ‡Ірџ‡і', + 'ME': 'рџ‡Ірџ‡Є', + 'MS': 'рџ‡Ірџ‡ё', + 'MA': '🇲🇦', + 'MZ': 'рџ‡Ірџ‡ї', + 'MM': 'рџ‡Ірџ‡І', + 'NA': '🇳🇦', + 'NR': 'рџ‡ірџ‡·', + 'NP': '🇳🇵', + 'NL': '🇳🇱', + 'NC': 'рџ‡ірџ‡Ё', + 'NZ': 'рџ‡ірџ‡ї', + 'NI': '🇳🇮', + 'NE': 'рџ‡ірџ‡Є', + 'NG': '🇳🇬', + 'NU': 'рџ‡ірџ‡є', + 'NF': '🇳🇫', + 'MP': '🇲🇵', + 'NO': '🇳🇴', + 'OM': '🇴🇲', + 'PK': '🇵🇰', + 'PW': '🇵🇼', + 'PS': '🇵🇸', + 'PA': '🇵🇦', + 'PG': '🇵🇬', + 'PY': '🇵🇾', + 'PE': '🇵🇪', + 'PH': '🇵рџ‡', + 'PN': '🇵🇳', + 'PL': '🇵🇱', + 'PT': '🇵🇹', + 'PR': '🇵🇷', + 'QA': '🇶🇦', + 'RO': '🇷🇴', + 'RU': 'рџ‡·рџ‡є', + 'RW': 'рџ‡·рџ‡ј', + 'RE': 'рџ‡·рџ‡Є', + 'BL': '🇧🇱', + 'SH': 'рџ‡ёрџ‡', + 'KN': 'рџ‡°рџ‡і', + 'LC': '🇱🇨', + 'MF': '🇲🇫', + 'PM': '🇵🇲', + 'VC': '🇻🇨', + 'WS': 'рџ‡јрџ‡ё', + 'SM': 'рџ‡ёрџ‡І', + 'ST': '🇸🇹', + 'SA': '🇸🇦', + 'SN': 'рџ‡ёрџ‡і', + 'RS': 'рџ‡·рџ‡ё', + 'SC': 'рџ‡ёрџ‡Ё', + 'SL': '🇸🇱', + 'SG': '🇸🇬', + 'SX': 'рџ‡ёрџ‡Ѕ', + 'SK': 'рџ‡ёрџ‡°', + 'SI': '🇸🇮', + 'SB': '🇸🇧', + 'SO': '🇸🇴', + 'ZA': '🇿🇦', + 'GS': '🇬🇸', + 'SS': 'рџ‡ёрџ‡ё', + 'ES': 'рџ‡Єрџ‡ё', + 'LK': '🇱🇰', + 'SD': '🇸🇩', + 'SR': 'рџ‡ёрџ‡·', + 'SJ': 'рџ‡ёрџ‡Ї', + 'SZ': 'рџ‡ёрџ‡ї', + 'SE': 'рџ‡ёрџ‡Є', + 'CH': 'рџ‡Ёрџ‡', + 'SY': 'рџ‡ёрџ‡ѕ', + 'TW': '🇹🇼', + 'TJ': '🇹🇯', + 'TZ': '🇹🇿', + 'TH': '🇹рџ‡', + 'TL': '🇹🇱', + 'TG': '🇹🇬', + 'TK': '🇹🇰', + 'TO': '🇹🇴', + 'TT': '🇹🇹', + 'TN': '🇹🇳', + 'TR': '🇹🇷', + 'TM': '🇹🇲', + 'TC': '🇹🇨', + 'TV': '🇹🇻', + 'UG': '🇺🇬', + 'UA': '🇺🇦', + 'AE': '🇦🇪', + 'GB': '🇬🇧', + 'US': 'рџ‡єрџ‡ё', + 'UM': 'рџ‡єрџ‡І', + 'UY': 'рџ‡єрџ‡ѕ', + 'UZ': 'рџ‡єрџ‡ї', + 'VU': '🇻🇺', + 'VE': '🇻🇪', + 'VN': '🇻🇳', + 'VG': '🇻🇬', + 'VI': '🇻🇮', + 'WF': '🇼🇫', + 'EH': 'рџ‡Єрџ‡', + 'YE': 'рџ‡ѕрџ‡Є', + 'ZM': 'рџ‡їрџ‡І', + 'ZW': 'рџ‡їрџ‡ј', + "AX": '🇦🇽', +} + +@dataclass +class Reminder: + id: int + date: int + text: str + active: bool = True + +class NewReminderState(StatesGroup): + text = State() + date = State() + +class TimeUnit(Enum): + YEAR = 1 + MONTH = 2 + DAY = 3 + HOUR = 4 + MINUTE = 5 + SECOND = 6 + +class ReminderAction(str, Enum): + DELETE_COMPLETED = "delete_completed" + +class ReminderCallback(CallbackData, prefix="reminder"): + action: ReminderAction + +REMINDERS: dict[int, list[Reminder]] = {} + +WEATHER_COMMAND = BotCommand(command="weather", description="Получить РїСЂРѕРіРЅРѕР· РїРѕРіРѕРґС‹") +RPS_COMMAND = BotCommand(command="rps", description="Сыграть РІ камень, ножницы, бумага") +REMINDERS_COMMAND = BotCommand(command="reminders", description="Получить СЃРїРёСЃРѕРє всех напоминаний") +REMINDER_COMMAND = BotCommand(command="reminder", description="Создать напоминание") +CANCEL_COMMAND = BotCommand(command="cancel", description="Отменить текущее действие") + +ALL_COMMANDS = [ + WEATHER_COMMAND, + RPS_COMMAND, + REMINDERS_COMMAND, + REMINDER_COMMAND, + CANCEL_COMMAND +] + +START_MESSAGE = """ +Здравствуйте! + +РЇ Р±РѕС‚, созданный РїСЂРё выполнении задания Р’РЎР 2.5 РїРѕ учебной практике 1 РєСѓСЂСЃР° РІ РГПУ РёРј. Герцена. + +РњРѕРё возможности: + - Показывать текущую РїРѕРіРѕРґСѓ РІ любом РіРѕСЂРѕРґРµ РјРёСЂР° СЃ помощью команды /weather + - Рграть РІ камень, ножницы, бумага СЃ помощью команды /rps + - Создавать напоминания СЃ помощью команды /reminder + +Остальные команды: + /cancel - Отменить текущее действие (Например создание напоминания) + /reminders - Показать СЃРїРёСЃРѕРє всех напоминаний +""" + +owm = OWM(OWM_TOKEN, config=OWM_CONFIG) +mgr = owm.weather_manager() + +bot = Bot(token=TELEGRAM_TOKEN, default=DefaultBotProperties(parse_mode="html")) +dp = Dispatcher() +reminder_router = Router() + +def get_word_case(count: int, words: list[str]) -> str: + ONE = 0 + FEW = 1 + OTHER = 2 + + if (count % 10 == 1) and not (count % 100 == 11): + return words[ONE] + + if (count % 100) > 11 and (count % 100) <= 15: + return words[OTHER] + + if (count % 10 >= 2) and (count % 10 <= 4): + return words[FEW] + + return words[OTHER] + +def create_reminders_db(chat_id: int) -> None: + database_file = f"./databases/{chat_id}.db" + + if os.path.exists(database_file): + os.remove(database_file) + + with sqlite3.connect(database_file) as conn: + conn.execute("CREATE TABLE Reminders (id INTEGER PRIMARY KEY AUTOINCREMENT, expires_in INTEGER, content TEXT)") + +def add_reminder(chat_id: int, text: str, date: int) -> None: + database_file = f"./databases/{chat_id}.db" + + with sqlite3.connect(database_file) as conn: + cur = conn.cursor() + cur.execute("INSERT INTO Reminders (expires_in, content) VALUES (?, ?)", (date, text)) + + if not chat_id in REMINDERS: + REMINDERS[chat_id] = [] + + REMINDERS[chat_id].append(Reminder(cur.lastrowid, date, text)) + +def get_reminders(chat_id: int) -> list[Reminder]: + database_file = f"./databases/{chat_id}.db" + + now = datetime.now() + timestamp = calendar.timegm(now.timetuple()) + + result = None + with sqlite3.connect(database_file) as conn: + cur = conn.cursor() + cur.execute("SELECT * FROM Reminders") + result = cur.fetchall() + + def row_to_reminder(row: list[Any]) -> Reminder: + id = int(row[0]) + date = int(row[1]) + text = str(row[2]) + active = timestamp < date + + return Reminder(id, date, text, active) + + return list(map(row_to_reminder, result)) + +@dp.message(CommandStart()) +async def command_start_handler(message: Message) -> None: + await asyncio.to_thread(create_reminders_db, message.chat.id) + await message.answer(START_MESSAGE) + +@dp.message(Command(WEATHER_COMMAND)) +async def command_weather_handler(message: Message, command: CommandObject) -> None: + if command.args is None or command.args.isspace(): + await message.answer("Р’С‹ РЅРµ указали РіРѕСЂРѕРґ.\nПример использования: /weather Санкт-Петербург") + return + + try: + observation = mgr.weather_at_place(command.args) + except OwmNotFoundError: + await message.answer("Город РЅРµ найден :(") + return + + w: Weather = observation.weather + l: Location = observation.location + + detailed_status: str = w.detailed_status + country_name = COUNTRY_CODE_TO_COUNTRY_NAME[l.country] + temperature = w.temperature(unit="celsius") + temp = int(round(temperature["temp"])) + temp_max = int(round(temperature["temp_max"])) + temp_min = int(round(temperature["temp_min"])) + feels_like = int(round(temperature["feels_like"])) + humidity = w.humidity + + wind = w.wind() + wind_speed = wind["speed"] + + flag = COUNTRY_CODE_TO_FLAG[l.country] + + await message.answer(f"Место: <b>{l.name}, {country_name} {flag}</b>\nРџРѕРіРѕРґР°: <b>{detailed_status.capitalize()}</b>\nТемпература: <b>{temp} В°C</b>\nМакс. температура: <b>{temp_max} В°C</b>\nРњРёРЅ. температура: <b>{temp_min} В°C</b>\nОщущается как: <b>{feels_like} В°C</b>\nВлажность: <b>{humidity}%</b>\nВетер: <b>{wind_speed} Рј/c</b>") + +def get_current_reminders_text(reminders: list[Reminder]) -> str | None: + if len(reminders) == 0: + return None + + text = "РЎРїРёСЃРѕРє напоминаний:\n" + for i, reminder in enumerate(reminders, start=1): + date = datetime.fromtimestamp(reminder.date, UTC).strftime("%H:%M, %d/%m/%Y") + status = '\u231B' if reminder.active else '\u2705' + text += f"{i}. {status} [{date}] {reminder.text}\n\n" + return text + +@dp.message(Command(REMINDERS_COMMAND)) +async def command_reminders_handler(message: Message) -> None: + reminders = await asyncio.to_thread(get_reminders, message.chat.id) + if len(reminders) == 0: + await message.answer("РЈ вас нет напоминаний.") + return + + text = get_current_reminders_text(reminders) + + completed_present = any(not reminder.active for reminder in reminders) + + reply_markup = None + if completed_present: + keyboard = InlineKeyboardBuilder() + keyboard.button(text="Удалить завершённые напоминания", callback_data=ReminderCallback(action=ReminderAction.DELETE_COMPLETED).pack()) + reply_markup = keyboard.as_markup() + + await message.answer(text, reply_markup=reply_markup) + +def delete_completed_reminders(chat_id: int) -> int: + database_file = f"./databases/{chat_id}.db" + + reminders = REMINDERS[chat_id] + + deleted_count = 0 + + with sqlite3.connect(database_file) as conn: + for i in reversed(range(len(reminders))): + reminder = reminders[i] + if reminder.active: continue + del reminders[i] + + conn.execute("DELETE FROM Reminders WHERE id = ?", (reminder.id,)) + deleted_count += 1 + + conn.commit() + + return deleted_count + + +@dp.callback_query(ReminderCallback.filter(F.action == ReminderAction.DELETE_COMPLETED)) +async def handle_delete_completed_reminders(query: CallbackQuery, callback_data: ReminderCallback, bot: Bot) -> None: + action = callback_data.action + + chat_id = query.from_user.id + + if action == ReminderAction.DELETE_COMPLETED: + reminders = REMINDERS[chat_id] + + if len(reminders) > 0: + deleted_count = await asyncio.to_thread(delete_completed_reminders, chat_id) + + word = get_word_case(deleted_count, ("неактивное напоминание", "неактивных напоминания", "неактивных напоминаний")) + + new_text = f"Р’С‹ успешно удалили {deleted_count} {word}.\n" + + reminders_text = get_current_reminders_text(reminders) + + new_text += reminders_text if reminders_text else "РќР° данный момент Сѓ вас нет напоминаний." + + await query.message.delete_reply_markup() + await query.message.edit_text(new_text) + else: + await query.message.answer(f"РЈ вас РїРѕРєР° нет завершённых напоминаний.") + + await query.answer() + +@reminder_router.message(Command(CANCEL_COMMAND)) +@reminder_router.message(F.text.casefold() == "cancel") +async def new_reminder_cancel(message: Message, state: FSMContext) -> None: + current_state = await state.get_state() + if current_state is None: + await message.answer("Нечего отменять.", reply_markup=ReplyKeyboardRemove()) + return + + await state.clear() + await message.answer("Создание напоминания отменено.", reply_markup=ReplyKeyboardRemove()) + +@reminder_router.message(Command(REMINDER_COMMAND)) +async def command_new_reminder_handler(message: Message, state: FSMContext) -> None: + current_state = await state.get_state() + if current_state is not None: + await state.clear() + + await state.set_state(NewReminderState.text) + await message.answer("Введите текст напоминания", reply_markup=ReplyKeyboardRemove()) + +@reminder_router.message(NewReminderState.text) +async def new_reminder_text(message: Message, state: FSMContext) -> None: + await state.update_data(text=message.text) + await state.set_state(NewReminderState.date) + await message.answer("Теперь введите время Рё дату напоминания РІ формате: <b>ЧЧ:РњРњ ДД/РњРњ/ГГГГ</b>\n\nПримечание:\n<i>Дата РЅРµ обязательна, РїРѕ умолчанию - текущий день</i>") + +@reminder_router.message(NewReminderState.date) +async def new_reminder_date(message: Message, state: FSMContext) -> None: + example_date = "10:20 23/12/2025" + + date_time = message.text.split(" ") + if len(date_time) == 0: + await message.answer(f"Р’С‹ указали время Рё дату неправильно.\nПример: <b>{example_date}</b>") + return + + t = date_time[0].split(":") + if len(t) != 2: + await message.answer(f"Р’С‹ указали время неправильно.\nПример: <b>{example_date}</b>") + return + + try: + hours = int(t[0]) + except ValueError: + await message.answer(f"Р’С‹ указали время неправильно.\nПример: <b>{example_date}</b>") + return + + if hours >= 24: + await message.answer(f"Час должен быть меньше 24.\nПример: <b>{example_date}</b>") + return + + if hours < 0: + await message.answer(f"Час должен быть больше или равен нулю.\nПример: <b>{example_date}</b>") + return + + try: + minutes = int(t[1]) + except ValueError: + await message.answer(f"Р’С‹ указали время неправильно.\nПример: <b>{example_date}</b>") + return + + if minutes >= 60: + await message.answer(f"Минуты должны быть меньше 60.\nПример: <b>{example_date}</b>") + return + + if minutes < 0: + await message.answer(f"Минуты должны быть больше или равны нулю.\nПример: <b>{example_date}</b>") + return + + today = datetime.today() + + day = today.day + month = today.month + year = today.year + + if len(date_time) > 1: + d = date_time[1].split('/') + if len(d) != 3: + await message.answer(f"Р’С‹ указали дату неправильно.\nПример: <b>{example_date}</b>") + return + + try: + day = int(d[0]) + except ValueError: + await message.answer(f"Р’С‹ указали дату неправильно.\nПример: <b>{example_date}</b>") + return + + if day < 0: + await message.answer(f"День РЅРµ может быть меньше <b>нуля</b>.\nПример: <b>{example_date}</b>") + return + + try: + month = int(d[1]) + except ValueError: + await message.answer(f"Р’С‹ указали дату неправильно.\nПример: <b>{example_date}</b>") + return + + if not 1 <= month <= 12: + await message.answer(f"Месяц должен быть РІ диапазоне РѕС‚ <b>1</b> РґРѕ <b>12</b>.\nПример: <b>{example_date}</b>") + return + + try: + year = int(d[2]) + except ValueError: + await message.answer(f"Р’С‹ указали дату неправильно.\nПример: <b>{example_date}</b>") + return + + _, max_days = calendar.monthrange(year, month) + + if day > max_days: + await message.answer(f"Последний день месяца - <b>{max_days}</b>, Р° РЅРµ <b>{day}</b>.\nПример: <b>{example_date}</b>") + return + + now = datetime.now() + now_timestamp = calendar.timegm(now.timetuple()) + + date = datetime(year, month, day, hours, minutes) + timestamp = calendar.timegm(date.timetuple()) + + if timestamp < now_timestamp: + await message.answer(f"Создание напоминания РЅР° прошедшую дату бессмыслено. Введите РґСЂСѓРіСѓСЋ дату.") + return + + data = await state.update_data(date=timestamp) + + await asyncio.to_thread(add_reminder, message.chat.id, data['text'], data['date']) + + diff_date = relativedelta(date, now) + + time_values = ( + (diff_date.years, TimeUnit.YEAR), + (diff_date.months, TimeUnit.MONTH), + (diff_date.days, TimeUnit.DAY), + (diff_date.hours, TimeUnit.HOUR), + (diff_date.minutes, TimeUnit.MINUTE), + (diff_date.seconds, TimeUnit.SECOND) + ) + + add_comma = False + notifies_in = "" + for value, unit in time_values: + if unit != TimeUnit.SECOND and value <= 0: continue + + if add_comma: notifies_in += ", " + add_comma = True + notifies_in += f"{value} " + cases: tuple[str, str, str] = None + match unit: + case TimeUnit.YEAR: cases = ("РіРѕРґ", "РіРѕРґР°", "лет") + case TimeUnit.MONTH: cases = ("месяц", "месяца", "месяцев") + case TimeUnit.DAY: cases = ("день", "РґРЅСЏ", "дней") + case TimeUnit.HOUR: cases = ("час", "часа", "часов") + case TimeUnit.MINUTE: cases = ("минуту", "минуты", "РјРёРЅСѓС‚") + case TimeUnit.SECOND: cases = ("секунду", "секунды", "секунд") + notifies_in += get_word_case(value, cases) + notifies_in += '.' + + await message.answer(f"Напоминание успешно создано! РЇ напомню вам РѕР± этом через <b>{notifies_in}</b>") + + await state.clear() + +async def check_reminders_expiration() -> None: + now = datetime.now() + timestamp = calendar.timegm(now.timetuple()) + + for chat_id, reminders in REMINDERS.items(): + for reminder in reminders: + if reminder.active and timestamp > reminder.date: + reminder.active = False + + date = datetime.fromtimestamp(reminder.date, UTC).strftime("%H:%M, %d/%m/%Y") + await bot.send_message(chat_id, f"Напоминание:\n\n{reminder.text}\n\n{date}") + +class RpsVariant(Enum): + ROCK = 1 + PAPER = 2 + SCISSORS = 3 + + def from_name(name: str) -> Self | None: + match name: + case "камень": return RpsVariant.ROCK + case "бумага": return RpsVariant.PAPER + case "ножницы": return RpsVariant.SCISSORS + case _: return None + + @property + def name(self) -> str: + match self: + case RpsVariant.ROCK: return "камень" + case RpsVariant.PAPER: return "бумага" + case RpsVariant.SCISSORS: return "ножницы" + + @property + def name_acusative(self) -> str: + match self: + case RpsVariant.ROCK: return "камень" + case RpsVariant.PAPER: return "бумагу" + case RpsVariant.SCISSORS: return "ножницы" + +RPS_VARIANTS = [RpsVariant.ROCK, RpsVariant.PAPER, RpsVariant.SCISSORS] + +RPS_MAP = { + RpsVariant.ROCK: RpsVariant.SCISSORS, + RpsVariant.PAPER: RpsVariant.ROCK, + RpsVariant.SCISSORS: RpsVariant.PAPER +} + +@dp.message(Command(RPS_COMMAND)) +async def command_rps_handler(message: Message, command: CommandObject) -> None: + if not command.args or command.args.isspace(): + await message.answer("Р’С‹ РЅРµ выбрали предмет.\nПример использования: /rps камень") + return + + user_variant = RpsVariant.from_name(command.args.lower()) + + if user_variant is None: + await message.answer(f"Такого варианта нет.\nВозможные варианты: {RpsVariant.ROCK.name}, {RpsVariant.SCISSORS.name}, {RpsVariant.PAPER.name}.") + return + + variant = random.choice(RPS_VARIANTS) + + if RPS_MAP.get(user_variant) == variant: + await message.answer(f"Р’С‹ выиграли! РЇ выбрал <b>{variant.name_acusative}</b>.") + elif RPS_MAP.get(variant) == user_variant: + await message.answer(f"Р’С‹ проиграли! РЇ выбрал <b>{variant.name_acusative}</b>.") + else: + await message.answer(f"Ничья! РЇ выбрал <b>{variant.name_acusative}</b>.") + +def load_all_reminders() -> None: + now = datetime.now() + timestamp = calendar.timegm(now.timetuple()) + + for address, _, files in os.walk("./databases/"): + for name in files: + chat_id = int(name[:-3]) + path = os.path.join(address, name) + + result = None + with sqlite3.connect(path) as conn: + cur = conn.cursor() + cur.execute("SELECT * FROM Reminders") + result = cur.fetchall() + + reminders: list[Reminder] = [] + for id, date, text in result: + active = timestamp < date + reminders.append(Reminder(id, date, text, active=active)) + REMINDERS[chat_id] = reminders + +async def main() -> None: + await bot.set_my_commands(commands=ALL_COMMANDS) + + dp.include_router(reminder_router) + + scheduler = AsyncIOScheduler() + scheduler.add_job(check_reminders_expiration, IntervalTrigger(seconds=10)) + scheduler.start() + + logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING) + + await dp.start_polling(bot) + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, stream=sys.stdout) + load_all_reminders() + asyncio.run(main()) \ No newline at end of file diff --git "a/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5.pdf" "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..581f42ec26dffc35507a2ed0a4d65ddf383a4507 Binary files /dev/null and "b/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5/\320\222\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 2.5.pdf" differ diff --git a/Efimov SR, ISR, Task 1.2.pdf "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.2.pdf" similarity index 100% rename from Efimov SR, ISR, Task 1.2.pdf rename to "\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.2.pdf" diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.3.png" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.3.png" new file mode 100644 index 0000000000000000000000000000000000000000..47df0ca28dada69b463e4350c3abca067902413b Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.3.png" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.4.pdf" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.4.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..46ff76ade1520269eb1e3e01ad5abfa68ca2b617 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.4.pdf" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.5.pdf" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.5.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..87a19cd4c629da428533e75dfda90fa24dc8e279 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.5.pdf" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.6.pdf" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.6.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..0e46706ab925fb6cf672105892437a41363899e7 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.6.pdf" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.7.pdf" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.7.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..8fd41b472bd42c1289484fb811a5ec3bf7ad3d76 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.7.pdf" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.8.pdf" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.8.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..c208a56d9e667dc555a1f94eea714880808395f6 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.8.pdf" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.9/\320\230\320\241\320\240 1.9.pdf" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.9/\320\230\320\241\320\240 1.9.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..ae510b9d2dc3a1d34aee4c1e9da2bf67ac4f50b6 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.9/\320\230\320\241\320\240 1.9.pdf" differ diff --git "a/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.9/\320\230\320\241\320\240 1.9.png" "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.9/\320\230\320\241\320\240 1.9.png" new file mode 100644 index 0000000000000000000000000000000000000000..7e4706141150710a894bccdb39df91511f618b13 Binary files /dev/null and "b/\320\230\320\241\320\240, \320\227\320\260\320\264\320\260\320\275\320\270\320\265 1.9/\320\230\320\241\320\240 1.9.png" differ diff --git "a/\320\267\320\260\320\264\320\260\320\275\320\270\320\265 \320\275\320\260 \320\277\321\200\320\260\320\272\321\202\320\270\320\272\321\203 1 \320\272\321\203\321\200\321\201 (\321\200\321\203\320\272\320\276\320\262\320\276\320\264\320\270\321\202\320\265\320\273\321\214 \320\230\320\273\321\214\320\270\320\275\320\260 \320\242\320\241).docx" "b/\320\267\320\260\320\264\320\260\320\275\320\270\320\265 \320\275\320\260 \320\277\321\200\320\260\320\272\321\202\320\270\320\272\321\203 1 \320\272\321\203\321\200\321\201 (\321\200\321\203\320\272\320\276\320\262\320\276\320\264\320\270\321\202\320\265\320\273\321\214 \320\230\320\273\321\214\320\270\320\275\320\260 \320\242\320\241).docx" new file mode 100644 index 0000000000000000000000000000000000000000..6a3720315a5de5d07a34720dacf2945e32b85624 Binary files /dev/null and "b/\320\267\320\260\320\264\320\260\320\275\320\270\320\265 \320\275\320\260 \320\277\321\200\320\260\320\272\321\202\320\270\320\272\321\203 1 \320\272\321\203\321\200\321\201 (\321\200\321\203\320\272\320\276\320\262\320\276\320\264\320\270\321\202\320\265\320\273\321\214 \320\230\320\273\321\214\320\270\320\275\320\260 \320\242\320\241).docx" differ diff --git "a/\320\276\321\202\321\207\320\265\321\202 \320\277\320\276 \320\277\321\200\320\260\320\272\321\202\320\270\320\272\320\265 1 \320\272\321\203\321\200\321\201 (\321\200\321\203\320\272\320\276\320\262\320\276\320\264\320\270\321\202\320\265\320\273\321\214 \320\230\320\273\321\214\320\270\320\275\320\260 \320\242\320\241).docx" "b/\320\276\321\202\321\207\320\265\321\202 \320\277\320\276 \320\277\321\200\320\260\320\272\321\202\320\270\320\272\320\265 1 \320\272\321\203\321\200\321\201 (\321\200\321\203\320\272\320\276\320\262\320\276\320\264\320\270\321\202\320\265\320\273\321\214 \320\230\320\273\321\214\320\270\320\275\320\260 \320\242\320\241).docx" new file mode 100644 index 0000000000000000000000000000000000000000..8e110792939793c265f70c4ac5ff3d91a2f74db8 Binary files /dev/null and "b/\320\276\321\202\321\207\320\265\321\202 \320\277\320\276 \320\277\321\200\320\260\320\272\321\202\320\270\320\272\320\265 1 \320\272\321\203\321\200\321\201 (\321\200\321\203\320\272\320\276\320\262\320\276\320\264\320\270\321\202\320\265\320\273\321\214 \320\230\320\273\321\214\320\270\320\275\320\260 \320\242\320\241).docx" differ