diff --git a/.gitignore b/.gitignore
index 491c0a0..2de64f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,8 @@
.idea/
venv/
-spamer/
**/__pycache__/
-users_json_files/
-bot/bot_main/for_photo_creation/photo_size.txt
-test.py
-bot_db.sql
+temp_data/
database_dump
.env
+photo_size.txt
+test.py
diff --git a/bot/__init__.py b/bot/__init__.py
new file mode 100644
index 0000000..ff35bf4
--- /dev/null
+++ b/bot/__init__.py
@@ -0,0 +1,31 @@
+from .commands import (
+ start_command,
+ help_command,
+ keywords_command,
+ time_command,
+ date_calculator_command,
+ add_text_to_photo_command,
+ weather_command,
+ currency_converter_command,
+ task_scheduler_command,
+ password_generator_command,
+)
+from .keywords import (
+ keywords_for_interaction
+)
+from .bot_starter import MyBot
+
+__all__ = (
+ "start_command",
+ "help_command",
+ "keywords_command",
+ "time_command",
+ "date_calculator_command",
+ "add_text_to_photo_command",
+ "weather_command",
+ "currency_converter_command",
+ "task_scheduler_command",
+ "password_generator_command",
+ "keywords_for_interaction",
+ "MyBot",
+)
diff --git a/bot/bot_main/bot_classes/ConnectionDB.py b/bot/bot_main/bot_classes/ConnectionDB.py
deleted file mode 100644
index 14c3f68..0000000
--- a/bot/bot_main/bot_classes/ConnectionDB.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-import mysql
-import mysql.connector
-
-
-from dotenv import load_dotenv, find_dotenv
-
-
-class ConnectionDB:
- def __init__(self):
- load_dotenv(find_dotenv())
- self.con = mysql.connector.connect(
- host=os.getenv('HOST'),
- user=os.getenv('DB_USER'),
- passwd=os.getenv('PASSWORD'),
- database=os.getenv('DATABASE'),
- )
diff --git a/bot/bot_main/bot_classes/ConverterForm.py b/bot/bot_main/bot_classes/ConverterForm.py
deleted file mode 100644
index d6b2a48..0000000
--- a/bot/bot_main/bot_classes/ConverterForm.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class ConverterForm(StatesGroup):
- dollar = State()
- euro = State()
- hryvnia = State()
diff --git a/bot/bot_main/bot_classes/DaysToBirthday.py b/bot/bot_main/bot_classes/DaysToBirthday.py
deleted file mode 100644
index 14037d0..0000000
--- a/bot/bot_main/bot_classes/DaysToBirthday.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class DaysToBirthday(StatesGroup):
- day_month = State()
diff --git a/bot/bot_main/bot_classes/DuplicateDescriptionError.py b/bot/bot_main/bot_classes/DuplicateDescriptionError.py
deleted file mode 100644
index 77dbac2..0000000
--- a/bot/bot_main/bot_classes/DuplicateDescriptionError.py
+++ /dev/null
@@ -1,3 +0,0 @@
-class DuplicateDescriptionError(Exception):
- def __init__(self, message):
- self.message = message
diff --git a/bot/bot_main/bot_classes/PasswordGeneratorStates.py b/bot/bot_main/bot_classes/PasswordGeneratorStates.py
deleted file mode 100644
index d792d84..0000000
--- a/bot/bot_main/bot_classes/PasswordGeneratorStates.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class PasswordGeneratorStates(StatesGroup):
- update_description = State()
- update_password = State()
- delete = State()
-
- set_description = State()
diff --git a/bot/bot_main/bot_classes/PhotoInscription.py b/bot/bot_main/bot_classes/PhotoInscription.py
deleted file mode 100644
index e78c186..0000000
--- a/bot/bot_main/bot_classes/PhotoInscription.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class PhotoInscription(StatesGroup):
- user_incription_config = State()
diff --git a/bot/bot_main/bot_classes/SearchTerm.py b/bot/bot_main/bot_classes/SearchTerm.py
deleted file mode 100644
index 22cc787..0000000
--- a/bot/bot_main/bot_classes/SearchTerm.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class SearchTerm(StatesGroup):
- search_term = State()
diff --git a/bot/bot_main/bot_classes/StatisticsTable.py b/bot/bot_main/bot_classes/StatisticsTable.py
deleted file mode 100644
index ab3e470..0000000
--- a/bot/bot_main/bot_classes/StatisticsTable.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from bot.bot_main.bot_classes.ConnectionDB import ConnectionDB
-
-
-class StatisticsTable(ConnectionDB):
- def __init__(self):
- super().__init__()
- self.cur = self.con.cursor()
- self.create_table()
-
-
- def create_table(self):
- self.cur.execute(
- '''
- CREATE TABLE IF NOT EXISTS statistics (
- id INT PRIMARY KEY AUTO_INCREMENT,
- usage_date DATE NOT NULL,
- user_id INT NOT NULL,
- task_sched_delete_num INT DEFAULT 0,
- task_sched_update_num INT DEFAULT 0,
- task_sched_insert_num INT DEFAULT 0,
- pass_gen_delete_num INT DEFAULT 0,
- pass_gen_update_num INT DEFAULT 0,
- pass_gen_insert_num INT DEFAULT 0,
- CONSTRAINT unique_date_id UNIQUE (usage_date, user_id),
- FOREIGN KEY (user_id) REFERENCES users_info(user_id) ON DELETE CASCADE ON UPDATE CASCADE
- )
- '''
- )
\ No newline at end of file
diff --git a/bot/bot_main/bot_classes/StickerTable.py b/bot/bot_main/bot_classes/StickerTable.py
deleted file mode 100644
index 36f60ac..0000000
--- a/bot/bot_main/bot_classes/StickerTable.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from bot.bot_main.bot_classes.ConnectionDB import ConnectionDB
-
-
-class StickerTable(ConnectionDB):
- def __init__(self):
- super().__init__()
- self.cur = self.con.cursor()
- self.create_table_stickers()
-
-
- def create_table_stickers(self):
- self.cur.execute(
- '''
- CREATE TABLE IF NOT EXISTS stickers(
- id INT PRIMARY KEY AUTO_INCREMENT,
- sticker VARCHAR(80),
- CONSTRAINT unique_sticker UNIQUE(sticker)
- )
- '''
- )
-
- def get_sticker(self):
- self.cur.execute(
- '''
- SELECT sticker FROM stickers
- ORDER BY id;
- '''
- )
-
- result = self.cur.fetchall()
-
- return result
-
- def insert_into_sticker_table(self, sticker_id):
- self.cur.execute(
- '''
- INSERT IGNORE INTO stickers(sticker)
- VALUES (%s)
- ''',
- (sticker_id,)
- )
-
- self.con.commit()
\ No newline at end of file
diff --git a/bot/bot_main/bot_classes/TaskScheduler.py b/bot/bot_main/bot_classes/TaskScheduler.py
deleted file mode 100644
index 6444216..0000000
--- a/bot/bot_main/bot_classes/TaskScheduler.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class TaskScheduler(StatesGroup):
- insert = State()
- update = State()
- delete = State()
diff --git a/bot/bot_main/bot_classes/UserSticker.py b/bot/bot_main/bot_classes/UserSticker.py
deleted file mode 100644
index b6a3aae..0000000
--- a/bot/bot_main/bot_classes/UserSticker.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from aiogram.dispatcher.filters.state import StatesGroup, State
-
-
-class UserSticker(StatesGroup):
- user_sticker = State()
diff --git a/bot/bot_main/bot_classes/UserToken.py b/bot/bot_main/bot_classes/UserToken.py
deleted file mode 100644
index 64de878..0000000
--- a/bot/bot_main/bot_classes/UserToken.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from bot.bot_main.bot_classes.ConnectionDB import ConnectionDB
-
-
-class UserToken(ConnectionDB):
- def __init__(self):
- super().__init__()
- self.cur = self.con.cursor()
- self.create_table()
-
-
- def create_table(self):
- self.cur.execute(
- '''
- CREATE TABLE IF NOT EXISTS generated_tokens (
- id INT PRIMARY KEY AUTO_INCREMENT,
- user_id INT NOT NULL,
- user_token VARCHAR(150),
- CONSTRAINT unique_token UNIQUE (user_id),
- FOREIGN KEY (user_id) REFERENCES users_info(user_id) ON DELETE CASCADE ON UPDATE CASCADE
- )
- '''
- )
-
-
- def select_all_tokens(self):
- self.cur.execute(
- '''
- SELECT user_token FROM generated_tokens
- '''
- )
-
- all_tokens_tuple_lst = self.cur.fetchall()
- all_tokens_lst = [token_tuple[0] for token_tuple in all_tokens_tuple_lst]
-
- return all_tokens_lst
-
-
- def select_token(self, get_user_id):
- self.cur.execute(
- '''
- SELECT user_token FROM generated_tokens
- WHERE user_id = %s
- ''',
- (get_user_id,)
- )
-
- get_token = self.cur.fetchone()
-
- if get_token:
- return get_token[0]
-
-
- def add_token(self, get_user_id, get_new_token):
- self.cur.execute(
- '''
- INSERT INTO generated_tokens(user_id, user_token)
- VALUES (%s, %s)
- ON DUPLICATE KEY UPDATE user_token = %s
- ''',
- (get_user_id, get_new_token, get_new_token)
- )
-
- self.con.commit()
-
-
- # def update_token(self, get_new_token, get_user_id):
- # self.cur.execute(
- # '''
- # UPDATE generated_tokens
- # SET user_token = %s
- # WHERE user_id = %s
- # ''',
- # (get_new_token, get_user_id,)
- # )
-
-
- def remove_token(self, get_user_id):
- self.cur.execute(
- '''
- DELETE FROM generated_tokens
- WHERE user_id = %s
- ''',
- (get_user_id,)
- )
-
- self.con.commit()
diff --git a/bot/bot_main/bot_classes/UsersDataStore.py b/bot/bot_main/bot_classes/UsersDataStore.py
deleted file mode 100644
index 059d3ed..0000000
--- a/bot/bot_main/bot_classes/UsersDataStore.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from bot.bot_main.bot_classes.ConnectionDB import ConnectionDB
-
-
-class UsersDataStore(ConnectionDB):
- def __init__(self):
- super().__init__()
- self.cur = self.con.cursor()
- self.update_start_date_after_month()
-
-
- def create_table(self):
- self.cur.execute(
- '''
- CREATE TABLE IF NOT EXISTS users_info(
- id INT PRIMARY KEY AUTO_INCREMENT,
- user_id INT NOT NULL,
- username VARCHAR(200) NOT NULL,
- full_name VARCHAR(200) NOT NULL,
- chat_id VARCHAR(30) NOT NULL,
- start_msg_date DATE DEFAULT(CURRENT_DATE),
- CONSTRAINT unique_data UNIQUE(user_id)
- )
- '''
- )
-
-
- def update_start_date_after_month(self):
- self.cur.execute(
- '''
- UPDATE users_info, statistics
- SET start_msg_date = CURDATE()
- WHERE DATEDIFF(usage_date, start_msg_date) > 30
- '''
- )
-
- self.con.commit()
-
-
- def connect_to_db(self, user_id, username, full_name, chat_id):
- self.cur.execute(
- '''
- INSERT IGNORE INTO users_info (user_id, username, full_name, chat_id)
- VALUES (%s, %s, %s, %s)
- ON DUPLICATE KEY UPDATE username = %s, full_name = %s, chat_id = %s
- ''',
- (user_id, username, full_name, chat_id, username, full_name, chat_id)
- )
-
- self.con.commit()
diff --git a/bot/bot_main/commands_and_keywords/add_text_to_photo_command.py b/bot/bot_main/commands_and_keywords/add_text_to_photo_command.py
deleted file mode 100644
index 38ac163..0000000
--- a/bot/bot_main/commands_and_keywords/add_text_to_photo_command.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-from aiogram.types import ContentType, InputFile
-
-from bot.bot_main.bot_classes.PhotoInscription import PhotoInscription
-from bot.bot_main.for_photo_creation.photo_size import get_data_from_txt
-from bot.bot_main.for_photo_creation.validate_args_number import validate_args
-from bot.bot_main.main_objects_initialization import dp, bot
-from bot.other_functions.delete_with_delay import delete_messages
-
-
-@dp.message_handler(state='*', commands=['photo'])
-async def get_photo_config(message: types.Message):
- await PhotoInscription.user_incription_config.set()
- bot_message = await message.reply(
- 'Send me an image with description where have to '
- 'be a text which i will add to your image, text must be '
- 'in the following format: example//100//200//233//500//20\n\n'
- 'example - this is a text that will be added to image)\n'
- '100 - X coordinate (only integer allowed)\n'
- '200 - Y coordinate (only integer allowed\n'
- '233 - image width (only integer allowed)\n'
- '500 - image height (only integer allowed)\n'
- '20 - font size (only integer allowed)',
- parse_mode='HTML'
- )
-
- await delete_messages(message, bot_message)
-
-@dp.message_handler(state=PhotoInscription.user_incription_config, content_types=ContentType.PHOTO)
-async def create_photo(message: types.Message, state: FSMContext):
- photo = message.photo[-1]
- await photo.download(destination_file='imgs/test.jpg')
-
- description = message.caption
-
- if not description:
- await message.reply('Sorry, that photo doesn’t have a caption. Try again.')
- else:
- user_input_lst = description.split('//', 5)
- formatted_lst = [args.strip() for args in user_input_lst]
-
- create_photo_using_args = await validate_args(formatted_lst, message)
-
- if create_photo_using_args:
- result_photo = InputFile('imgs/result.jpg')
- txt_data = get_data_from_txt()
- await bot.send_photo(
- message.chat.id,
- photo=result_photo, caption=f'{txt_data}', parse_mode='HTML'
- )
-
- await state.finish()
diff --git a/bot/bot_main/commands_and_keywords/currency_converter_command.py b/bot/bot_main/commands_and_keywords/currency_converter_command.py
deleted file mode 100644
index c17d259..0000000
--- a/bot/bot_main/commands_and_keywords/currency_converter_command.py
+++ /dev/null
@@ -1,138 +0,0 @@
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-
-from bot.bot_main.bot_classes.ConverterForm import ConverterForm
-from bot.bot_main.main_objects_initialization import dp
-from bot.keyboards.currency_converter.converter_keyboard import converter_keyboard
-from bot.keyboards.currency_converter.currency_keyboard import currency_keyboard
-from bot.keyboards.currency_converter.return_keyboard import return_keyboard
-from bot.other_functions import currency_cost as cc
-from bot.other_functions.close_keyboard import close_keyboard
-
-
-@dp.message_handler(state='*', commands=['currency'])
-async def currency(message: types.Message):
- await message.reply('Choose menu option💵:', reply_markup=currency_keyboard)
-
-
-@dp.callback_query_handler(text=['currency_rate'])
-async def exchange_rate(call: types.CallbackQuery):
- await call.message.edit_text(
- f'BUY / SALE\n'
- f''
- f'1 UAH = {cc.find_dollars_buy_in_hryvnias():.4f} / {cc.find_dollars_sale_in_hryvnias():.4f} USD\n'
- f'1 UAH = {cc.find_euros_buy_in_hryvnias():.4f} / {cc.find_euros_sale_in_hryvnias():.4f} EUR\n'
- f'1 EUR = {cc.find_dollars_buy_in_euros():.4f} / {cc.find_dollars_sale_in_euros():.4f} USD\n'
- f'1 EUR = {cc.find_hryvnias_buy_in_euros():.4f} / {cc.find_hryvnias_sale_in_euros():.4f} UAH\n'
- f'1 USD = {cc.find_euros_buy_in_dollars():.4f} / {cc.find_euros_sale_in_dollars():.4f} EUR\n'
- f'1 USD = {cc.find_hryvnias_buy_in_dollars():.4f} / {cc.find_hryvnias_sale_in_dollars():.4f} UAH'
- f'', parse_mode='HTML', reply_markup=return_keyboard
- )
- await call.answer()
-
-
-@dp.callback_query_handler(text=['currency_converter'])
-async def converter(call: types.CallbackQuery):
- await call.message.edit_text('Currency converter:', reply_markup=converter_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text=['convert_dollar'])
-async def convert_dollar(call: types.CallbackQuery):
- await ConverterForm.dollar.set()
- await call.answer('Enter the amount of $ to convert into euros and hryvnias...', True)
-
-
-@dp.message_handler(state=ConverterForm.dollar)
-async def process_dollar(message: types.Message, state: FSMContext):
- input_data = message.text
-
- try:
- input_data = input_data.replace(',','.')
- answer = float(input_data)
-
- await message.reply(
- f'Cost of buying {input_data} dollars in hryvnias: '
- f'{answer / cc.find_dollars_buy_in_hryvnias():.4f}\n'
- f'Selling price of {input_data} dollars in hryvnias: '
- f'{answer / cc.find_dollars_sale_in_hryvnias():.4f}\n'
- f'Cost of buying {input_data} dollars in euros: '
- f'{answer / cc.find_dollars_buy_in_euros():.4f}\n'
- f'Selling price of {input_data} dollars in euros: '
- f'{answer / cc.find_dollars_sale_in_euros():.4f}\n',
- )
- except ValueError:
- await message.reply('Invalid input. Must be a number. Try again.')
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['convert_euro'])
-async def convert_euro(call: types.CallbackQuery):
- await ConverterForm.euro.set()
- await call.answer('Enter the amount of € to convert to dollars and hryvnias...', True)
-
-
-@dp.message_handler(state=ConverterForm.euro)
-async def process_euro(message: types.Message, state: FSMContext):
- input_data = message.text
-
- try:
- input_data = input_data.replace(',', '.')
- answer = float(input_data)
-
- await message.reply(
- f'Cost of buying {input_data} euros in hryvnias: '
- f'{answer / cc.find_euros_buy_in_hryvnias():.4f}\n'
- f'Selling price of {input_data} euros in hryvnias: '
- f'{answer / cc.find_euros_sale_in_hryvnias():.4f}\n'
- f'Cost of buying {input_data} euros in dollars: '
- f'{answer / cc.find_euros_buy_in_dollars():.4f}\n'
- f'Selling price of {input_data} euros in dollars: '
- f'{answer / cc.find_euros_sale_in_dollars():.4f}\n',
- )
- except ValueError:
- await message.reply('Invalid input. Must be a number. Try again.')
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['convert_hryvnia'])
-async def convert_hryvnia(call: types.CallbackQuery):
- await ConverterForm.hryvnia.set()
- await call.answer('Enter the amount of ₴ to convert to dollars and euros...', True)
-
-
-@dp.message_handler(state=ConverterForm.hryvnia)
-async def process_hryvnia(message: types.Message, state: FSMContext):
- input_data = message.text
-
- try:
- input_data = input_data.replace(',', '.')
- answer = float(input_data)
-
- await message.reply(
- f'Cost of buying {input_data} hryvnias in dollars:'
- f'{answer / cc.find_hryvnias_buy_in_dollars():.4f}\n'
- f'Selling price of {input_data} hryvnias in dollars: '
- f'{answer / cc.find_hryvnias_sale_in_dollars():.4f}\n'
- f'Cost of buying {input_data} hryvnias in euros: '
- f'{answer / cc.find_hryvnias_buy_in_euros():.4f}\n'
- f'Selling price of {input_data} hryvnias in euros: '
- f'{answer / cc.find_hryvnias_sale_in_euros():.4f}\n',
- )
- except ValueError:
- await message.reply('Invalid input. Must be a number. Try again.')
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['back_to_menu'])
-async def back_to_main_menu(call: types.CallbackQuery):
- await call.message.edit_text('Choose menu option💵:', reply_markup=currency_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text=['close_converter'])
-async def close_converter(call: types.CallbackQuery):
- await close_keyboard(call)
diff --git a/bot/bot_main/commands_and_keywords/keywords_command.py b/bot/bot_main/commands_and_keywords/keywords_command.py
deleted file mode 100644
index 235c417..0000000
--- a/bot/bot_main/commands_and_keywords/keywords_command.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from aiogram import types
-
-from bot.bot_main.main_objects_initialization import dp
-
-
-@dp.message_handler(state='*', commands=['keywords'])
-async def get_keywords(message: types.Message):
- await message.reply(
- 'bitcoin — get bitcoin price\n\n'
-
- 'dtr sticker — add your sticker to bot database\n\n'
-
- 'dtr example — send you few examples how to use /photo\n\n'
-
- 'msgd — deletes the bot message you replied to and your message\n\n'
-
- 'random mem any_text — send me an image with caption where must be words "random mem" '
- 'and after this words you can type anything. All what you will type after keyword will be printed '
- 'on your image and sent to you back. Font color will be randomly generated.\n\n'
-
- 'mem any_text — send me an image with caption where must be word "mem" '
- 'and after this word you can type anything. All what you will type after keyword will be printed '
- 'on your image and sent to you. Font color for the image will be automatically set by the system '
- 'depending on the colors that the image contains.\n\n'
-
- 'What date will it be after N days? — send you a message with a date that will '
- 'be after N days from the current date in format dd.mm.yyyy\n\n'
-
- 'What date will be after N days if we start from "your_date"? — send you a message with a date '
- 'that will be after N days from "your_date" '
- '(must be in the same format as in the result) in format dd.mm.yyyy\n\n'
-
- 'dtr delete tasks — deletes the table and all its contents created using the command '
- '/taskscheduler and creates a new empty task table\n\n'
-
- 'dtr delete passwords — deletes the table and all its contents created using the command '
- '/password and creates a new empty password table\n\n',
- parse_mode='HTML'
- )
diff --git a/bot/bot_main/commands_and_keywords/keywords_for_interaction.py b/bot/bot_main/commands_and_keywords/keywords_for_interaction.py
deleted file mode 100644
index 1d6d3db..0000000
--- a/bot/bot_main/commands_and_keywords/keywords_for_interaction.py
+++ /dev/null
@@ -1,206 +0,0 @@
-import re
-from datetime import datetime, timedelta
-
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-from aiogram.dispatcher.filters import IsReplyFilter
-from aiogram.types import ContentType, InputFile
-
-from bot.bot_main.bot_classes.UserSticker import UserSticker
-from bot.bot_main.for_photo_creation.remake_user_photo import create_new_photo_auto_config
-from bot.other_functions import currency_cost as cc
-from bot.bot_main.main_objects_initialization import dp, sticker_table, bot, unique_table, store_users_data
-from bot.other_functions.check_date_words import check_day
-from bot.other_functions.get_days_and_date_num import exctract_from_user_input_days_and_date, \
- exctract_from_user_input_days_num
-# from bot.other_functions.non_admin_message_filter import delete_non_admin_message, get_admin_ids
-from bot.other_functions.remove_start_keyword import remove_mem_from_start, remove_rand_mem_from_start
-
-
-@dp.message_handler(regexp='^dtr delete tasks$|^дтр видали завдання$')
-async def tasks_delete(message: types.Message):
- user_id = message.from_id
- unique_table.drop_remake_task_table(user_id)
- await message.reply(text='All tasks successfully deleted!!!')
-
-
-@dp.message_handler(regexp='^dtr delete passwords$|^дтр видали паролі$')
-async def passwords_delete(message: types.Message):
- user_id = message.from_id
- unique_table.drop_remake_password_table(user_id)
- await message.reply(text='All passwords successfully deleted!!!')
-
-
-@dp.message_handler(regexp='^location$|^місцезнаходження$')
-async def bot_location(message: types.Message):
- await bot.send_location(message.chat.id, latitude=49.924394, longitude=27.746868)
-
-
-@dp.message_handler(regexp='^bitcoin$|^біткоін$|^біток$')
-async def bitcoin_price(message: types.Message):
- await message.reply(
- f'Bitcoin price right now: {cc.bitcoin_price()} $\n', parse_mode='HTML'
- )
-
-
-@dp.message_handler(regexp='^dtr sticker$|^дтр стікер$')
-async def handle_message(message: types.Message):
- await UserSticker.user_sticker.set()
- await message.reply(
- 'Send me a sticker and I will save it to database.'
- 'Then you can use stickers by typing command /sticker.'
- )
-
-
-@dp.message_handler(regexp='^dtr example$|^дтр приклад$')
-async def help_with_photo(message: types.Message):
- await message.reply(
- 'Below you can see examples how to use command /photo\n\n'
- 'example - only one argument. Text "example" '
- 'will be printed in the top left corner, font size = 20.\n\n'
-
- 'example//40 - two arguments. Text "example" '
- 'will be printed in the top left corner, font size = 40.\n\n'
-
- '100//200 - two arguments. Resize photo: width = 100, height = 200\n\n'
-
- 'example//100//200 - three arguments. Text "example" '
- 'will be printed in position where coordinate X = 100 '
- 'and coordinate Y = 200, font size = 20.\n\n'
-
- 'example//100//200//40 - four arguments. Text "example" '
- 'will be printed in position where coordinate X = 100 '
- 'and coordinate Y = 200, font size = 40.\n\n'
-
- 'example//100//200//1000//2000//50 - six arguments. Text "example" '
- 'will be printed in position where coordinate X = 100 '
- 'and coordinate Y = 200. New width will be equal to 1000 and height - 2000, font size = 50.\n',
- parse_mode='HTML'
- )
-
-@dp.message_handler(state=UserSticker.user_sticker, content_types=ContentType.STICKER)
-async def create_photo(message: types.Message, state: FSMContext):
- sended_user_sticker = message.sticker.file_id
- sticker_table.insert_into_sticker_table(sended_user_sticker)
- await message.reply(f'Your sticker was added successfully!')
- await state.finish()
-
-
-@dp.message_handler(regexp='^random mem|^рандом мем', content_types=ContentType.PHOTO)
-async def send_auto_config_photo_with_rand_text_clr(message: types.Message):
- photo = message.photo[-1]
- await photo.download(destination_file='imgs/test_auto_conf.jpg')
- get_user_photo_caption = message.caption
- formatted_text = remove_rand_mem_from_start(get_user_photo_caption)
-
- create_new_photo_auto_config(False, formatted_text)
-
- result_photo = InputFile('imgs/result_auto_conf.jpg')
- await bot.send_photo(message.chat.id, photo=result_photo)
-
-
-@dp.message_handler(regexp='^mem|^мем', content_types=ContentType.PHOTO)
-async def send_auto_config_photo_with_text(message: types.Message):
- photo = message.photo[-1]
- await photo.download(destination_file='imgs/test_auto_conf.jpg')
- get_user_photo_caption = message.caption
- formatted_text = remove_mem_from_start(get_user_photo_caption)
-
- create_new_photo_auto_config(True, formatted_text)
-
- result_photo = InputFile('imgs/result_auto_conf.jpg')
- await bot.send_photo(message.chat.id, photo=result_photo)
-
-
-@dp.message_handler(IsReplyFilter(True), regexp='^msgd$|пвдв$')
-async def delete_two_messages(message: types.Message):
- if message.reply_to_message.from_user.id == bot.id:
- replied_msg_id = message.reply_to_message.message_id
- await bot.delete_message(chat_id=message.chat.id, message_id=replied_msg_id)
- await message.delete()
- else:
- return
-
-
-@dp.message_handler(
- regexp=re.compile(
- '^(Яка дата|Який день) буде через [1-9]+[0-9]* (день|днів|дня)[?]*$|'
- '^What (date|day) will be after [1-9]+[0-9]* (day|days)[?]*$',
- re.IGNORECASE
- )
-)
-async def find_date_after_days_from_current_date(message: types.Message):
- user_input = message.text
- extracted_days = exctract_from_user_input_days_num(user_input)
-
- today = datetime.now()
- answer = today + timedelta(days=extracted_days)
- format_answer = answer.strftime('%d.%m.%Y')
-
- await message.reply(
- f'After {extracted_days} {check_day(extracted_days)} date will be {format_answer}!'
- )
-
-
-@dp.message_handler(
- regexp=re.compile(
- '^(Яка дата|Який день) буде через [1-9]+[0-9]* (день|днів|дня),* якщо починати з '
- '([1-9]|0[1-9]|[1-2][0-9]|3[0-1]).([1-9]|0[1-9]|1[0-2]).([1-9]+.*[0-9]+)[?]*$|'
- '^What (date|day) will be after [1-9]+[0-9]* (day|days) if we start from '
- '([1-9]|0[1-9]|[1-2][0-9]|3[0-1]).([1-9]|0[1-9]|1[0-2]).([1-9]+.*[0-9]+)[?]*$',
- re.IGNORECASE
- )
-)
-async def find_date_after_days(message: types.Message):
- user_input = message.text
- extracted_data = exctract_from_user_input_days_and_date(user_input)
- days_num = extracted_data[0]
- month_day = extracted_data[1][0]
- month = extracted_data[1][1]
- year = extracted_data[1][2]
-
- try:
- user_date = datetime(day=month_day, month=month, year=year)
- format_user_date = user_date.strftime('%d.%m.%Y')
- answer = user_date + timedelta(days=days_num)
- format_answer = answer.strftime('%d.%m.%Y')
-
- await message.reply(
- f'If start from {format_user_date} after '
- f'{days_num} {check_day(days_num)}, date will be {format_answer}!'
- )
- except ValueError:
- await message.reply(
- f'Some data is entered incorrectly!!!\n\n'
- f'I think the problem is the {month_day} (first number in your date) because '
- f'the month you entered does not contain such day of the month.',
- parse_mode='HTML'
- )
-
-
-# @dp.message_handler(content_types=ContentType.VOICE)
-# async def delete_voice_except_admins(message: types.Message):
-# await delete_non_admin_message(message)
-
-
-# @dp.message_handler(content_types=ContentType.VIDEO_NOTE)
-# async def delete_video_note_except_admins(message: types.Message):
-# await delete_non_admin_message(message)
-
-
-@dp.message_handler(content_types=ContentType.ANY)
-async def check_bot_usage(message: types.Message):
- chat_id = message.chat.id
- bot_id = message.bot.id
- user_id = message.from_id
- username = message.from_user.username
- full_name = message.from_user.full_name
- message_id = message.message_id
-
- if username is None:
- username = ' '
-
- if full_name is None:
- full_name = ' '
-
- store_users_data.connect_to_db(user_id, username, full_name, chat_id)
diff --git a/bot/bot_main/commands_and_keywords/main_commands.py b/bot/bot_main/commands_and_keywords/main_commands.py
deleted file mode 100644
index 82ce6d1..0000000
--- a/bot/bot_main/commands_and_keywords/main_commands.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from aiogram import types
-
-from bot.bot_main.main_objects_initialization import dp
-from config import EN_COMMAND_DESC_LIST
-
-
-@dp.message_handler(state='*', commands=['start'])
-async def start_command(message: types.Message):
- await message.reply('I am activated. Type your commands or keywords.')
-
-
-@dp.message_handler(state='*', commands=['help'])
-async def help_command(message: types.Message):
- await message.reply(text=EN_COMMAND_DESC_LIST)
diff --git a/bot/bot_main/commands_and_keywords/password_generator_command.py b/bot/bot_main/commands_and_keywords/password_generator_command.py
deleted file mode 100644
index a73856b..0000000
--- a/bot/bot_main/commands_and_keywords/password_generator_command.py
+++ /dev/null
@@ -1,539 +0,0 @@
-import asyncio
-
-import aiogram.utils.exceptions
-import mysql.connector
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-from aiogram.types import CallbackQuery
-
-from bot.bot_main.bot_classes.DuplicateDescriptionError import DuplicateDescriptionError
-from bot.bot_main.bot_classes.PasswordGeneratorStates import PasswordGeneratorStates
-from bot.bot_main.for_password_generation.generate_password import main_generation
-# from bot.bot_main.for_password_generation import password_content_callbacks
-# from bot.bot_main.for_password_generation import password_length_callbacks
-from bot.bot_main.for_password_generation.password_content_callbacks import default_content_keyboard
-from bot.bot_main.for_password_generation.password_length_callbacks import default_length_keyboard
-from bot.bot_main.main_objects_initialization import dp, bot, unique_table, store_users_data
-from bot.keyboards.password_generator.back_keyboard import back_to_telegram_generator_keyboard
-from bot.keyboards.password_generator.download_app_keyboard import download_app_keyboard
-from bot.keyboards.password_generator.generation_keyboard import third_generator_keyboard
-from bot.keyboards.password_generator.radio_keyboard import first_generator_keyboard
-from bot.keyboards.password_generator.set_length_keyboard import second_generator_keyboard
-from bot.keyboards.password_generator.main_keyboard import password_start_keyboard
-from bot.keyboards.password_generator.return_to_update_keyboard import password_generator_return_to_update
-from bot.keyboards.password_generator.telegram_keyboard import password_telegram_keyboard
-from bot.keyboards.password_generator.update_keyboard import password_generator_update_keyboard
-from bot.other_functions import check_for_comma_and_dot, check_for_id_in_table
-from bot.other_functions.check_for_repetetive_characters import check_if_repeatable_characters_is_present
-from bot.other_functions.check_length_keyboard import keyboard_length_choice
-from bot.other_functions.check_radio_keyboard import keyboard_content_choice
-from bot.other_functions.close_keyboard import close_keyboard
-from bot.other_functions.encryption_decryption import encrypt, decrypt
-from bot.other_functions.work_with_json import send_json, remove_json
-from bot.other_functions.message_delete_exception import message_delete_control
-
-DELETE_TIMEOUT = 60
-
-@dp.message_handler(state='*', commands=['password'])
-async def password(message: types.Message):
- chat_id = message.chat.id
- user_id = message.from_id
- username = message.from_user.username
- full_name = message.from_user.full_name
- store_users_data.connect_to_db(user_id, username, full_name, chat_id)
-
- await message.reply(
- 'Welcome to password generator 🔒\n\n'
- ''
- 'As you can see below, there are two buttons. By clicking on the first button, '
- 'you will go to the password generator embedded in me. After clicking the second button, '
- 'you will be presented with options to download the application to your local machine.'
- '',
- reply_markup=password_start_keyboard,
- parse_mode='HTML'
- )
-
-
-@dp.callback_query_handler(text=['telegram_password'])
-async def telegram_password(call: CallbackQuery):
- await call.message.edit_text(
- 'Choose menu option:\n\n',
- reply_markup=password_telegram_keyboard,
- parse_mode='HTML'
- )
-
-
-@dp.callback_query_handler(text=['show_password'])
-async def option_show_passwords(call: types.CallbackQuery):
- select_result = unique_table.select_pass_gen_table(f'pass_gen_table_{call.from_user.id}')
-
- if not select_result:
- await call.message.edit_text('Table has no records', reply_markup=back_to_telegram_generator_keyboard)
- else:
- all_passwords = ''.join(
- f'Password №{password_number}\n'
- f'ID: {password_data[0]}\n'
- f'Description: {password_data[1]}\n'
- f'Password: {decrypt(password_data[2])}\n'
- f'Length: {password_data[3]}\n'
- f'Has repetetive?: {password_data[4]}\n\n'
- for password_number, password_data in enumerate(select_result, 1)
- )
-
- try:
- await call.message.edit_text(
- text=all_passwords,
- reply_markup=back_to_telegram_generator_keyboard,
- parse_mode='HTML'
- )
-
- await call.answer()
- except aiogram.utils.exceptions.BadRequest:
- await call.message.edit_text(
- text='Your passwords data is too big. I will send you a JSON file.',
- reply_markup=back_to_telegram_generator_keyboard,
- parse_mode='HTML'
- )
-
- sent_message = await send_json(call)
-
- await call.answer()
-
- await asyncio.sleep(DELETE_TIMEOUT)
- await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
-
-
-@dp.callback_query_handler(text=['create_password'])
-async def create_password(call: CallbackQuery):
- message_text = 'Choose what your password can contain:'
-
- await call.message.edit_text(text=message_text, parse_mode='HTML', reply_markup=first_generator_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text=['change_desc_pass'])
-async def change_desc_pass(call: CallbackQuery):
- await call.message.edit_text(
- 'What do you want to change?:\n\n',
- reply_markup=password_generator_update_keyboard,
- parse_mode='HTML'
- )
-
-
-@dp.callback_query_handler(text=['change_description'])
-async def change_password(call: CallbackQuery):
- await PasswordGeneratorStates.update_description.set()
- await call.answer('Enter description information in the format: "ID value", "description text"', True)
- await call.message.delete()
-
-
-@dp.message_handler(state=PasswordGeneratorStates.update_description)
-async def process_description_update(message: types.Message, state: FSMContext):
- try:
- user_data = check_for_comma_and_dot.check_user_data(message.text)
- user_chosen_id = check_for_comma_and_dot.check_user_id(message.text)
-
- if int(user_chosen_id) not in check_for_id_in_table.check_for_password_id(message):
- raise ValueError
-
-
- unique_table.update_password_desc(
- f'pass_gen_table_{message.from_user.id}', user_data, int(user_chosen_id)
- )
-
- await message.answer('Data has been changed!', reply_markup=password_generator_return_to_update)
- except ValueError:
- await message.answer('Entered data was incorrect!', reply_markup=password_generator_return_to_update)
- except mysql.connector.errors.IntegrityError:
- await message.answer(
- 'Password with such description already exists!', reply_markup=password_generator_return_to_update
- )
-
- await message_delete_control(message)
-
- await state.finish()
-
-@dp.callback_query_handler(text=['change_password'])
-async def change_password(call: CallbackQuery):
- await PasswordGeneratorStates.update_password.set()
- await call.answer('Enter password information in the format: "ID value", "password text"', True)
- await call.message.delete()
-
-
-@dp.message_handler(state=PasswordGeneratorStates.update_password)
-async def process_password_update(message: types.Message, state: FSMContext):
- try:
- user_data = check_for_comma_and_dot.check_user_data(message.text)
- user_data_length = len(user_data)
- repetetive_characters_in_password = check_if_repeatable_characters_is_present(user_data)
- user_chosen_id = check_for_comma_and_dot.check_user_id(message.text)
-
- if int(user_chosen_id) not in check_for_id_in_table.check_for_password_id(message):
- raise ValueError
-
- encryped_password = encrypt(user_data)
- unique_table.update_password(
- f'pass_gen_table_{message.from_user.id}',
- encryped_password,
- user_data_length,
- repetetive_characters_in_password,
- int(user_chosen_id)
- )
-
- await message.answer('Data has been changed!', reply_markup=password_generator_return_to_update)
- except ValueError:
- await message.answer('Entered data was incorrect!', reply_markup=password_generator_return_to_update)
- except mysql.connector.errors.IntegrityError:
- await message.answer(
- 'Password with such description already exists!', reply_markup=password_generator_return_to_update
- )
-
- await message_delete_control(message)
-
- await state.finish()
-
-@dp.callback_query_handler(text=['delete_password'])
-async def delete_password(call: CallbackQuery):
- await PasswordGeneratorStates.delete.set()
- await call.answer('Enter password ID and all data about that password will be removed.', True)
- await call.message.delete()
-
-
-@dp.message_handler(state=PasswordGeneratorStates.delete)
-async def process_password_update(message: types.Message, state: FSMContext):
- try:
- if int(message.text) not in check_for_id_in_table.check_for_password_id(message):
- raise ValueError
-
- unique_table.delete_from_password_table(f'pass_gen_table_{message.from_user.id}', message.text)
- await message.answer('Data deleted successfully!', reply_markup=back_to_telegram_generator_keyboard)
- except ValueError:
- await message.answer('Password ID was entered incorrectly!', reply_markup=back_to_telegram_generator_keyboard)
-
- await message_delete_control(message)
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['password_app'])
-async def password_app(call: CallbackQuery):
- await call.message.edit_text(
- 'Choose your system: ', parse_mode='HTML', reply_markup=download_app_keyboard
- )
- await call.answer()
-
-
-@dp.callback_query_handler(text=['windows_installer'])
-async def windows_installer(call: CallbackQuery):
- with open('password_generator_app/Password_Generator_Windows_Setup.zip', 'rb') as zip_archive:
- zip_content = zip_archive.read()
-
- sent_message = await call.bot.send_document(
- chat_id=call.message.chat.id,
- document=('Password_Generator_Windows_Setup.zip', zip_content),
- caption='Password for archive: 123454321',
- parse_mode = 'HTML'
- )
-
- await call.answer()
-
- await asyncio.sleep(DELETE_TIMEOUT)
- await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
-
-
-@dp.callback_query_handler(text=['linux_macos'])
-async def linux_macos(call: CallbackQuery):
- with open('password_generator_app/Password_Generator_Linux_MacOS.zip', 'rb') as zip_archive:
- zip_content = zip_archive.read()
-
- sent_message = await call.bot.send_document(
- chat_id=call.message.chat.id,
- document=('Password_Generator_Linux_MacOS.zip', zip_content),
- caption='Password for archive: 123454321',
- parse_mode='HTML'
- )
-
- await call.answer()
-
- await asyncio.sleep(DELETE_TIMEOUT)
- await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
-
-
-@dp.callback_query_handler(text=['json_password'])
-async def json_password(call: CallbackQuery):
- await call.message.edit_text(
- 'You can see your JSON file below.',
- parse_mode='HTML', reply_markup=back_to_telegram_generator_keyboard
- )
-
- sent_message = await send_json(call)
- remove_json(call)
-
- await call.answer()
-
- await asyncio.sleep(DELETE_TIMEOUT)
- await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
-
-
-@dp.callback_query_handler(text=['generate_password'])
-async def generate_password(call: types.CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- data = await storage.get_data()
- necessary_keys = ['bot_message_id', 'password_contains', 'password_length']
- user_alphabet = data.get('password_contains')
- user_length = data.get('password_length')
-
- if all(key in data for key in necessary_keys):
- alphabet = keyboard_content_choice(user_alphabet)
- password_length = keyboard_length_choice(user_length)
- generated_password = main_generation(alphabet, password_length)
- data.update({'generated_pasword': generated_password})
-
- await call.answer(cache_time=2)
-
- await call.message.edit_text(
- text=f'Your password:\n{generated_password}',
- parse_mode='HTML'
- )
- await call.message.edit_reply_markup(
- reply_markup=third_generator_keyboard
- )
- await storage.set_data(data)
- else:
- await call.answer(
- 'You need to press one of the button what your password could '
- 'contain and choose password difficulty first!', True
- )
-
-
-@dp.callback_query_handler(text=['store_in_db'])
-async def option_set_description(call: types.CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- data = await storage.get_data()
- necessary_keys = ['bot_message_id', 'password_contains', 'password_length', 'generated_pasword']
-
- if all(key in data for key in necessary_keys):
- await PasswordGeneratorStates.set_description.set()
- await call.answer('Enter description text...', True)
- else:
- await call.answer(
- 'You need to press one of the button what your password could '
- 'contain, choose password difficulty and generate password first!', True
- )
-
-
-@dp.message_handler(state=PasswordGeneratorStates.set_description)
-async def process_description(message: types.Message, state: FSMContext):
- user_desc = message.text
- storage = dp.current_state(chat=message.chat.id, user=message.from_user.id)
- data = await storage.get_data()
- data.update({'description': f'{user_desc}'})
- await storage.set_data(data)
- message_id = data['bot_message_id']
-
- if 'generated_pasword' in data:
- try:
- if len(user_desc) > 384:
- raise ValueError
-
- table_name = f'pass_gen_table_{message.from_id}'
- user_description = data['description']
- user_password = data['generated_pasword']
- password_length = keyboard_length_choice(data['password_length'])
- has_repetetive = check_if_repeatable_characters_is_present(user_password)
-
- await message.delete()
-
- if user_description in unique_table.select_description(table_name):
- raise DuplicateDescriptionError('Duplicate description in user table.')
- else:
- encryped_password = encrypt(user_password)
- unique_table.insert_password_data(
- table_name,
- user_description,
- encryped_password,
- password_length,
- has_repetetive
- )
-
- await message.from_user.bot.edit_message_text(
- text=f'Password saved successfuly!!!\n\n'
- f'Your description:\n{data["description"]}\n'
- f'Your password:\n{data["generated_pasword"]}',
- chat_id=message.chat.id,
- message_id=message_id,
- parse_mode='HTML'
- )
- await message.from_user.bot.edit_message_reply_markup(
- chat_id=message.chat.id,
- message_id=message_id,
- reply_markup=third_generator_keyboard
- )
- except ValueError:
- await message.from_user.bot.edit_message_text(
- text='Invalid input!\nToo many characters entered.',
- chat_id=message.chat.id,
- message_id=message_id,
- )
- await message.from_user.bot.edit_message_reply_markup(
- chat_id=message.chat.id,
- message_id=message_id,
- reply_markup=third_generator_keyboard
- )
- except DuplicateDescriptionError:
- await message.from_user.bot.edit_message_text(
- text='Invalid input!\nDescription already in the table!!!',
- chat_id=message.chat.id,
- message_id=message_id,
- )
- await message.from_user.bot.edit_message_reply_markup(
- chat_id=message.chat.id,
- message_id=message_id,
- reply_markup=third_generator_keyboard
- )
-
- await state.finish()
- await storage.set_data(data)
-
-
-@dp.callback_query_handler(text=['next_to_second'])
-async def next_to_second(call: CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- data = await storage.get_data()
- necessary_keys = ['bot_message_id', 'password_contains']
-
- if 'password_length' not in data:
- if all(key in data for key in necessary_keys):
- await call.message.edit_text(
- 'Choose the password difficulty:\n\n',
- reply_markup=second_generator_keyboard,
- parse_mode='HTML'
- )
- else:
- await call.message.edit_text(text='Choose what your password can contain:', parse_mode='HTML')
- await call.message.edit_reply_markup(reply_markup=first_generator_keyboard)
- await call.answer('You should select any of these before continue!', True)
- else:
- await call.message.edit_text(
- 'Choose the password difficulty:\n\n',
- parse_mode='HTML'
- )
- await call.message.edit_reply_markup(reply_markup=second_generator_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text=['next_to_third'])
-async def next_to_third(call: CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- data = await storage.get_data()
- necessary_keys = ['bot_message_id', 'password_contains', 'password_length']
-
- if 'generated_pasword' in data:
- if data['description']:
- await call.message.edit_text(
- text = f'Password saved successfuly!!!\n\n'
- f'Your description:\n{data["description"]}\n'
- f'Your password:\n{data["generated_pasword"]}',
- reply_markup = third_generator_keyboard,
- parse_mode = 'HTML'
- )
- else:
- await call.message.edit_text(
- text=f'Your password:\n{data["generated_pasword"]}',
- reply_markup=third_generator_keyboard,
- parse_mode='HTML'
- )
- else:
- if all(key in data for key in necessary_keys):
- await call.message.edit_text(
- 'Generate your password and write it to the database! \n\n',
- reply_markup=third_generator_keyboard,
- parse_mode='HTML'
- )
- else:
- await call.message.edit_text(text='Choose the password difficulty:', parse_mode='HTML')
- await call.message.edit_reply_markup(reply_markup=second_generator_keyboard)
- await call.answer('You need to choose what will contain your password and choose its difficulty!', True)
-
-
-@dp.callback_query_handler(text=['main_generator_menu'])
-async def main_generator_menu(call: CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- await storage.finish()
-
- default_content_keyboard()
- default_length_keyboard()
-
- await call.message.edit_text(
- 'Choose menu option: \n\n',
- reply_markup=password_telegram_keyboard,
- parse_mode='HTML'
- )
-
-
-@dp.callback_query_handler(text=['back_to_first'])
-async def back_to_first(call: CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- data = await storage.get_data()
-
- message_text = 'Choose what your password can contain:'
-
- await call.message.edit_text(text=message_text, parse_mode='HTML', reply_markup=first_generator_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text=['back_to_second'])
-async def back_to_second(call: CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- data = await storage.get_data()
- user_choice = data.get('password_length')
-
- message_text = 'Choose the password difficulty:\n\n'
-
- await call.message.edit_text(text=message_text, parse_mode='HTML')
- await call.message.edit_reply_markup(reply_markup=second_generator_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text=['return_to_update_menu'])
-async def return_to_update_menu(call: CallbackQuery):
- await call.message.edit_text(
- 'What do you want to change?:',
- parse_mode='HTML', reply_markup=password_generator_update_keyboard
- )
- await call.answer()
-
-
-@dp.callback_query_handler(text=['back_to_telegram_generator'])
-async def back_to_telegram_generator(call: CallbackQuery):
- storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
- await storage.finish()
-
- default_content_keyboard()
- default_length_keyboard()
-
- await call.message.edit_text(
- 'Choose menu option: ',
- parse_mode='HTML', reply_markup=password_telegram_keyboard
- )
- await call.answer()
-
-
-@dp.callback_query_handler(text=['back_to_main_menu'])
-async def back_to_main_menu(call: CallbackQuery):
- await call.message.edit_text(
- 'Welcome to password generator 🔒\n\n'
- ''
- 'As you can see below, there are two buttons. By clicking on the first button, '
- 'you will go to the password generator embedded in me. After clicking the second button, '
- 'you will be presented with options to download the application to your local machine.'
- '',
- parse_mode='HTML',
- reply_markup=password_start_keyboard
- )
- await call.answer()
-
-
-@dp.callback_query_handler(text=['close_app'])
-async def close_app(call: CallbackQuery):
- await close_keyboard(call)
diff --git a/bot/bot_main/commands_and_keywords/random_numbers_generation_command.py b/bot/bot_main/commands_and_keywords/random_numbers_generation_command.py
deleted file mode 100644
index e4d3850..0000000
--- a/bot/bot_main/commands_and_keywords/random_numbers_generation_command.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from random import randint
-
-from aiogram import types
-
-from bot.bot_main.main_objects_initialization import dp
-from bot.keyboards.random_number import random_numbers_keyboard
-from bot.other_functions.close_keyboard import close_keyboard
-
-
-@dp.message_handler(state='*', commands=['random'])
-async def randomize(message: types.Message):
- await message.reply('Select a number range: ', reply_markup=random_numbers_keyboard.random_number_keyboard)
-
-
-@dp.callback_query_handler(text=['random_value_1', 'random_value_2', 'random_value_3', 'random_value_4'])
-async def choose_random(call: types.CallbackQuery):
- if call.data == 'random_value_1':
- await call.answer(f'Generated random number: {randint(0, 10)} 🎲', True)
- elif call.data == 'random_value_2':
- await call.answer(f'Generated random number: {randint(0, 100)} 🎲', True)
- elif call.data == 'random_value_3':
- await call.answer(f'Generated random number: {randint(-100, 100)} 🎲', True)
- elif call.data == 'random_value_4':
- await call.answer(f'Generated random number: \n{randint(-1000000, 1000000)} 🎲', True)
-
- await call.answer()
-
-
-@dp.callback_query_handler(text=['close_random'])
-async def close_random(call: types.CallbackQuery):
- await close_keyboard(call)
diff --git a/bot/bot_main/commands_and_keywords/sticker_command.py b/bot/bot_main/commands_and_keywords/sticker_command.py
deleted file mode 100644
index 8adeab4..0000000
--- a/bot/bot_main/commands_and_keywords/sticker_command.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from aiogram import types
-
-from bot.bot_main import main_objects_initialization
-from bot.other_functions.extract_random_data import get_random_data
-from bot.bot_main.main_objects_initialization import dp, bot
-
-
-@dp.message_handler(state='*', commands=['sticker'])
-async def choose_sticker(message: types.Message):
- data = main_objects_initialization.sticker_table.get_sticker()
- await bot.send_sticker(message.chat.id, sticker=f'{get_random_data(data)}')
- await message.delete()
diff --git a/bot/bot_main/commands_and_keywords/task_scheduler_command.py b/bot/bot_main/commands_and_keywords/task_scheduler_command.py
deleted file mode 100644
index 9ca9bc6..0000000
--- a/bot/bot_main/commands_and_keywords/task_scheduler_command.py
+++ /dev/null
@@ -1,124 +0,0 @@
-import mysql.connector
-
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-
-from bot.bot_main import main_objects_initialization
-from bot.bot_main.bot_classes.TaskScheduler import TaskScheduler
-from bot.bot_main.main_objects_initialization import dp
-from bot.keyboards.task_scheduler import back_to_planner_keyboard
-from bot.keyboards.task_scheduler.scheduler_keyboard import scheduler_keyboard
-from bot.other_functions import check_for_comma_and_dot, check_for_id_in_table
-from bot.other_functions.close_keyboard import close_keyboard
-from bot.other_functions.message_delete_exception import message_delete_control
-
-
-@dp.message_handler(state='*', commands=['taskscheduler'])
-async def scheduler_call(message: types.Message):
- chat_id = message.chat.id
- user_id = message.from_id
- username = message.from_user.username
- full_name = message.from_user.full_name
- main_objects_initialization.store_users_data.connect_to_db(user_id, username, full_name, chat_id)
-
- await message.reply('Choose a task scheduler function:', reply_markup=scheduler_keyboard)
-
-
-@dp.callback_query_handler(text=['select'])
-async def option_select(call: types.CallbackQuery):
- select_result = main_objects_initialization.unique_table.select_table('table_' + str(call.from_user.id))
- if not select_result:
- await call.message.edit_text('Table has no records', reply_markup=back_to_planner_keyboard.return_keyboard)
- else:
- all_tasks = ''.join(
- f'ID{task[0]} | Task №{task_number} | {task[1]}\n'
- for task_number, task in enumerate(select_result, 1)
- )
-
- await call.message.edit_text(all_tasks, reply_markup=back_to_planner_keyboard.return_keyboard)
-
-
-@dp.callback_query_handler(text=['insert'])
-async def option_insert(call: types.CallbackQuery):
- await TaskScheduler.insert.set()
- await call.answer('Enter the task text...', True)
- await call.message.delete()
-
-@dp.message_handler(state=TaskScheduler.insert)
-async def process_insert(message: types.Message, state: FSMContext):
- try:
- if len(message.text) > 768:
- raise ValueError
-
- main_objects_initialization.unique_table.insert_into_table(('table_' + str(message.from_user.id)), message.text)
- await message.answer('Data added successfully!', reply_markup=back_to_planner_keyboard.return_keyboard)
- except ValueError:
- await message.answer('Too many characters entered!', reply_markup=back_to_planner_keyboard.return_keyboard)
-
- await message_delete_control(message)
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['update'])
-async def option_update(call: types.CallbackQuery):
- await TaskScheduler.update.set()
- await call.answer('Enter information in the format: "ID value", "task text"', True)
- await call.message.delete()
-
-
-@dp.message_handler(state=TaskScheduler.update)
-async def process_update(message: types.Message, state: FSMContext):
- try:
- user_data = check_for_comma_and_dot.check_user_data(message.text)
- user_id = check_for_comma_and_dot.check_user_id(message.text)
-
- if int(user_id) not in check_for_id_in_table.check_for_id(message):
- raise ValueError
-
- main_objects_initialization.unique_table.update_table(f'table_{message.from_user.id}', user_data, int(user_id))
- await message.answer('Data has been changed!', reply_markup=back_to_planner_keyboard.return_keyboard)
- except ValueError:
- await message.answer('Data is entered incorrectly!', reply_markup=back_to_planner_keyboard.return_keyboard)
- except mysql.connector.errors.IntegrityError:
- await message.answer('You have already added this task', reply_markup=back_to_planner_keyboard.return_keyboard)
-
- await message_delete_control(message)
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['delete'])
-async def option_delete(call: types.CallbackQuery):
- await TaskScheduler.delete.set()
- await call.answer('Enter the ID of the task you want to delete...', True)
- await call.message.delete()
-
-
-@dp.message_handler(state=TaskScheduler.delete)
-async def process_delete(message: types.Message, state: FSMContext):
- try:
- if int(message.text) not in check_for_id_in_table.check_for_id(message):
- raise ValueError
-
- main_objects_initialization.unique_table.delete_from_table(('table_' + str(message.from_user.id)), message.text)
- await message.answer('Data deleted successfully!', reply_markup=back_to_planner_keyboard.return_keyboard)
- except ValueError:
- await message.answer('Task ID was entered incorrectly!', reply_markup=back_to_planner_keyboard.return_keyboard)
-
- await message_delete_control(message)
-
- await state.finish()
-
-
-@dp.callback_query_handler(text=['back_to_scheduler'])
-async def back_to_main_menu(call: types.CallbackQuery):
- await call.message.edit_text(
- 'Choose a task scheduler function:', reply_markup=scheduler_keyboard
- )
- await call.answer()
-
-
-@dp.callback_query_handler(text=['close_scheduler'])
-async def close_scheduler(call: types.CallbackQuery):
- await close_keyboard(call)
diff --git a/bot/bot_main/commands_and_keywords/time_commands.py b/bot/bot_main/commands_and_keywords/time_commands.py
deleted file mode 100644
index 4ba5f42..0000000
--- a/bot/bot_main/commands_and_keywords/time_commands.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import time
-from datetime import date, datetime
-
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-
-from bot.bot_main.bot_classes.DaysToBirthday import DaysToBirthday
-from bot.bot_main.main_objects_initialization import dp
-from bot.other_functions.check_date_words import check_year, check_month, check_day
-
-
-@dp.message_handler(state='*', commands=['time'])
-async def search_time(message: types.Message):
- utc_time = datetime.utcnow()
- current_year = utc_time.timetuple().tm_year
- current_month = utc_time.timetuple().tm_mon
- current_day = utc_time.timetuple().tm_mday
-
- delta = date(current_year, current_month, current_day) - date(2022, 2, 23)
- days_of_unity = date(current_year, current_month, current_day) - date(1919, 1, 21)
-
- await message.reply(
- f'Current date:\t{utc_time.strftime("%d.%m.%Y UTC")} 📅\n'
- f'Current time:\t{utc_time.strftime("%H:%M:%S UTC")} 🕔\n'
- f'Day of the week:\t{(utc_time.strftime("%A")).upper()}\n'
- f'Day of the year:\t{utc_time.timetuple().tm_yday} 🌞\n'
- f'Number of days after russian full scale invasion to Ukraine:\t{delta.days} 🕊\n'
- f'Day of Unity of Ukraine:\t{days_of_unity.days} 🤝'
- )
-
- if current_month == 1 and current_day == 22:
- anniversary = current_year - 1919
- await message.answer(f'WOW, {anniversary}TH ANNIVERSARY OF THE UNITY OF UKRAINE')
-
-
-@dp.message_handler(state='*', commands=['birthday'])
-async def get_birthday(message: types.Message):
- await DaysToBirthday.day_month.set()
- await message.reply('Enter date of birth (or other date) in format dd.mm.yyyy...')
-
-
-@dp.message_handler(state=DaysToBirthday.day_month)
-async def process_birthday(message: types.Message, state: FSMContext):
- input_data = message.text
- input_data = input_data.split('.', 2)
- current_year = time.localtime().tm_year
- current_month = time.localtime().tm_mon
- current_day = time.localtime().tm_mday
-
- try:
- answer = abs(
- date(current_year, current_month, current_day) -
- date(int(input_data[2]), int(input_data[1]), int(input_data[0]))
- )
-
- years = int(answer.days / 365)
- months = int((answer.days % 365) / 31)
- days = answer.days - years * 365 - months * 31
- await message.reply(f'The number of days between the entered date and the current date: {answer.days}\n'
- f'Formatted date: '
- f'{years} {check_year(years)}, {months} {check_month(months)}, {days} {check_day(days)}')
- except ValueError:
- await message.reply('Your input was incorrect! Try again.')
- except IndexError:
- await message.reply('Your input was incorrect! Try again.')
-
- await state.finish()
diff --git a/bot/bot_main/commands_and_keywords/user_info_command.py b/bot/bot_main/commands_and_keywords/user_info_command.py
deleted file mode 100644
index f747b7f..0000000
--- a/bot/bot_main/commands_and_keywords/user_info_command.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import asyncio
-
-from aiogram import types
-
-from bot.bot_main.for_password_generation.generate_password import generate_token
-from bot.bot_main.main_objects_initialization import dp, store_users_data, token_table, bot, unique_table
-from bot.keyboards.token.token_keyboard import token_keyboard
-from bot.other_functions.close_keyboard import close_keyboard
-from bot.other_functions.encryption_decryption import encrypt, decrypt
-
-DELETE_TIMEOUT = 30
-
-@dp.message_handler(state='*', commands=['token'])
-async def user_data(message: types.Message):
- get_username = message.from_user.username
- user_id = message.from_user.id
- full_name = message.from_user.full_name
- chat_id = message.chat.id
-
- if chat_id == user_id:
- if unique_table.check_tables_to_allow_token(user_id):
- store_users_data.connect_to_db(user_id, get_username, full_name, chat_id)
-
- all_tokens = token_table.select_all_tokens()
- user_token = token_table.select_token(user_id)
-
- get_username = check_for_username(get_username)
-
- if user_token in all_tokens:
- await message.reply(
- f'Username: {get_username}\n'
- f'Telegram ID: {user_id}\n\n'
- f'Desktop application token:\n{decrypt(user_token)}',
- parse_mode='HTML',
- reply_markup=token_keyboard
- )
- else:
- await message.reply(
- f'Username: {get_username}\n'
- f'Telegram ID: {user_id}\n\n',
- parse_mode='HTML',
- reply_markup=token_keyboard
- )
- else:
- bot_message = await message.reply(
- 'First, you need to use the password generator (/password) '
- 'and create at least one password or click one of the buttons:\n'
- '"Show all passwords", "Change description", "Change password", "Delete password"!\n'
- 'Only then can you use this command.'
- )
- await asyncio.sleep(DELETE_TIMEOUT)
- await bot.delete_message(chat_id=chat_id, message_id=bot_message.message_id)
- else:
- bot_message = await message.reply('This command can be used only in private conversation with bot!')
- await asyncio.sleep(DELETE_TIMEOUT)
- await bot.delete_message(chat_id=chat_id, message_id=bot_message.message_id)
-
-
-@dp.callback_query_handler(text='add_token')
-async def add_token(call: types.CallbackQuery):
- random_token = generate_token()
- user_token = encrypt(random_token)
- get_username = call.from_user.username
- user_id = call.from_user.id
- token_table.add_token(user_id, user_token)
-
- get_username = check_for_username(get_username)
-
- await call.message.edit_text(
- f'Username: {get_username}\n'
- f'Telegram ID: {user_id}\n\n'
- f'Desktop application token:\n{random_token}',
- parse_mode='HTML',
- )
-
- await call.message.edit_reply_markup(reply_markup=token_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text='delete_token')
-async def delete_token(call: types.CallbackQuery):
- get_username = call.from_user.username
- user_id = call.from_user.id
-
- all_tokens = token_table.select_all_tokens()
- user_token = token_table.select_token(user_id)
-
- if user_token in all_tokens:
- token_table.remove_token(user_id)
- else:
- await call.answer()
- return
-
- get_username = check_for_username(get_username)
-
- await call.message.edit_text(
- f'Username: {get_username}\n'
- f'Telegram ID: {user_id}',
- parse_mode='HTML',
- )
-
- await call.message.edit_reply_markup(reply_markup=token_keyboard)
- await call.answer()
-
-
-@dp.callback_query_handler(text='close_token')
-async def close_token(call: types.CallbackQuery):
- await close_keyboard(call)
-
-
-def check_for_username(username):
- if username is not None:
- username = f'{username}'
- else:
- username = f'You does not have a username🤷'
-
- return username
-
diff --git a/bot/bot_main/commands_and_keywords/weather_command.py b/bot/bot_main/commands_and_keywords/weather_command.py
deleted file mode 100644
index fbb8b89..0000000
--- a/bot/bot_main/commands_and_keywords/weather_command.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import requests
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-
-from bot.bot_main.bot_classes.WeatherInfo import WeatherInfo
-from bot.bot_main.main_objects_initialization import dp
-from bot.parsing.parse_temprature import find_avarage_temp_between_two, parse_minmax_temp, \
- avarage_day_temp, parse_avarage_precipitation_probability
-
-
-@dp.message_handler(state='*', commands=['weather'])
-async def weather(message: types.Message):
- await WeatherInfo.place.set()
- await message.reply('Enter the name of the town...')
-
-
-@dp.message_handler(state=WeatherInfo.place)
-async def process_weather(message: types.Message, state: FSMContext):
- input_place = message.text
-
- url = f'https://ua.sinoptik.ua/погода-{input_place}'
- answer = requests.get(url)
-
- if answer.status_code == 200:
- temp_list = find_avarage_temp_between_two(url)
- await message.reply(
- f'{parse_minmax_temp(url)}'
- f'{avarage_day_temp(url)}'
- f'Night temrature: {temp_list[0]}\n'
- f'Morning temperature: {temp_list[1]}\n'
- f'Day temperature: {temp_list[2]}\n'
- f'Evening temperature: {temp_list[3]}\n\n'
- f'Maximum chance of precipitation: {parse_avarage_precipitation_probability(url)}%',
- parse_mode='Markdown'
- )
- else:
- await message.reply('Can not find such town name!')
-
- await state.finish()
diff --git a/bot/bot_main/commands_and_keywords/youtube_search_command.py b/bot/bot_main/commands_and_keywords/youtube_search_command.py
deleted file mode 100644
index 9d5104e..0000000
--- a/bot/bot_main/commands_and_keywords/youtube_search_command.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from aiogram import types
-from aiogram.dispatcher import FSMContext
-
-from bot.bot_main.bot_classes.SearchTerm import SearchTerm
-from bot.bot_main.main_objects_initialization import dp
-from bot.parsing import parse_link
-
-
-@dp.message_handler(state='*', commands=['eugene'])
-async def parse_links(message: types.Message):
- await SearchTerm.search_term.set()
- await message.reply('Enter a key words for video search in youtube...')
-
-
-@dp.message_handler(state=SearchTerm.search_term)
-async def process_links(message: types.Message, state: FSMContext):
- input_data = message.text
-
- answer = parse_link.links_split(input_data)
-
- result = ''
- for i in range(0, 5):
- result += f'{answer[i]}\n'
-
- if "{'result': []}" not in result:
- await message.reply(f'{result}')
- else:
- await message.answer('The link could not be found for this request.')
-
- await state.finish()
diff --git a/bot/bot_main/for_password_generation/generate_password.py b/bot/bot_main/for_password_generation/generate_password.py
deleted file mode 100644
index 7bdfb9c..0000000
--- a/bot/bot_main/for_password_generation/generate_password.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from random import choices
-from string import digits, ascii_letters, punctuation
-
-
-def exclude_invalid_symblols_for_markup() -> str:
- excluded_symbols = '<>&"'
- allowed_symbols = ''.join([char for char in punctuation if char not in excluded_symbols])
-
- return allowed_symbols
-
-
-def main_generation(password_alphabet: str, password_length: int) -> str:
- return ''.join(choices(password_alphabet, k=password_length))
-
-
-def generate_token() -> str:
- token_alphabet = ascii_letters + digits + exclude_invalid_symblols_for_markup()
-
- return ''.join(choices(token_alphabet, k=50))
diff --git a/bot/bot_main/for_password_generation/password_content_callbacks.py b/bot/bot_main/for_password_generation/password_content_callbacks.py
deleted file mode 100644
index 3bfe1eb..0000000
--- a/bot/bot_main/for_password_generation/password_content_callbacks.py
+++ /dev/null
@@ -1,143 +0,0 @@
-from aiogram import types
-
-from bot.bot_main.main_objects_initialization import dp
-from bot.keyboards.password_generator.radio_keyboard import first_generator_keyboard, all_button, letters_button, \
- digits_button, let_dig_button, let_sig_button, dig_sig_button
-from bot.other_functions.change_state_mem_storage import update_state
-
-
-def default_content_keyboard():
- all_button.text = 'All characters'
- letters_button.text = 'Only letters'
- digits_button.text = 'Only digits'
- let_dig_button.text = 'Letters & digits'
- let_sig_button.text = 'Letters & signs'
- dig_sig_button.text = 'Digits & signs'
-
-
-@dp.callback_query_handler(text=['all_characters'])
-async def option_all_characters(call: types.CallbackQuery):
- result = await update_state(call)
-
- await call.answer(
- 'Now your password can contain all posible characters!',
- True
- )
-
- if all_button.text == 'All characters ✅':
- pass
- else:
- all_button.text = 'All characters ✅'
- letters_button.text = 'Only letters'
- digits_button.text = 'Only digits'
- let_dig_button.text = 'Letters & digits'
- let_sig_button.text = 'Letters & signs'
- dig_sig_button.text = 'Digits & signs'
-
- await call.message.edit_reply_markup(first_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['only_letters'])
-async def option_only_letters(call: types.CallbackQuery):
- result = await update_state(call)
- await call.answer(
- 'Now your password can contain only small and big english letters!',
- True
- )
-
- if letters_button.text == 'Only letters ✅':
- pass
- else:
- all_button.text = 'All characters'
- letters_button.text = 'Only letters ✅'
- digits_button.text = 'Only digits'
- let_dig_button.text = 'Letters & digits'
- let_sig_button.text = 'Letters & signs'
- dig_sig_button.text = 'Digits & signs'
-
- await call.message.edit_reply_markup(first_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['only_digits'])
-async def option_only_digits(call: types.CallbackQuery):
- result = await update_state(call)
- await call.answer(
- 'Now your password can contain only digits!',
- True
- )
-
- if digits_button.text == 'Only digits ✅':
- pass
- else:
- all_button.text = 'All characters'
- letters_button.text = 'Only letters'
- digits_button.text = 'Only digits ✅'
- let_dig_button.text = 'Letters & digits'
- let_sig_button.text = 'Letters & signs'
- dig_sig_button.text = 'Digits & signs'
-
- await call.message.edit_reply_markup(first_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['letters_digits'])
-async def option_letters_digits(call: types.CallbackQuery):
- result = await update_state(call)
- await call.answer(
- 'Now your password can contain english letters and digits!',
- True
- )
-
- if let_dig_button.text == 'Letters & digits ✅':
- pass
- else:
- all_button.text = 'All characters'
- letters_button.text = 'Only letters'
- digits_button.text = 'Only digits'
- let_dig_button.text = 'Letters & digits ✅'
- let_sig_button.text = 'Letters & signs'
- dig_sig_button.text = 'Digits & signs'
-
- await call.message.edit_reply_markup(first_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['letters_signs'])
-async def option_letters_signs(call: types.CallbackQuery):
- result = await update_state(call)
- await call.answer(
- 'Now your password can contain all letters and signs!',
- True
- )
-
- if let_sig_button.text == 'Letters & signs ✅':
- pass
- else:
- all_button.text = 'All characters'
- letters_button.text = 'Only letters'
- digits_button.text = 'Only digits'
- let_dig_button.text = 'Letters & digits'
- let_sig_button.text = 'Letters & signs ✅'
- dig_sig_button.text = 'Digits & signs'
-
- await call.message.edit_reply_markup(first_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['digits_signs'])
-async def option_digits_signs(call: types.CallbackQuery):
- result = await update_state(call)
-
- await call.answer(
- 'Now your password can contain digits and signs!',
- True
- )
-
- if dig_sig_button.text == 'Digits & signs ✅':
- pass
- else:
- all_button.text = 'All characters'
- letters_button.text = 'Only letters'
- digits_button.text = 'Only digits'
- let_dig_button.text = 'Letters & digits'
- let_sig_button.text = 'Letters & signs'
- dig_sig_button.text = 'Digits & signs ✅'
-
- await call.message.edit_reply_markup(first_generator_keyboard)
diff --git a/bot/bot_main/for_password_generation/password_length_callbacks.py b/bot/bot_main/for_password_generation/password_length_callbacks.py
deleted file mode 100644
index fd4f0ad..0000000
--- a/bot/bot_main/for_password_generation/password_length_callbacks.py
+++ /dev/null
@@ -1,165 +0,0 @@
-from aiogram import types
-
-from bot.bot_main.main_objects_initialization import dp
-from bot.keyboards.password_generator.set_length_keyboard import very_easy_button, easy_button, normal_button, \
- hard_button, very_hard_button, unbreakable_button, second_generator_keyboard
-from bot.other_functions.change_state_mem_storage import update_with_length_state
-
-
-def default_length_keyboard():
- very_easy_button.text = 'Very easy'
- easy_button.text = 'Easy'
- normal_button.text = 'Normal'
- hard_button.text = 'Hard'
- very_hard_button.text = 'Very hard'
- unbreakable_button.text = 'Unbreakable'
-
-
-@dp.callback_query_handler(text=['very_easy'])
-async def option_very_easy(call: types.CallbackQuery):
- result = await update_with_length_state(call)
-
- if result is None:
- pass
- else:
- await call.answer(
- 'Length of your password will be equal to 10 characters!',
- True
- )
-
- if very_easy_button.text == 'Very easy ✅':
- pass
- else:
- very_easy_button.text = 'Very easy ✅'
- easy_button.text = 'Easy'
- normal_button.text = 'Normal'
- hard_button.text = 'Hard'
- very_hard_button.text = 'Very hard'
- unbreakable_button.text = 'Unbreakable'
-
- await call.message.edit_reply_markup(second_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['easy'])
-async def option_easy(call: types.CallbackQuery):
- result = await update_with_length_state(call)
-
- if result is None:
- pass
- else:
- await call.answer(
- 'Length of your password will be equal to 20 characters!',
- True
- )
-
- if easy_button.text == 'Easy ✅':
- pass
- else:
- very_easy_button.text = 'Very easy'
- easy_button.text = 'Easy ✅'
- normal_button.text = 'Normal'
- hard_button.text = 'Hard'
- very_hard_button.text = 'Very hard'
- unbreakable_button.text = 'Unbreakable'
-
- await call.message.edit_reply_markup(second_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['normal'])
-async def option_normal(call: types.CallbackQuery):
- result = await update_with_length_state(call)
-
- if result is None:
- pass
- else:
- await call.answer(
- 'Length of your password will be equal to 30 characters!',
- True
- )
-
- if normal_button.text == 'Normal ✅':
- pass
- else:
- very_easy_button.text = 'Very easy'
- easy_button.text = 'Easy'
- normal_button.text = 'Normal ✅'
- hard_button.text = 'Hard'
- very_hard_button.text = 'Very hard'
- unbreakable_button.text = 'Unbreakable'
-
- await call.message.edit_reply_markup(second_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['hard'])
-async def option_hard(call: types.CallbackQuery):
- result = await update_with_length_state(call)
-
- if result is None:
- pass
- else:
- await call.answer(
- 'Length of your password will be equal to 40 characters!',
- True
- )
-
- if hard_button.text == 'Hard ✅':
- pass
- else:
- very_easy_button.text = 'Very easy'
- easy_button.text = 'Easy'
- normal_button.text = 'Normal'
- hard_button.text = 'Hard ✅'
- very_hard_button.text = 'Very hard'
- unbreakable_button.text = 'Unbreakable'
-
- await call.message.edit_reply_markup(second_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['very_hard'])
-async def option_very_hard(call: types.CallbackQuery):
- result = await update_with_length_state(call)
-
- if result is None:
- pass
- else:
- await call.answer(
- 'Length of your password will be equal to 50 characters!',
- True
- )
-
- if very_hard_button.text == 'Very hard ✅':
- pass
- else:
- very_easy_button.text = 'Very easy'
- easy_button.text = 'Easy'
- normal_button.text = 'Normal'
- hard_button.text = 'Hard'
- very_hard_button.text = 'Very hard ✅'
- unbreakable_button.text = 'Unbreakable'
-
- await call.message.edit_reply_markup(second_generator_keyboard)
-
-
-@dp.callback_query_handler(text=['unbreakable'])
-async def option_unbreakable(call: types.CallbackQuery):
- result = await update_with_length_state(call)
-
- if result is None:
- pass
- else:
- await call.answer(
- 'Length of your password will be equal to 100 characters!',
- True
- )
-
- if unbreakable_button.text == 'Unbreakable ✅':
- pass
- else:
- very_easy_button.text = 'Very easy'
- easy_button.text = 'Easy'
- normal_button.text = 'Normal'
- hard_button.text = 'Hard'
- very_hard_button.text = 'Very hard'
- unbreakable_button.text = 'Unbreakable ✅'
-
- await call.message.edit_reply_markup(second_generator_keyboard)
diff --git a/bot/bot_main/for_photo_creation/photo_size.py b/bot/bot_main/for_photo_creation/photo_size.py
deleted file mode 100644
index f771f39..0000000
--- a/bot/bot_main/for_photo_creation/photo_size.py
+++ /dev/null
@@ -1,23 +0,0 @@
-def get_ratio(width, height) -> str:
- if width > height:
- return f'{round(width / height, 2)} : 1'
- elif width < height:
- return f'1 : {round(height / width, 2)}'
- else:
- return '1 : 1'
-
-def write_to_txt(width, height):
- sides_ratio = get_ratio(width, height)
- with open('bot/bot_main/for_photo_creation/photo_size.txt', 'w+') as size_txt:
- size_txt.write(
- f'Width: {width}\n'
- f'Height: {height}\n'
- f'Approximate sides ratio: {sides_ratio}\n'
- )
-
-
-def get_data_from_txt() -> str:
- with open('bot/bot_main/for_photo_creation/photo_size.txt', 'r') as size_txt:
- result = size_txt.read()
-
- return result
diff --git a/bot/bot_main/main_objects_initialization.py b/bot/bot_main/main_objects_initialization.py
deleted file mode 100644
index 44e4ac8..0000000
--- a/bot/bot_main/main_objects_initialization.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from aiogram import Bot, Dispatcher
-from aiogram.contrib.fsm_storage.memory import MemoryStorage
-
-from bot.bot_main.bot_classes.StatisticsTable import StatisticsTable
-from bot.bot_main.bot_classes.UniqueTablesForUsers import UniqueTablesForUsers
-from bot.bot_main.bot_classes.UserToken import UserToken
-from bot.bot_main.bot_classes.UsersDataStore import UsersDataStore
-from bot.bot_main.bot_classes.StickerTable import StickerTable
-from config import API_TOKEN
-
-bot = Bot(token=API_TOKEN)
-storage = MemoryStorage()
-dp = Dispatcher(bot, storage=storage)
-
-store_users_data = UsersDataStore()
-statistics_table = StatisticsTable()
-unique_table = UniqueTablesForUsers()
-sticker_table = StickerTable()
-token_table = UserToken()
diff --git a/bot/bot_starter.py b/bot/bot_starter.py
new file mode 100644
index 0000000..b305c33
--- /dev/null
+++ b/bot/bot_starter.py
@@ -0,0 +1,7 @@
+from aiogram.utils import executor
+from .config import dp
+
+
+class MyBot:
+ def start(self) -> None:
+ return executor.start_polling(dp, skip_updates=True)
diff --git a/bot/commands/__init__.py b/bot/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/commands/add_text_to_photo_command.py b/bot/commands/add_text_to_photo_command.py
new file mode 100644
index 0000000..076bd1e
--- /dev/null
+++ b/bot/commands/add_text_to_photo_command.py
@@ -0,0 +1,59 @@
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+from aiogram.types import ContentType, InputFile
+
+from .commands_utils.states import PhotoInscription
+from .commands_utils.for_photo_creation.photo_size import get_data_from_txt
+from .commands_utils.for_photo_creation.validate_args_number import validate_args
+from ..config import dp, bot
+from ..enums import Command
+from bot.commands.commands_utils.delete_with_delay import delete_messages
+
+
+@dp.message_handler(state="*", commands=Command.IMAGE_EDITOR)
+async def get_photo_config(message: types.Message):
+ await PhotoInscription.user_inscription_config.set()
+ bot_message = await message.reply(
+ "Send me an image with description where have to "
+ "be a text which i will add to your image, text must be "
+ "in the following format: example//100//200//233//500//20\n\n"
+ "example - this is a text that will be added to image)\n"
+ "100 - X coordinate (only integer allowed)\n"
+ "200 - Y coordinate (only integer allowed\n"
+ "233 - image width (only integer allowed)\n"
+ "500 - image height (only integer allowed)\n"
+ "20 - font size (only integer allowed)",
+ parse_mode="HTML",
+ )
+
+ await delete_messages(message, bot_message)
+
+
+@dp.message_handler(
+ state=PhotoInscription.user_inscription_config, content_types=ContentType.PHOTO
+)
+async def create_photo(message: types.Message, state: FSMContext):
+ photo = message.photo[-1]
+ await photo.download(destination_file="imgs/test.jpg")
+
+ description = message.caption
+
+ if not description:
+ await message.reply("Sorry, that photo doesn’t have a caption. Try again.")
+ else:
+ user_input_lst = description.split("//", 5)
+ formatted_lst = [args.strip() for args in user_input_lst]
+
+ create_photo_using_args = await validate_args(formatted_lst, message)
+
+ if create_photo_using_args:
+ result_photo = InputFile("imgs/result.jpg")
+ txt_data = get_data_from_txt()
+ await bot.send_photo(
+ message.chat.id,
+ photo=result_photo,
+ caption=f"{txt_data}",
+ parse_mode="HTML",
+ )
+
+ await state.finish()
diff --git a/bot/commands/commands_utils/__init__.py b/bot/commands/commands_utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/other_functions/change_state_mem_storage.py b/bot/commands/commands_utils/change_state_mem_storage.py
similarity index 60%
rename from bot/other_functions/change_state_mem_storage.py
rename to bot/commands/commands_utils/change_state_mem_storage.py
index 0a4e052..b726dc9 100644
--- a/bot/other_functions/change_state_mem_storage.py
+++ b/bot/commands/commands_utils/change_state_mem_storage.py
@@ -1,4 +1,4 @@
-from bot.bot_main.main_objects_initialization import dp
+from bot.config import dp
async def update_state(call):
@@ -7,8 +7,8 @@ async def update_state(call):
storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
data = await storage.get_data()
- data.update({'bot_message_id': call.message.message_id})
- data.update({'password_contains': choice})
+ data.update({"bot_message_id": call.message.message_id})
+ data.update({"password_contains": choice})
await storage.set_data(data)
data = await storage.get_data()
@@ -21,10 +21,10 @@ async def update_with_length_state(call):
storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
data = await storage.get_data()
- necessary_keys = ['bot_message_id', 'password_contains']
+ necessary_keys = ["bot_message_id", "password_contains"]
if all(key in data for key in necessary_keys):
- data.update({'password_length': choice})
+ data.update({"password_length": choice})
await storage.set_data(data)
data = await storage.get_data()
@@ -32,7 +32,9 @@ async def update_with_length_state(call):
return data
else:
await call.answer(
- 'You should press one of the button what your password could contain '
- 'first. And only then you can choose difficulty!', True, cache_time=2
+ "You should press one of the button what your password could contain "
+ "first. And only then you can choose difficulty!",
+ True,
+ cache_time=2,
)
return
diff --git a/bot/commands/commands_utils/check_for_comma_and_dot.py b/bot/commands/commands_utils/check_for_comma_and_dot.py
new file mode 100644
index 0000000..ff5ccc6
--- /dev/null
+++ b/bot/commands/commands_utils/check_for_comma_and_dot.py
@@ -0,0 +1,26 @@
+def check_user_id(message):
+ user_id = ""
+ if ", " in message:
+ user_id = message[: message.find(",")]
+ elif "," in message:
+ user_id = message[: message.find(",")]
+ elif ". " in message:
+ user_id = message[: message.find(".")]
+ elif "." in message:
+ user_id = message[: message.find(".")]
+
+ return user_id
+
+
+def check_user_data(message):
+ user_data = ""
+ if ", " in message:
+ user_data = message[message.find(",") + 2:]
+ elif "," in message:
+ user_data = message[message.find(",") + 1:]
+ elif ". " in message:
+ user_data = message[message.find(".") + 2:]
+ elif "." in message:
+ user_data = message[message.find(".") + 1:]
+
+ return user_data
diff --git a/bot/other_functions/check_for_id_in_table.py b/bot/commands/commands_utils/check_for_id_in_table.py
similarity index 57%
rename from bot/other_functions/check_for_id_in_table.py
rename to bot/commands/commands_utils/check_for_id_in_table.py
index 4e1a22f..ae729a8 100644
--- a/bot/other_functions/check_for_id_in_table.py
+++ b/bot/commands/commands_utils/check_for_id_in_table.py
@@ -1,8 +1,8 @@
-from bot.bot_main.main_objects_initialization import unique_table
+from bot.config import unique_table
def check_for_id(message):
- table_data = unique_table.select_table(f'table_{message.from_user.id}')
+ table_data = unique_table.select_table(f"table_{message.from_user.id}")
temp_lst = []
for count, value in enumerate(table_data):
@@ -10,12 +10,12 @@ def check_for_id(message):
return temp_lst
+
def check_for_password_id(message):
- id_data = unique_table.select_pass_gen_id(f'pass_gen_table_{message.from_user.id}')
+ id_data = unique_table.select_pass_gen_id(f"pass_gen_table_{message.from_user.id}")
temp_lst = []
for id_value in id_data:
temp_lst.append(int(id_value[0]))
return temp_lst
-
diff --git a/bot/other_functions/check_for_repetetive_characters.py b/bot/commands/commands_utils/check_for_repetitive_characters.py
similarity index 100%
rename from bot/other_functions/check_for_repetetive_characters.py
rename to bot/commands/commands_utils/check_for_repetitive_characters.py
diff --git a/bot/commands/commands_utils/check_length_keyboard.py b/bot/commands/commands_utils/check_length_keyboard.py
new file mode 100644
index 0000000..20ae600
--- /dev/null
+++ b/bot/commands/commands_utils/check_length_keyboard.py
@@ -0,0 +1,13 @@
+def keyboard_length_choice(user_choice: str) -> int:
+ password_complexities = {
+ "very_easy": 10,
+ "easy": 20,
+ "normal": 30,
+ "hard": 40,
+ "very_hard": 50,
+ "unbreakable": 100,
+ }
+
+ for complexity_key in password_complexities:
+ if user_choice == complexity_key:
+ return password_complexities[complexity_key]
diff --git a/bot/commands/commands_utils/check_radio_keyboard.py b/bot/commands/commands_utils/check_radio_keyboard.py
new file mode 100644
index 0000000..168c9bf
--- /dev/null
+++ b/bot/commands/commands_utils/check_radio_keyboard.py
@@ -0,0 +1,22 @@
+from string import digits, ascii_letters
+
+from bot.commands.commands_utils.generate_password import (
+ exclude_invalid_symbols_for_markup,
+)
+
+
+def keyboard_content_choice(user_choice: str) -> str:
+ allowed_punctuation = exclude_invalid_symbols_for_markup()
+
+ password_content_options = {
+ "all_characters": digits + ascii_letters + allowed_punctuation,
+ "only_letters": ascii_letters,
+ "only_digits": digits,
+ "letters_digits": digits + ascii_letters,
+ "letters_signs": ascii_letters + allowed_punctuation,
+ "digits_signs": digits + allowed_punctuation,
+ }
+
+ for content_option_key in password_content_options:
+ if user_choice == content_option_key:
+ return password_content_options[content_option_key]
diff --git a/bot/commands/commands_utils/currency_converter_reply_builder.py b/bot/commands/commands_utils/currency_converter_reply_builder.py
new file mode 100644
index 0000000..025f8df
--- /dev/null
+++ b/bot/commands/commands_utils/currency_converter_reply_builder.py
@@ -0,0 +1,20 @@
+from typing import Type, Literal, List
+
+Currency: Type[str] = Literal["dollars", "hryvnias", "euros", "bitcoins"]
+
+
+def currency_reply(
+ currency_amount: float,
+ input_data: str,
+ main_currency: Currency,
+ exchange_currency: List[Currency],
+ buy_price: List[float],
+ sell_price: List[float],
+) -> str:
+ return "".join(
+ f"Cost of buying {input_data} {main_currency} in {exchange_currency[index]}: "
+ f"{currency_amount / buy_price[index]:.4f}\n"
+ f"Selling price of {input_data} {main_currency} in {exchange_currency[index]}: "
+ f"{currency_amount / sell_price[index]:.4f}\n\n"
+ for index in range(len(buy_price))
+ )
diff --git a/bot/commands/commands_utils/currency_cost.py b/bot/commands/commands_utils/currency_cost.py
new file mode 100644
index 0000000..d2f42ad
--- /dev/null
+++ b/bot/commands/commands_utils/currency_cost.py
@@ -0,0 +1,256 @@
+import json
+from abc import ABC, abstractmethod
+from typing import Any, Union, Dict, List, Tuple
+from urllib.request import urlopen
+
+import requests
+
+# from bot.config import CURRENCY_URL, BITCOIN_URL
+from bot.enums.currencies import Currency
+
+
+# from .currency_converter_reply_builder import Currency
+
+
+class Converter:
+ CURRENCY_URL = "https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=11"
+ BITCOIN_URL = "https://api.coindesk.com/v1/bpi/currentprice.json"
+
+ def _fetch_url_data(self, url: str) -> Any:
+ response = urlopen(url)
+ currency_rate_data = json.loads(response.read())
+ return currency_rate_data
+
+ def fetch_currency_data(self) -> Dict[str, Dict[str, float]]:
+ data = self._fetch_url_data(self.CURRENCY_URL)
+ return {
+ "buy": {
+ "uah_eur": float(data[0]["buy"]),
+ "uah_usd": float(data[1]["buy"]),
+ },
+ "sale": {
+ "uah_eur": float(data[0]["sale"]),
+ "uah_usd": float(data[1]["sale"]),
+ },
+ }
+
+ def fetch_bitcoin_data(self) -> Dict[str, float]:
+ data = self._fetch_url_data(self.BITCOIN_URL)
+ return {
+ "btc_usd": float(data["bpi"]["USD"]["rate_float"]),
+ }
+
+ def united_currency_data(self) -> Dict[str, Dict[str, float]]:
+ main_data = self.fetch_currency_data()
+ crypto_data = self.fetch_bitcoin_data()
+ main_data["buy"]["uah_btc"] = crypto_data["btc_usd"] * main_data["buy"]["uah_usd"]
+ main_data["sale"]["uah_btc"] = crypto_data["btc_usd"] * main_data["sale"]["uah_usd"]
+
+ return main_data
+
+ def convert(
+ self, base_currency: Currency
+ ) -> Dict[str, Union[Currency, List[Currency], List[float]]]:
+ currency_list = [*Currency]
+ currency_list.remove(base_currency)
+
+ currency_data = {
+ "main_currency": base_currency,
+ "exchange_currency": currency_list,
+ }
+
+ convert_data = self.united_currency_data()
+ print(convert_data["buy"].values())
+ print(convert_data["sale"].values())
+
+ if base_currency == Currency.HRYVNIAS:
+ currency_data["buy_price"] = list(convert_data["buy"].values())
+ currency_data["sale_price"] = list(convert_data["sale"].values())
+ elif base_currency == Currency.DOLLARS:
+ currency_data["buy_price"] = [1, 1, 1]
+ currency_data["sale_price"] = [1, 1, 1]
+ elif base_currency == Currency.EUROS:
+ currency_data["buy_price"] = [1, 1, 1]
+ currency_data["sale_price"] = [1, 1, 1]
+ elif base_currency == Currency.BITCOINS:
+ currency_data["buy_price"] = [1, 1, 1]
+ currency_data["sale_price"] = [1, 1, 1]
+
+ return currency_data
+
+
+# print(Converter().unite_currency_data())
+# class Converter:
+# def __init__(self):
+# self.currency_rate_data = self._get_currency_rate_data(CURRENCY_URL)
+# self.crypto_rate_data = self._get_currency_rate_data(BITCOIN_URL)
+# self._parse_currency_url()
+# self.buy_prices_usd = {}
+# self.sale_prices_usd = {}
+#
+# def _get_currency_rate_data(self, url: str) -> Any:
+# response = urlopen(url)
+# currency_rate_data = json.loads(response.read())
+# return currency_rate_data
+#
+# def _parse_currency_url(self) -> None:
+# self.buy_prices_usd
+#
+# def _parse_crypto_url(self) -> None:
+# ...
+#
+# def convert(
+# self, converting_currency: CurrencyType
+# ) -> Dict[str, Union[CurrencyType, List[CurrencyType], List[float]]]:
+# input_data = self.check_currency_type(converting_currency)
+#
+# return {
+# "main_currency": input_data["converting_currency"],
+# "exchange_currency": input_data["currency_list"],
+# "buy_price": [1, 1, 1],
+# "sell_price": [1, 1, 1],
+# }
+#
+# def check_currency_type(
+# self, converting_currency: CurrencyType
+# ) -> Dict[str, Union[CurrencyType, List[CurrencyType]]]:
+# currency_list = [*Currency]
+# currency_list.remove(converting_currency)
+#
+# return {
+# "converting_currency": converting_currency,
+# "currency_list": currency_list,
+# }
+#
+#
+# Converter().convert(Currency.HRYVNIAS)
+
+# class Converter(ABC):
+# def __init__(self, url: str):
+# self.response = urlopen(url)
+# self.currency_rate_data = json.loads(self.response.read())
+#
+# @abstractmethod
+# def convert(self, currency: Currency) -> Dict[str, Union[Currency, List[Currency], List[float]]]:
+# raise NotImplementedError
+#
+# @abstractmethod
+# def check_currency_type(self) -> None:
+# raise NotImplementedError
+#
+#
+# class DefaultConverter(Converter):
+# def convert(self, currency: Currency) -> float:
+# data = {
+# "hryvnias": {
+# "sale_usd": 12,
+# "sale_eur": 12,
+# "buy_usd": 12,
+# "buy_eur": 11,
+# },
+# "dollars": {
+# "sale_uah": 12,
+# "sale_eur": 12,
+# "buy_uah": 1 / float(self.currency_rate_data[1]["buy"]),
+# "buy_eur": 11,
+# },
+# "euros": {
+# "sale_uah": 12,
+# "sale_usd": 12,
+# "buy_uah": 1 / float(self.currency_rate_data[0]["buy"]),
+# "buy_usd": 11,
+# }
+# }
+
+
+# class BitcoinConverter(Converter):
+# def convert(self, currency: Crypto) -> float:
+# data = {
+# "bitcoins": {
+# "uah": 12,
+# "usd": 12,
+# "eur": 12,
+# },
+# }
+
+
+# def get_currency_rate_data(url: str) -> Any:
+# response = urlopen(url)
+# currency_rate_data = json.loads(response.read())
+# return currency_rate_data
+#
+#
+# def find_dollars_buy_in_hryvnias(currency_rate_data: Any) -> float:
+# return 1 / float(currency_rate_data[1]["buy"])
+#
+#
+# def find_euros_buy_in_hryvnias(currency_rate_data: Any) -> float:
+# return 1 / float(currency_rate_data[0]["buy"])
+#
+#
+# def find_dollars_buy_in_euros(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[0]["buy"]) / float(currency_rate_data[1]["buy"])
+#
+#
+# def find_hryvnias_buy_in_euros(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[0]["buy"])
+#
+#
+# def find_euros_buy_in_dollars(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[1]["buy"]) / float(currency_rate_data[0]["buy"])
+#
+#
+# def find_hryvnias_buy_in_dollars(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[1]["buy"])
+#
+#
+# def find_dollars_sale_in_hryvnias(currency_rate_data: Any) -> float:
+# return 1 / float(currency_rate_data[1]["sale"])
+#
+#
+# def find_euros_sale_in_hryvnias(currency_rate_data: Any) -> float:
+# return 1 / float(currency_rate_data[0]["sale"])
+#
+#
+# def find_dollars_sale_in_euros(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[0]["sale"]) / float(currency_rate_data[1]["sale"])
+#
+#
+# def find_hryvnias_sale_in_euros(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[0]["sale"])
+#
+#
+# def find_euros_sale_in_dollars(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[1]["sale"]) / float(currency_rate_data[0]["sale"])
+#
+#
+# def find_hryvnias_sale_in_dollars(currency_rate_data: Any) -> float:
+# return float(currency_rate_data[1]["sale"])
+#
+#
+# def bitcoin_price_in_dollars(currency_rate_data: Any) -> float:
+# return float(currency_rate_data["bpi"]["USD"]["rate_float"])
+#
+#
+# def bitcoin_price_in_euros(currency_rate_data: Any) -> float:
+# return float(currency_rate_data["bpi"]["EUR"]["rate_float"])
+#
+#
+# def uah_to_usd(currency_rate_data: Any) -> str:
+# return "".join(
+# f"1 UAH = {find_dollars_buy_in_hryvnias(currency_rate_data):.4f} / "
+# f"{find_dollars_sale_in_hryvnias(currency_rate_data):.4f} USD\n"
+# )
+#
+#
+# def uah_to_euro(currency_rate_data: Any) -> str:
+# return "".join(
+# f"1 UAH = {find_euros_buy_in_hryvnias(currency_rate_data):.4f} / "
+# f"{find_euros_sale_in_hryvnias(currency_rate_data):.4f} EUR\n"
+# )
+
+# def uah_to_btc(currency_rate_data: Any) -> str:
+# return "".join(
+# f"1 UAH = {(find_dollars_buy_in_hryvnias(currency_rate_data) / bitcoin_price_in_dollars(currency_rate_data)):.4f} / "
+# f"{find_euros_sale_in_hryvnias(currency_rate_data):.4f} BTC\n"
+# )
diff --git a/bot/other_functions/delete_with_delay.py b/bot/commands/commands_utils/delete_with_delay.py
similarity index 85%
rename from bot/other_functions/delete_with_delay.py
rename to bot/commands/commands_utils/delete_with_delay.py
index 6821d8c..fae8b2f 100644
--- a/bot/other_functions/delete_with_delay.py
+++ b/bot/commands/commands_utils/delete_with_delay.py
@@ -1,9 +1,10 @@
import asyncio
-from bot.bot_main.main_objects_initialization import bot
+from bot.config import bot
DELETE_TIMEOUT = 60
+
async def delete_messages(message, bot_message):
user_message = message.message_id
await asyncio.sleep(DELETE_TIMEOUT)
diff --git a/bot/commands/commands_utils/encryption_decryption.py b/bot/commands/commands_utils/encryption_decryption.py
new file mode 100644
index 0000000..f0c5d06
--- /dev/null
+++ b/bot/commands/commands_utils/encryption_decryption.py
@@ -0,0 +1,14 @@
+import base64
+
+
+def encrypt(data) -> str:
+ encode = base64.b85encode(data.encode("UTF-16"))
+ sub_start_symbols = (encode.decode()).split("|N", 1)[1] # -> into database
+ return sub_start_symbols
+
+
+def decrypt(data) -> str:
+ print(data)
+ add_start_symbols = "|N" + data
+ decode = base64.b85decode(add_start_symbols).decode("UTF-16") # -> from database
+ return decode
diff --git a/bot/commands/commands_utils/for_photo_creation/__init__.py b/bot/commands/commands_utils/for_photo_creation/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/commands/commands_utils/for_photo_creation/photo_size.py b/bot/commands/commands_utils/for_photo_creation/photo_size.py
new file mode 100644
index 0000000..8d4a105
--- /dev/null
+++ b/bot/commands/commands_utils/for_photo_creation/photo_size.py
@@ -0,0 +1,33 @@
+import os
+
+TXT_FILE = "photo_size.txt"
+TXT_SIZE_PATH = os.path.join("bot", "commands", "commands_utils", "for_photo_creation", TXT_FILE)
+
+def get_ratio(width, height) -> str:
+ if width > height:
+ return f"{round(width / height, 2)} : 1"
+ elif width < height:
+ return f"1 : {round(height / width, 2)}"
+ else:
+ return "1 : 1"
+
+
+def write_to_txt(width, height):
+ sides_ratio = get_ratio(width, height)
+
+ with open(TXT_SIZE_PATH, "w+") as size_txt:
+ size_txt.write(
+ f"Width: {width}\n"
+ f"Height: {height}\n"
+ f"Approximate sides ratio: {sides_ratio}\n"
+ )
+
+
+def get_data_from_txt() -> str:
+ with open(TXT_SIZE_PATH, "r") as size_txt:
+ result = size_txt.read()
+
+ return result
+
+
+# write_to_txt(100, 100)
\ No newline at end of file
diff --git a/bot/bot_main/for_photo_creation/remake_user_photo.py b/bot/commands/commands_utils/for_photo_creation/remake_user_photo.py
similarity index 59%
rename from bot/bot_main/for_photo_creation/remake_user_photo.py
rename to bot/commands/commands_utils/for_photo_creation/remake_user_photo.py
index bf09681..617a83c 100644
--- a/bot/bot_main/for_photo_creation/remake_user_photo.py
+++ b/bot/commands/commands_utils/for_photo_creation/remake_user_photo.py
@@ -1,17 +1,14 @@
import random
import textwrap
-import warnings
from PIL import Image, ImageFont, ImageDraw
+from .photo_size import write_to_txt
-from bot.bot_main.for_photo_creation.photo_size import write_to_txt
-
-def create_new_photo(user_data=' ', pos_x=0, pos_y=0, img_width=0, img_height=0, img_font=20):
-
- warnings.filterwarnings('ignore', category=DeprecationWarning)
-
- used_image = Image.open('imgs/test.jpg')
+def create_new_photo(
+ user_data=" ", pos_x=0, pos_y=0, img_width=0, img_height=0, img_font=20
+):
+ used_image = Image.open("imgs/test.jpg")
if img_width == 0 and img_height == 0:
width, height = used_image.size
@@ -19,28 +16,28 @@ def create_new_photo(user_data=' ', pos_x=0, pos_y=0, img_width=0, img_height=0,
else:
used_image = change_size_statement(img_width, img_height, used_image)
- IMAGE_WIDTH, IMAGE_HEIGHT = used_image.size
- write_to_txt(IMAGE_WIDTH, IMAGE_HEIGHT)
+ image_width, image_height = used_image.size
+ write_to_txt(image_width, image_height)
default_font_size = 20
font_size = change_font_size(img_font, default_font_size)
- if ((pos_x or pos_y) < 0) or (pos_x > IMAGE_WIDTH or pos_y > pos_y):
+ if ((pos_x or pos_y) < 0) or (pos_x > image_width or pos_y > pos_y):
return True
draw = ImageDraw.Draw(used_image)
- font = ImageFont.truetype('fonts/AdverGothicC.ttf', font_size)
+ font = ImageFont.truetype("fonts/AdverGothicC.ttf", font_size)
- dominant_color = get_image_general_color(used_image, IMAGE_WIDTH, IMAGE_HEIGHT)
+ dominant_color = get_image_general_color(used_image, image_width, image_height)
font_color = visible_color(dominant_color)
lines = []
- line = ''
- line_height = font.getsize('A')[1]
+ line = ""
+ line_height = font.getbbox("A")[1]
- for word in user_data.split(' '):
- if draw.textsize(line + ' ' + word, font=font)[0] <= IMAGE_WIDTH - pos_x:
- line += ' ' + word
+ for word in user_data.split(" "):
+ if draw.textlength(text=line + " " + word, font=font) <= image_width - pos_x:
+ line += " " + word
else:
lines.append(line.strip())
line = word
@@ -52,33 +49,31 @@ def create_new_photo(user_data=' ', pos_x=0, pos_y=0, img_width=0, img_height=0,
draw.text((pos_x, pos_y), line, fill=font_color, font=font)
pos_y += line_height + (font_size * 0.2)
- used_image.save('imgs/result.jpg')
+ used_image.save("imgs/result.jpg")
-def create_new_photo_auto_config(clr_choice: bool, user_data=' '):
- warnings.filterwarnings('ignore', category=DeprecationWarning)
-
- used_image = Image.open('imgs/test_auto_conf.jpg')
- IMAGE_WIDTH, IMAGE_HEIGHT = used_image.size
+def create_new_photo_auto_config(clr_choice: bool, user_data=" "):
+ used_image = Image.open("imgs/test_auto_conf.jpg")
+ image_width, image_height = used_image.size
draw = ImageDraw.Draw(used_image)
# 20 big 'A' with font size = 50 is width 722. One 'A' == 36
- if IMAGE_WIDTH <= 500:
- font_size = int(IMAGE_WIDTH * 0.1)
+ if image_width <= 500:
+ font_size = int(image_width * 0.1)
wrap_width = int(font_size * 0.8)
- elif IMAGE_WIDTH <= 1000:
- font_size = int(IMAGE_WIDTH * 0.05)
+ elif image_width <= 1000:
+ font_size = int(image_width * 0.05)
wrap_width = int(font_size * 0.8)
else:
- font_size = int(IMAGE_WIDTH * 0.03)
+ font_size = int(image_width * 0.03)
wrap_width = int(font_size * 0.8)
text_wrap = textwrap.wrap(user_data, width=wrap_width)
- font = ImageFont.truetype('fonts/AdverGothicC.ttf', font_size)
+ font = ImageFont.truetype("fonts/AdverGothicC.ttf", font_size)
- current_height, padding = IMAGE_HEIGHT - font_size, 10
+ current_height, padding = image_height - font_size, 10
- generalized_color = get_image_general_color(used_image, IMAGE_WIDTH, IMAGE_HEIGHT)
+ generalized_color = get_image_general_color(used_image, image_width, image_height)
if clr_choice:
font_color = visible_color(generalized_color)
else:
@@ -87,14 +82,14 @@ def create_new_photo_auto_config(clr_choice: bool, user_data=' '):
for new_line in text_wrap[::-1]:
text_width, text_height = draw.textsize(new_line, font=font)
draw.text(
- ((IMAGE_WIDTH - text_width) / 2, current_height),
+ ((image_width - text_width) / 2, current_height),
new_line,
font=font,
- fill=font_color
+ fill=font_color,
)
current_height -= text_height + padding
- used_image.save('imgs/result_auto_conf.jpg')
+ used_image.save("imgs/result_auto_conf.jpg")
def change_size_statement(new_width: int, new_height: int, image: Image) -> Image:
@@ -123,16 +118,21 @@ def get_image_general_color(image: Image, width, height) -> tuple:
sum_blue = 0
for color_counter, color in enumerate(colors, 1):
- color_coeff = 1 + (color[0] / full_pixels_num)
- sum_red += int(color[1][0] * color_coeff)
- sum_green += int(color[1][1] * color_coeff)
- sum_blue += int(color[1][2] * color_coeff)
+ color_coefficient = 1 + (color[0] / full_pixels_num)
+ sum_red += int(color[1][0] * color_coefficient)
+ sum_green += int(color[1][1] * color_coefficient)
+ sum_blue += int(color[1][2] * color_coefficient)
color_number = len(colors) * 2
- result_color = sum_red // color_number, sum_green // color_number, sum_blue // color_number
+ result_color = (
+ sum_red // color_number,
+ sum_green // color_number,
+ sum_blue // color_number,
+ )
return result_color
+
def visible_color(background: tuple) -> tuple:
red_clr = 255 - background[0]
green_clr = 255 - background[1]
@@ -143,7 +143,9 @@ def visible_color(background: tuple) -> tuple:
def change_color(background: tuple) -> tuple:
- brightness = (background[0] * 299 + background[1] * 587 + background[2] * 114) // 1000
+ brightness = (
+ background[0] * 299 + background[1] * 587 + background[2] * 114
+ ) // 1000
if brightness < 128:
rand_col_1 = random.randint(128, 255)
@@ -155,4 +157,3 @@ def change_color(background: tuple) -> tuple:
rand_col_3 = random.randint(0, 127)
return rand_col_1, rand_col_2, rand_col_3
-
diff --git a/bot/bot_main/for_photo_creation/validate_args_number.py b/bot/commands/commands_utils/for_photo_creation/validate_args_number.py
similarity index 69%
rename from bot/bot_main/for_photo_creation/validate_args_number.py
rename to bot/commands/commands_utils/for_photo_creation/validate_args_number.py
index a74f321..b86c07f 100644
--- a/bot/bot_main/for_photo_creation/validate_args_number.py
+++ b/bot/commands/commands_utils/for_photo_creation/validate_args_number.py
@@ -1,5 +1,5 @@
-from bot.bot_main.for_photo_creation.photo_size import get_data_from_txt
-from bot.bot_main.for_photo_creation.remake_user_photo import create_new_photo
+from .photo_size import get_data_from_txt
+from .remake_user_photo import create_new_photo
async def validate_args(formatted_lst, message):
@@ -20,7 +20,7 @@ async def validate_args(formatted_lst, message):
pos_y=int(formatted_lst[2]),
img_font=int(formatted_lst[3]),
)
- elif len(formatted_lst) == 2 and check_for_int_type(formatted_lst[0], formatted_lst[1]):
+ elif len(formatted_lst) == 2 and check_for_digit(formatted_lst[0]) and check_for_digit(formatted_lst[1]):
get_error_state = create_new_photo(
img_width=int(formatted_lst[0]),
img_height=int(formatted_lst[1]),
@@ -43,28 +43,25 @@ async def validate_args(formatted_lst, message):
if get_error_state:
await message.reply(
- f'Coordinate X or Y are out of image bounds! Try again.\n{get_data_from_txt()}', parse_mode='HTML'
+ f"Coordinate X or Y are out of image bounds! Try again.\n{get_data_from_txt()}",
+ parse_mode="HTML",
)
return False
except ValueError:
await message.reply(
- f'One or more arguments has incorrect value! Try again.\n{get_data_from_txt()}', parse_mode='HTML'
+ f"One or more arguments has incorrect value! Try again.\n{get_data_from_txt()}",
+ parse_mode="HTML",
)
return False
except IndexError:
await message.reply(
- f'Your input is incorrect! Try again.\n{get_data_from_txt()}', parse_mode='HTML'
+ f"Your input is incorrect! Try again.\n{get_data_from_txt()}",
+ parse_mode="HTML",
)
return False
return True
-def check_for_int_type(str1: str, str2: str) -> bool:
- for char in str1, str2:
- if char.isdigit():
- continue
- else:
- return False
-
- return True
+def check_for_digit(str_arg: str) -> bool:
+ return all(char.isdigit() for char in str_arg)
diff --git a/bot/commands/commands_utils/generate_password.py b/bot/commands/commands_utils/generate_password.py
new file mode 100644
index 0000000..a3ab546
--- /dev/null
+++ b/bot/commands/commands_utils/generate_password.py
@@ -0,0 +1,15 @@
+from random import choices
+from string import punctuation
+
+
+def exclude_invalid_symbols_for_markup() -> str:
+ excluded_symbols = '<>&"'
+ allowed_symbols = "".join(
+ [char for char in punctuation if char not in excluded_symbols]
+ )
+
+ return allowed_symbols
+
+
+def main_generation(password_alphabet: str, password_length: int) -> str:
+ return "".join(choices(password_alphabet, k=password_length))
diff --git a/bot/commands/commands_utils/get_days_and_date_num.py b/bot/commands/commands_utils/get_days_and_date_num.py
new file mode 100644
index 0000000..20e66f1
--- /dev/null
+++ b/bot/commands/commands_utils/get_days_and_date_num.py
@@ -0,0 +1,25 @@
+import re
+from typing import Tuple, List
+
+
+def extract_from_user_input_days_num(user_input: str) -> int:
+ days_num_pattern = re.compile("[1-9]+[0-9]*")
+
+ days_num = re.findall(days_num_pattern, user_input)
+ return int(days_num[0])
+
+
+def extract_from_user_input_days_and_date(user_input: str) -> Tuple[int, List[int]]:
+ days_num = extract_from_user_input_days_num(user_input)
+ date_pattern = re.compile(
+ "([1-9]|0[1-9]|[1-2][0-9]|3[0-1]).([1-9]|0[1-9]|1[0-2]).([1-9]+.*[0-9]+)"
+ )
+
+ result_date = re.findall(date_pattern, user_input)
+
+ int_list = []
+ for date_element in result_date[0]:
+ int_list.append(int(date_element))
+ result_date = int_list
+
+ return days_num, result_date
diff --git a/bot/commands/commands_utils/get_id_and_convert.py b/bot/commands/commands_utils/get_id_and_convert.py
new file mode 100644
index 0000000..f8d079f
--- /dev/null
+++ b/bot/commands/commands_utils/get_id_and_convert.py
@@ -0,0 +1,2 @@
+def get_id_from_str(table_name):
+ return int("".join(decimal for decimal in table_name if decimal.isdecimal()))
diff --git a/bot/other_functions/message_delete_exception.py b/bot/commands/commands_utils/message_delete_exception.py
similarity index 100%
rename from bot/other_functions/message_delete_exception.py
rename to bot/commands/commands_utils/message_delete_exception.py
diff --git a/bot/other_functions/non_admin_message_filter.py b/bot/commands/commands_utils/non_admin_message_filter.py
similarity index 81%
rename from bot/other_functions/non_admin_message_filter.py
rename to bot/commands/commands_utils/non_admin_message_filter.py
index 7570296..5dff998 100644
--- a/bot/other_functions/non_admin_message_filter.py
+++ b/bot/commands/commands_utils/non_admin_message_filter.py
@@ -1,17 +1,17 @@
-from bot.bot_main.main_objects_initialization import bot
+from bot.config import bot
def get_admin_ids(administrators_list) -> list:
administrators_id_list = []
for admin in range(len(administrators_list)):
- administrators_id_list.append(administrators_list[admin]['user']['id'])
+ administrators_id_list.append(administrators_list[admin]["user"]["id"])
return administrators_id_list
async def delete_non_admin_message(message):
- if message.chat.type == 'supergroup':
+ if message.chat.type == "supergroup":
administrators_list = await bot.get_chat_administrators(message.chat.id)
administrators_id_list = get_admin_ids(administrators_list)
diff --git a/bot/commands/commands_utils/parse_temprature.py b/bot/commands/commands_utils/parse_temprature.py
new file mode 100644
index 0000000..9464726
--- /dev/null
+++ b/bot/commands/commands_utils/parse_temprature.py
@@ -0,0 +1,105 @@
+import re
+import requests
+from bs4 import BeautifulSoup as Bs
+from typing import List, Tuple, Any
+
+
+def parse_temp_at_time(url: str) -> str:
+ r = requests.get(url)
+ html = Bs(r.content, "html.parser")
+ time = html.select(".imgBlock p")
+ temp_at_time = html.select(".today-temp")
+
+ return time[0].text + ": " + temp_at_time[0].text
+
+
+def parse_minmax_temp(url: str) -> str:
+ r = requests.get(url)
+ html = Bs(r.content, "html.parser")
+ min_temp = html.select(".min span")
+ max_temp = html.select(".max span")
+
+ return (
+ "Min temperature: "
+ + min_temp[0].text
+ + "C\n"
+ + "Max temperature: "
+ + max_temp[0].text
+ + "C\n"
+ )
+
+
+def parse_today_temp(url: str) -> list:
+ r = requests.get(url)
+ html = Bs(r.content, "html.parser")
+
+ today_temp = html.select(".temperature td")
+
+ new_list = []
+ for td in today_temp:
+ new_list.append(td.text)
+
+ return new_list
+
+
+def list_of_tuples(url: str) -> List[Tuple[Any, Any]]:
+ initial_list = parse_today_temp(url)
+ new_list = [
+ (initial_list[i - 1], initial_list[i]) for i in range(1, len(initial_list), 2)
+ ]
+ return new_list
+
+
+def parse_average_precipitation_probability(url: str) -> int:
+ r = requests.get(url)
+ html = Bs(r.content, "html.parser")
+
+ precipitation_probability = html.select("tr:nth-child(8) td")
+ count = 0
+ temp = 0
+ precipitation_probability_list = []
+ for td in precipitation_probability:
+ if td.text == "-":
+ precipitation_probability_list.append(0)
+ temp = temp
+ else:
+ precipitation_probability_list.append(int(td.text))
+ temp += int(td.text)
+ count += 1
+
+ return max(precipitation_probability_list)
+
+
+def check_for_negative_temp(temperature: float) -> str:
+ if temperature > 0:
+ return "+" + str(temperature) + "°C"
+ else:
+ return str(temperature) + "°C"
+
+
+def find_average_temp_between_two(url: str) -> List[str]:
+ temp_lst = list_of_tuples(url)
+ result_list = []
+
+ for i in temp_lst:
+ sum_of_two = 0
+
+ for j in range(2):
+ result = int(re.search(r"-*\d+", i[j]).group())
+ sum_of_two += result
+
+ result_list.append(check_for_negative_temp(sum_of_two / 2))
+
+ return result_list
+
+
+def average_day_temp(url: str) -> str:
+ average_temp = 0
+ count = 0
+ for i in parse_today_temp(url):
+ average_temp += int(re.search(r"-*\d+", i).group())
+ count += 1
+
+ return (
+ "Average temperature: " + check_for_negative_temp(average_temp / count) + "\n\n"
+ )
diff --git a/bot/commands/commands_utils/password_content_callbacks.py b/bot/commands/commands_utils/password_content_callbacks.py
new file mode 100644
index 0000000..8c66f81
--- /dev/null
+++ b/bot/commands/commands_utils/password_content_callbacks.py
@@ -0,0 +1,134 @@
+from aiogram import types
+
+from bot.config import dp
+from bot.keyboards.password_generator.radio_keyboard import (
+ first_generator_keyboard,
+ all_button,
+ letters_button,
+ digits_button,
+ let_dig_button,
+ let_sig_button,
+ dig_sig_button,
+)
+from bot.commands.commands_utils.change_state_mem_storage import update_state
+
+
+def default_content_keyboard():
+ all_button.text = "All characters"
+ letters_button.text = "Only letters"
+ digits_button.text = "Only digits"
+ let_dig_button.text = "Letters & digits"
+ let_sig_button.text = "Letters & signs"
+ dig_sig_button.text = "Digits & signs"
+
+
+@dp.callback_query_handler(text=["all_characters"])
+async def option_all_characters(call: types.CallbackQuery):
+ result = await update_state(call)
+
+ await call.answer("Now your password can contain all possible characters!", True)
+
+ if all_button.text == "All characters ✅":
+ pass
+ else:
+ all_button.text = "All characters ✅"
+ letters_button.text = "Only letters"
+ digits_button.text = "Only digits"
+ let_dig_button.text = "Letters & digits"
+ let_sig_button.text = "Letters & signs"
+ dig_sig_button.text = "Digits & signs"
+
+ await call.message.edit_reply_markup(first_generator_keyboard)
+
+
+@dp.callback_query_handler(text=["only_letters"])
+async def option_only_letters(call: types.CallbackQuery):
+ result = await update_state(call)
+ await call.answer(
+ "Now your password can contain only small and big english letters!", True
+ )
+
+ if letters_button.text == "Only letters ✅":
+ pass
+ else:
+ all_button.text = "All characters"
+ letters_button.text = "Only letters ✅"
+ digits_button.text = "Only digits"
+ let_dig_button.text = "Letters & digits"
+ let_sig_button.text = "Letters & signs"
+ dig_sig_button.text = "Digits & signs"
+
+ await call.message.edit_reply_markup(first_generator_keyboard)
+
+
+@dp.callback_query_handler(text=["only_digits"])
+async def option_only_digits(call: types.CallbackQuery):
+ result = await update_state(call)
+ await call.answer("Now your password can contain only digits!", True)
+
+ if digits_button.text == "Only digits ✅":
+ pass
+ else:
+ all_button.text = "All characters"
+ letters_button.text = "Only letters"
+ digits_button.text = "Only digits ✅"
+ let_dig_button.text = "Letters & digits"
+ let_sig_button.text = "Letters & signs"
+ dig_sig_button.text = "Digits & signs"
+
+ await call.message.edit_reply_markup(first_generator_keyboard)
+
+
+@dp.callback_query_handler(text=["letters_digits"])
+async def option_letters_digits(call: types.CallbackQuery):
+ result = await update_state(call)
+ await call.answer("Now your password can contain english letters and digits!", True)
+
+ if let_dig_button.text == "Letters & digits ✅":
+ pass
+ else:
+ all_button.text = "All characters"
+ letters_button.text = "Only letters"
+ digits_button.text = "Only digits"
+ let_dig_button.text = "Letters & digits ✅"
+ let_sig_button.text = "Letters & signs"
+ dig_sig_button.text = "Digits & signs"
+
+ await call.message.edit_reply_markup(first_generator_keyboard)
+
+
+@dp.callback_query_handler(text=["letters_signs"])
+async def option_letters_signs(call: types.CallbackQuery):
+ result = await update_state(call)
+ await call.answer("Now your password can contain all letters and signs!", True)
+
+ if let_sig_button.text == "Letters & signs ✅":
+ pass
+ else:
+ all_button.text = "All characters"
+ letters_button.text = "Only letters"
+ digits_button.text = "Only digits"
+ let_dig_button.text = "Letters & digits"
+ let_sig_button.text = "Letters & signs ✅"
+ dig_sig_button.text = "Digits & signs"
+
+ await call.message.edit_reply_markup(first_generator_keyboard)
+
+
+@dp.callback_query_handler(text=["digits_signs"])
+async def option_digits_signs(call: types.CallbackQuery):
+ result = await update_state(call)
+
+ await call.answer("Now your password can contain digits and signs!", True)
+
+ if dig_sig_button.text == "Digits & signs ✅":
+ pass
+ else:
+ all_button.text = "All characters"
+ letters_button.text = "Only letters"
+ digits_button.text = "Only digits"
+ let_dig_button.text = "Letters & digits"
+ let_sig_button.text = "Letters & signs"
+ dig_sig_button.text = "Digits & signs ✅"
+
+ await call.message.edit_reply_markup(first_generator_keyboard)
diff --git a/bot/commands/commands_utils/password_length_callbacks_handler.py b/bot/commands/commands_utils/password_length_callbacks_handler.py
new file mode 100644
index 0000000..419b814
--- /dev/null
+++ b/bot/commands/commands_utils/password_length_callbacks_handler.py
@@ -0,0 +1,85 @@
+from typing import Coroutine, Any, Dict
+
+from aiogram import types
+from aiogram.types import InlineKeyboardButton
+
+from bot.config import dp
+from bot.keyboards.password_generator.set_length_keyboard import (
+ very_easy_button,
+ easy_button,
+ normal_button,
+ hard_button,
+ very_hard_button,
+ unbreakable_button,
+ second_generator_keyboard,
+)
+from bot.commands.commands_utils.change_state_mem_storage import update_with_length_state
+
+
+class LengthController:
+ BUTTON_OPTIONS: Dict[InlineKeyboardButton, str] = {
+ very_easy_button: "Very easy",
+ easy_button: "Easy",
+ normal_button: "Normal",
+ hard_button: "Hard",
+ very_hard_button: "Very hard",
+ unbreakable_button: "Unbreakable",
+ }
+ OPTION_SELECTED_SYMBOL: str = " ✅"
+ EMPTY_STRING: str = ""
+
+ async def select_option(
+ self,
+ call: types.CallbackQuery,
+ result: Coroutine[Any, Any, dict | None],
+ button: InlineKeyboardButton,
+ ) -> None:
+ if result is not None and self.OPTION_SELECTED_SYMBOL not in button.text:
+ for button_option in self.BUTTON_OPTIONS:
+ normal_option = self.try_get_option(button_option)
+ self.check_for_selection(button_option, button, normal_option)
+ await call.message.edit_reply_markup(second_generator_keyboard)
+
+ def check_for_selection(
+ self,
+ current_button: InlineKeyboardButton,
+ selected_button: InlineKeyboardButton,
+ option_data: str,
+ ) -> None:
+ if current_button != selected_button:
+ current_button.text = option_data
+ else:
+ current_button.text = option_data + self.OPTION_SELECTED_SYMBOL
+
+ def back_to_default_keyboard(self) -> None:
+ for button_option in self.BUTTON_OPTIONS:
+ button_option.text = self.try_get_option(button_option)
+
+ def try_get_option(self, chosen_option: InlineKeyboardButton) -> str:
+ try:
+ return self.BUTTON_OPTIONS[chosen_option]
+ except KeyError:
+ return chosen_option.text.replace(
+ self.OPTION_SELECTED_SYMBOL, self.EMPTY_STRING
+ )
+
+
+@dp.callback_query_handler(
+ lambda call: call.data
+ in ["very_easy", "easy", "normal", "hard", "very_hard", "unbreakable"]
+)
+async def handle_difficulty_option(call: types.CallbackQuery):
+ button_mapping = {
+ "very_easy": very_easy_button,
+ "easy": easy_button,
+ "normal": normal_button,
+ "hard": hard_button,
+ "very_hard": very_hard_button,
+ "unbreakable": unbreakable_button,
+ }
+
+ option = call.data
+ result = await update_with_length_state(call)
+ length_controller = LengthController()
+ button = button_mapping.get(option)
+ await length_controller.select_option(call, result, button)
diff --git a/bot/commands/commands_utils/process_json.py b/bot/commands/commands_utils/process_json.py
new file mode 100644
index 0000000..3120e3b
--- /dev/null
+++ b/bot/commands/commands_utils/process_json.py
@@ -0,0 +1,77 @@
+import json
+import os
+from typing import List, Tuple, Any
+
+from aiogram import types
+
+from .encryption_decryption import decrypt
+
+NUMBER_OF_JSON_IDENTS = 12
+JSON_DIR = "temp_data"
+
+
+def make_json_dir() -> None:
+ if not os.path.exists(JSON_DIR):
+ os.makedirs(JSON_DIR)
+
+
+def remove_json(call: types.CallbackQuery) -> None:
+ path_to_json = os.path.join(JSON_DIR, f"json_{call.from_user.id}.json")
+
+ if os.path.exists(path_to_json):
+ os.remove(path_to_json)
+
+
+def write_to_json(user_id: int, table_rows: List[Tuple[Any]]) -> None:
+ data = [
+ {
+ "Password №": password_number,
+ "ID": password_data[0],
+ "Password description": password_data[1],
+ "Password": decrypt(password_data[2]),
+ "Length": password_data[3],
+ "Has repetitive?": password_data[4],
+ }
+ for password_number, password_data in enumerate(table_rows, 1)
+ ]
+
+ file_path = os.path.join(JSON_DIR, f"json_{user_id}.json")
+ make_json_dir()
+
+ with open(file_path, "w") as outfile:
+ json.dump(data, outfile, indent=NUMBER_OF_JSON_IDENTS, ensure_ascii=False)
+
+
+def read_json(user_id: int) -> bytes:
+ path_to_json = os.path.join(JSON_DIR, f"json_{user_id}.json")
+ with open(path_to_json, "rb") as json_file:
+ json_content = json_file.read()
+
+ return json_content
+
+
+async def table_is_empty_message(call: types.CallbackQuery) -> types.Message:
+ sent_message = await call.message.edit_text(
+ text="Table has no records. So, JSON is empty.",
+ parse_mode="HTML",
+ )
+ await call.message.edit_reply_markup(reply_markup=call.message.reply_markup)
+ return sent_message
+
+
+async def json_message(call: types.CallbackQuery, json_content: bytes) -> types.Message:
+ sent_message = await call.bot.send_document(
+ chat_id=call.message.chat.id,
+ document=("Passwords.json", json_content),
+ caption=f"Json file for @{call.from_user.username}",
+ parse_mode="HTML",
+ )
+ return sent_message
+
+
+async def send_json(call: types.CallbackQuery, user_id: int, data_list: List[Tuple[Any]]) -> types.Message:
+ make_json_dir()
+ write_to_json(user_id, data_list)
+ json_data = read_json(user_id)
+
+ return await json_message(call, json_data)
diff --git a/bot/commands/commands_utils/states.py b/bot/commands/commands_utils/states.py
new file mode 100644
index 0000000..fcbc750
--- /dev/null
+++ b/bot/commands/commands_utils/states.py
@@ -0,0 +1,36 @@
+from aiogram.dispatcher.filters.state import StatesGroup, State
+
+
+class ConverterForm(StatesGroup):
+ dollar = State()
+ euro = State()
+ hryvnia = State()
+
+
+class DaysToBirthday(StatesGroup):
+ day_month = State()
+
+
+class PasswordGeneratorStates(StatesGroup):
+ update_description = State()
+ update_password = State()
+ delete = State()
+ set_description = State()
+
+
+class PhotoInscription(StatesGroup):
+ user_inscription_config = State()
+
+
+class SearchTerm(StatesGroup):
+ search_term = State()
+
+
+class TaskScheduler(StatesGroup):
+ insert = State()
+ update = State()
+ delete = State()
+
+
+class WeatherInfo(StatesGroup):
+ place = State()
diff --git a/bot/commands/currency_converter_command.py b/bot/commands/currency_converter_command.py
new file mode 100644
index 0000000..c5426a4
--- /dev/null
+++ b/bot/commands/currency_converter_command.py
@@ -0,0 +1,141 @@
+from typing import Dict, Union, Tuple, List
+
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+
+from .commands_utils.currency_converter_reply_builder import currency_reply
+from .commands_utils.currency_cost import Converter
+# from .commands_utils.currency_cost import get_currency_rate_data
+from .commands_utils.states import ConverterForm
+from .commands_utils import currency_cost as cc
+from ..config import dp, CURRENCY_URL
+from ..enums import Command
+from ..enums.currencies import Currency
+# from ..enums.currencies import CurrencyType
+from ..enums.keyboard_callbacks import ConverterCallbackStorage
+from ..keyboards.currency_converter_keyboard import main_converter_menu, currency_rate_menu, convert_menu
+from ..keyboards.keyboard_close import close_keyboard
+
+
+@dp.message_handler(state="*", commands=Command.CURRENCY_CONVERTER)
+async def currency_command_handler(message: types.Message):
+ await message.reply("Choose menu option💵:", reply_markup=main_converter_menu)
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.CURRENCY_RATE)
+async def process_exchange_rate(call: types.CallbackQuery):
+ rate_data = cc.get_currency_rate_data(CURRENCY_URL)
+ # bitcoin = get_currency_rate_data(BITCOIN_URL)
+
+ await call.message.edit_text(
+ f"BUY / SALE\n"
+ f""
+ f"1 UAH = {cc.find_dollars_buy_in_hryvnias(rate_data):.4f} / {cc.find_dollars_sale_in_hryvnias(rate_data):.4f} USD\n"
+ f"1 UAH = {cc.find_euros_buy_in_hryvnias(rate_data):.4f} / {cc.find_euros_sale_in_hryvnias(rate_data):.4f} EUR\n"
+ f"1 EUR = {cc.find_dollars_buy_in_euros(rate_data):.4f} / {cc.find_dollars_sale_in_euros(rate_data):.4f} USD\n"
+ f"1 EUR = {cc.find_hryvnias_buy_in_euros(rate_data):.4f} / {cc.find_hryvnias_sale_in_euros(rate_data):.4f} UAH\n"
+ f"1 USD = {cc.find_euros_buy_in_dollars(rate_data):.4f} / {cc.find_euros_sale_in_dollars(rate_data):.4f} EUR\n"
+ f"1 USD = {cc.find_hryvnias_buy_in_dollars(rate_data):.4f} / {cc.find_hryvnias_sale_in_dollars(rate_data):.4f} UAH\n"
+ # f"1 BTC = {cc.bitcoin_price_in_dollars(bitcoin):.4f} USD\n"
+ # f"1 BTC = {cc.bitcoin_price_in_euros(bitcoin):.4f} EUR\n"
+ f"",
+ parse_mode="HTML",
+ reply_markup=currency_rate_menu,
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.CURRENCY_CONVERTER)
+async def process_converter(call: types.CallbackQuery):
+ await call.message.edit_text("Currency converter:", reply_markup=convert_menu)
+ await call.answer()
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.CONVERT_DOLLAR)
+async def convert_dollar(call: types.CallbackQuery):
+ await ConverterForm.dollar.set()
+ await call.answer(
+ "Enter the amount of $ to convert into euros and hryvnias...", True
+ )
+
+
+@dp.message_handler(state=ConverterForm.dollar)
+async def process_dollar(message: types.Message, state: FSMContext):
+ ...
+ # rate_data = get_currency_rate_data(CURRENCY_URL)
+ #
+ # data: Dict[str, Union[Currency, Tuple[Currency, Currency], Tuple[float, float]]] = {
+ # "main_currency": "dollars",
+ # "exchange_currency": ("hryvnias", "euros"),
+ # "buy_price": (cc.find_dollars_buy_in_hryvnias(rate_data), cc.find_dollars_buy_in_euros(rate_data)),
+ # "sell_price": (cc.find_dollars_sale_in_hryvnias(rate_data), cc.find_dollars_sale_in_euros(rate_data)),
+ # }
+ # await process_currency(message, state, reply_data=data)
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.CONVERT_EURO)
+async def convert_euro(call: types.CallbackQuery):
+ await ConverterForm.euro.set()
+ await call.answer(
+ "Enter the amount of € to convert to dollars and hryvnias...", True
+ )
+
+
+@dp.message_handler(state=ConverterForm.euro)
+async def process_euro(message: types.Message, state: FSMContext):
+ ...
+ # rate_data = get_currency_rate_data(CURRENCY_URL)
+ #
+ # data: Dict[str, Union[Currency, Tuple[Currency, Currency], Tuple[float, float]]] = {
+ # "main_currency": "euros",
+ # "exchange_currency": ("hryvnias", "dollars"),
+ # "buy_price": (cc.find_euros_buy_in_hryvnias(rate_data), cc.find_euros_buy_in_dollars(rate_data)),
+ # "sell_price": (cc.find_euros_sale_in_hryvnias(rate_data), cc.find_euros_sale_in_dollars(rate_data)),
+ # }
+ # await process_currency(message, state, reply_data=data)
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.CONVERT_HRYVNIA)
+async def convert_hryvnia(call: types.CallbackQuery):
+ await ConverterForm.hryvnia.set()
+ await call.answer("Enter the amount of ₴ to convert to dollars and euros...", True)
+
+
+@dp.message_handler(state=ConverterForm.hryvnia)
+async def process_hryvnia(message: types.Message, state: FSMContext):
+ await process_currency(message, state, base_currency=Currency.HRYVNIAS)
+
+
+async def process_currency(
+ message: types.Message,
+ state: FSMContext,
+ base_currency: Currency
+) -> None:
+ input_data = message.text
+
+ try:
+ input_data = input_data.replace(",", ".")
+ currency_amount = float(input_data)
+ reply_dict = Converter().convert(base_currency)
+ reply_data = currency_reply(
+ currency_amount,
+ input_data,
+ *reply_dict.values(),
+ )
+
+ await message.reply(reply_data)
+ except ValueError:
+ await message.reply("Invalid input. Must be a number. Try again.")
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.MAIN_CONVERTER_MENU_BACK)
+async def back_to_main_converter_menu(call: types.CallbackQuery):
+ await call.message.edit_text("Choose menu option💵:", reply_markup=main_converter_menu)
+ await call.answer()
+
+
+@dp.callback_query_handler(text=ConverterCallbackStorage.CLOSE_CONVERTER)
+async def close_converter(call: types.CallbackQuery):
+ await close_keyboard(call)
diff --git a/bot/commands/date_calculator_command.py b/bot/commands/date_calculator_command.py
new file mode 100644
index 0000000..13f2f2c
--- /dev/null
+++ b/bot/commands/date_calculator_command.py
@@ -0,0 +1,46 @@
+import time
+from datetime import date
+
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+
+from .commands_utils.states import DaysToBirthday
+from ..config import dp
+from ..enums import Command
+from bot.keywords.keyword_utils.check_date_words import check_year, check_month, check_day
+
+
+@dp.message_handler(state="*", commands=Command.DATE_CALCULATOR)
+async def date_calculator_command_handler(message: types.Message):
+ await DaysToBirthday.day_month.set()
+ await message.reply("Enter date of birth (or other date) in format dd.mm.yyyy...")
+
+
+@dp.message_handler(state=DaysToBirthday.day_month)
+async def process_date_calculator_command(message: types.Message, state: FSMContext):
+ input_data = message.text
+ input_data = input_data.split(".", 2)
+ current_year = time.localtime().tm_year
+ current_month = time.localtime().tm_mon
+ current_day = time.localtime().tm_mday
+
+ try:
+ answer = abs(
+ date(current_year, current_month, current_day)
+ - date(int(input_data[2]), int(input_data[1]), int(input_data[0]))
+ )
+
+ years = int(answer.days / 365)
+ months = int((answer.days % 365) / 31)
+ days = answer.days - years * 365 - months * 31
+ await message.reply(
+ f"The number of days between the entered date and the current date: {answer.days}\n"
+ f"Formatted date: "
+ f"{years} {check_year(years)}, {months} {check_month(months)}, {days} {check_day(days)}"
+ )
+ except ValueError:
+ await message.reply("Your input was incorrect! Try again.")
+ except IndexError:
+ await message.reply("Your input was incorrect! Try again.")
+
+ await state.finish()
diff --git a/bot/commands/help_command.py b/bot/commands/help_command.py
new file mode 100644
index 0000000..f99b70b
--- /dev/null
+++ b/bot/commands/help_command.py
@@ -0,0 +1,10 @@
+from aiogram import types
+
+from ..config import dp
+from ..config import COMMANDS_DESCRIPTION
+from ..enums import Command
+
+
+@dp.message_handler(state="*", commands=Command.HELP)
+async def help_command_handler(message: types.Message):
+ await message.reply(text=COMMANDS_DESCRIPTION, parse_mode="HTML")
diff --git a/bot/commands/keywords_command.py b/bot/commands/keywords_command.py
new file mode 100644
index 0000000..1984eb3
--- /dev/null
+++ b/bot/commands/keywords_command.py
@@ -0,0 +1,31 @@
+from aiogram import types
+
+from ..config import dp
+from ..enums import Command
+
+
+@dp.message_handler(state="*", commands=Command.KEYWORD_LIST)
+async def get_keywords(message: types.Message):
+ await message.reply(
+ "bitcoin — get bitcoin price\n\n"
+ "dtr sticker — add your sticker to bot database\n\n"
+ "dtr example — send you few examples how to use /photo\n\n"
+ "msgd — deletes the bot message you replied to and your message\n\n"
+ 'random mem any_text — send me an image with caption where must be words "random mem" '
+ "and after this words you can type anything. All what you will type after keyword will be printed "
+ "on your image and sent to you back. Font color will be randomly generated.\n\n"
+ 'mem any_text — send me an image with caption where must be word "mem" '
+ "and after this word you can type anything. All what you will type after keyword will be printed "
+ "on your image and sent to you. Font color for the image will be automatically set by the system "
+ "depending on the colors that the image contains.\n\n"
+ "What date will it be after N days? — send you a message with a date that will "
+ "be after N days from the current date in format dd.mm.yyyy\n\n"
+ 'What date will be after N days if we start from "your_date"? — send you a message with a date '
+ 'that will be after N days from "your_date" '
+ "(must be in the same format as in the result) in format dd.mm.yyyy\n\n"
+ "dtr delete tasks — deletes the table and all its contents created using the command "
+ "/taskscheduler and creates a new empty task table\n\n"
+ "dtr delete passwords — deletes the table and all its contents created using the command "
+ "/password and creates a new empty password table\n\n",
+ parse_mode="HTML",
+ )
diff --git a/bot/commands/password_generator_command.py b/bot/commands/password_generator_command.py
new file mode 100644
index 0000000..b4af3d4
--- /dev/null
+++ b/bot/commands/password_generator_command.py
@@ -0,0 +1,623 @@
+import asyncio
+import mysql.connector
+import aiogram.utils.exceptions
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+from aiogram.types import CallbackQuery
+
+from .commands_utils.states import PasswordGeneratorStates
+from .commands_utils.generate_password import main_generation
+
+from .commands_utils.password_content_callbacks import (
+ default_content_keyboard,
+)
+from .commands_utils.password_length_callbacks_handler import (
+ LengthController,
+)
+from .commands_utils import check_for_comma_and_dot, check_for_id_in_table
+from .commands_utils.check_for_repetitive_characters import (
+ check_if_repeatable_characters_is_present,
+)
+from .commands_utils.check_length_keyboard import keyboard_length_choice
+from .commands_utils.check_radio_keyboard import keyboard_content_choice
+from .commands_utils.encryption_decryption import encrypt, decrypt
+from .commands_utils.process_json import send_json, remove_json, table_is_empty_message
+from .commands_utils.message_delete_exception import message_delete_control
+from ..config import (
+ dp,
+ bot,
+ unique_table,
+)
+from ..enums import Command
+from ..keyboards.keyboard_close import close_keyboard
+from ..keyboards.password_generator.back_keyboard import (
+ back_to_telegram_generator_keyboard,
+)
+from ..keyboards.password_generator.download_app_keyboard import download_app_keyboard
+from ..keyboards.password_generator.generation_keyboard import (
+ third_generator_keyboard,
+)
+from ..keyboards.password_generator.radio_keyboard import first_generator_keyboard
+from ..keyboards.password_generator.set_length_keyboard import (
+ second_generator_keyboard,
+)
+from ..keyboards.password_generator.main_keyboard import password_start_keyboard
+from ..keyboards.password_generator.return_to_update_keyboard import (
+ password_generator_return_to_update,
+)
+from ..keyboards.password_generator.telegram_keyboard import (
+ password_telegram_keyboard,
+)
+from ..keyboards.password_generator.update_keyboard import (
+ password_generator_update_keyboard,
+)
+
+DELETE_TIMEOUT = 60
+
+
+@dp.message_handler(state="*", commands=Command.PASSWORD_GENERATOR)
+async def password(message: types.Message):
+ await message.reply(
+ "Welcome to password generator 🔒\n\n"
+ ""
+ "As you can see below, there are two buttons. By clicking on the first button, "
+ "you will go to the password generator embedded in me. After clicking the second button, "
+ "you will be presented with options to download the application to your local machine."
+ "",
+ reply_markup=password_start_keyboard,
+ parse_mode="HTML",
+ )
+
+
+@dp.callback_query_handler(text=["telegram_password"])
+async def telegram_password(call: CallbackQuery):
+ await call.message.edit_text(
+ "Choose menu option:\n\n",
+ reply_markup=password_telegram_keyboard,
+ parse_mode="HTML",
+ )
+
+
+@dp.callback_query_handler(text=["show_password"])
+async def option_show_passwords(call: types.CallbackQuery):
+ select_result = unique_table.select_pass_gen_table(
+ f"pass_gen_table_{call.from_user.id}"
+ )
+
+ if not select_result:
+ await call.message.edit_text(
+ "Table has no records", reply_markup=back_to_telegram_generator_keyboard
+ )
+ else:
+ all_passwords = "".join(
+ f"Password №{password_number}\n"
+ f"ID: {password_data[0]}\n"
+ f"Description: {password_data[1]}\n"
+ f"Password: {decrypt(password_data[2])}\n"
+ f"Length: {password_data[3]}\n"
+ f"Has repetitive?: {password_data[4]}\n\n"
+ for password_number, password_data in enumerate(select_result, 1)
+ )
+
+ try:
+ await call.message.edit_text(
+ text=all_passwords,
+ reply_markup=back_to_telegram_generator_keyboard,
+ parse_mode="HTML",
+ )
+
+ await call.answer()
+ except aiogram.utils.exceptions.BadRequest:
+ await call.message.edit_text(
+ text="Your passwords data is too big. I will send you a JSON file.",
+ reply_markup=back_to_telegram_generator_keyboard,
+ parse_mode="HTML",
+ )
+
+ sent_message = await send_json(call)
+
+ await call.answer()
+
+ await asyncio.sleep(DELETE_TIMEOUT)
+ await bot.delete_message(
+ call.message.chat.id, message_id=sent_message.message_id
+ )
+
+
+@dp.callback_query_handler(text=["create_password"])
+async def create_password(call: CallbackQuery):
+ message_text = "Choose what your password can contain:"
+
+ await call.message.edit_text(
+ text=message_text, parse_mode="HTML", reply_markup=first_generator_keyboard
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["change_desc_pass"])
+async def change_desc_pass(call: CallbackQuery):
+ await call.message.edit_text(
+ "What do you want to change?:\n\n",
+ reply_markup=password_generator_update_keyboard,
+ parse_mode="HTML",
+ )
+
+
+@dp.callback_query_handler(text=["change_description"])
+async def change_password(call: CallbackQuery):
+ await PasswordGeneratorStates.update_description.set()
+ await call.answer(
+ 'Enter description information in the format: "ID value", "description text"',
+ True,
+ )
+ await call.message.delete()
+
+
+@dp.message_handler(state=PasswordGeneratorStates.update_description)
+async def process_description_update(message: types.Message, state: FSMContext):
+ try:
+ user_data = check_for_comma_and_dot.check_user_data(message.text)
+ user_chosen_id = check_for_comma_and_dot.check_user_id(message.text)
+
+ if int(user_chosen_id) not in check_for_id_in_table.check_for_password_id(
+ message
+ ):
+ raise ValueError
+
+ unique_table.update_password_desc(
+ f"pass_gen_table_{message.from_user.id}", user_data, int(user_chosen_id)
+ )
+
+ await message.answer(
+ "Data has been changed!", reply_markup=password_generator_return_to_update
+ )
+ except ValueError:
+ await message.answer(
+ "Entered data was incorrect!",
+ reply_markup=password_generator_return_to_update,
+ )
+ except mysql.connector.errors.IntegrityError:
+ await message.answer(
+ "Password with such description already exists!",
+ reply_markup=password_generator_return_to_update,
+ )
+
+ await message_delete_control(message)
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=["change_password"])
+async def change_password(call: CallbackQuery):
+ await PasswordGeneratorStates.update_password.set()
+ await call.answer(
+ 'Enter password information in the format: "ID value", "password text"', True
+ )
+ await call.message.delete()
+
+
+@dp.message_handler(state=PasswordGeneratorStates.update_password)
+async def process_password_update(message: types.Message, state: FSMContext):
+ try:
+ user_data = check_for_comma_and_dot.check_user_data(message.text)
+ user_data_length = len(user_data)
+ repetitive_characters_in_password = check_if_repeatable_characters_is_present(
+ user_data
+ )
+ user_chosen_id = check_for_comma_and_dot.check_user_id(message.text)
+
+ if int(user_chosen_id) not in check_for_id_in_table.check_for_password_id(
+ message
+ ):
+ raise ValueError
+
+ encrypted_password = encrypt(user_data)
+ unique_table.update_password(
+ f"pass_gen_table_{message.from_user.id}",
+ encrypted_password,
+ user_data_length,
+ repetitive_characters_in_password,
+ int(user_chosen_id),
+ )
+
+ await message.answer(
+ "Data has been changed!", reply_markup=password_generator_return_to_update
+ )
+ except ValueError:
+ await message.answer(
+ "Entered data was incorrect!",
+ reply_markup=password_generator_return_to_update,
+ )
+ except mysql.connector.errors.IntegrityError:
+ await message.answer(
+ "Password with such description already exists!",
+ reply_markup=password_generator_return_to_update,
+ )
+
+ await message_delete_control(message)
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=["delete_password"])
+async def delete_password(call: CallbackQuery):
+ await PasswordGeneratorStates.delete.set()
+ await call.answer(
+ "Enter password ID and all data about that password will be removed.", True
+ )
+ await call.message.delete()
+
+
+@dp.message_handler(state=PasswordGeneratorStates.delete)
+async def process_password_update(message: types.Message, state: FSMContext):
+ try:
+ if int(message.text) not in check_for_id_in_table.check_for_password_id(
+ message
+ ):
+ raise ValueError
+
+ unique_table.delete_from_password_table(
+ f"pass_gen_table_{message.from_user.id}", message.text
+ )
+ await message.answer(
+ "Data deleted successfully!",
+ reply_markup=back_to_telegram_generator_keyboard,
+ )
+ except ValueError:
+ await message.answer(
+ "Password ID was entered incorrectly!",
+ reply_markup=back_to_telegram_generator_keyboard,
+ )
+
+ await message_delete_control(message)
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=["password_app"])
+async def password_app(call: CallbackQuery):
+ await call.message.edit_text(
+ "Choose your system: ",
+ parse_mode="HTML",
+ reply_markup=download_app_keyboard,
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["windows_installer"])
+async def windows_installer(call: CallbackQuery):
+ with open(
+ "password_generator_app/Password_Generator_Windows_Setup.zip", "rb"
+ ) as zip_archive:
+ zip_content = zip_archive.read()
+
+ sent_message = await call.bot.send_document(
+ chat_id=call.message.chat.id,
+ document=("Password_Generator_Windows_Setup.zip", zip_content),
+ caption="Password for archive: 123454321",
+ parse_mode="HTML",
+ )
+
+ await call.answer()
+
+ await asyncio.sleep(DELETE_TIMEOUT)
+ await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
+
+
+@dp.callback_query_handler(text=["linux_macos"])
+async def linux_macos(call: CallbackQuery):
+ with open(
+ "password_generator_app/Password_Generator_v2_Linux.zip", "rb"
+ ) as zip_archive:
+ zip_content = zip_archive.read()
+
+ sent_message = await call.bot.send_document(
+ chat_id=call.message.chat.id,
+ document=("Password_Generator_v2_Linux.zip", zip_content),
+ caption="Password for archive: 123454321",
+ parse_mode="HTML",
+ )
+
+ await call.answer()
+
+ await asyncio.sleep(DELETE_TIMEOUT)
+ await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
+
+
+@dp.callback_query_handler(text=["json_password"])
+async def json_password(call: CallbackQuery):
+ user_id = call.from_user.id
+ password_data_list = unique_table.select_pass_gen_table(f"pass_gen_table_{user_id}")
+
+ if not password_data_list:
+ return await table_is_empty_message(call)
+
+ await call.message.edit_text(
+ "You can see your JSON file below.",
+ parse_mode="HTML",
+ reply_markup=back_to_telegram_generator_keyboard,
+ )
+
+ sent_message = await send_json(call, user_id, password_data_list)
+ remove_json(call)
+
+ await call.answer(cache_time=2)
+
+ await asyncio.sleep(DELETE_TIMEOUT)
+ await bot.delete_message(call.message.chat.id, message_id=sent_message.message_id)
+
+
+@dp.callback_query_handler(text=["generate_password"])
+async def generate_password(call: types.CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ data = await storage.get_data()
+ necessary_keys = ["bot_message_id", "password_contains", "password_length"]
+ user_alphabet = data.get("password_contains")
+ user_length = data.get("password_length")
+
+ if all(key in data for key in necessary_keys):
+ alphabet = keyboard_content_choice(user_alphabet)
+ password_length = keyboard_length_choice(user_length)
+ generated_password = main_generation(alphabet, password_length)
+ data.update({"generated_password": generated_password})
+
+ await call.answer(cache_time=2)
+
+ await call.message.edit_text(
+ text=f"Your password:\n{generated_password}", parse_mode="HTML"
+ )
+ await call.message.edit_reply_markup(reply_markup=third_generator_keyboard)
+ await storage.set_data(data)
+ else:
+ await call.answer(
+ "You need to press one of the button what your password could "
+ "contain and choose password difficulty first!",
+ True,
+ )
+
+
+@dp.callback_query_handler(text=["store_in_db"])
+async def option_set_description(call: types.CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ data = await storage.get_data()
+ necessary_keys = [
+ "bot_message_id",
+ "password_contains",
+ "password_length",
+ "generated_password",
+ ]
+
+ if all(key in data for key in necessary_keys):
+ await PasswordGeneratorStates.set_description.set()
+ await call.answer("Enter description text...", True)
+ else:
+ await call.answer(
+ "You need to press one of the button what your password could "
+ "contain, choose password difficulty and generate password first!",
+ True,
+ )
+
+
+@dp.message_handler(state=PasswordGeneratorStates.set_description)
+async def process_description(message: types.Message, state: FSMContext):
+ user_desc = message.text
+ storage = dp.current_state(chat=message.chat.id, user=message.from_user.id)
+ data = await storage.get_data()
+ data.update({"description": f"{user_desc}"})
+ await storage.set_data(data)
+ message_id = data["bot_message_id"]
+
+ if "generated_password" in data:
+ try:
+ if len(user_desc) > 384:
+ raise ValueError
+
+ table_name = f"pass_gen_table_{message.from_id}"
+ user_description = data["description"]
+ user_password = data["generated_password"]
+ password_length = keyboard_length_choice(data["password_length"])
+ has_repetitive = check_if_repeatable_characters_is_present(user_password)
+
+ await message.delete()
+
+ encrypted_password = encrypt(user_password)
+ unique_table.insert_password_data(
+ table_name,
+ user_description,
+ encrypted_password,
+ password_length,
+ has_repetitive,
+ )
+
+ await message.from_user.bot.edit_message_text(
+ text=f"Password saved successfully!!!\n\n"
+ f'Your description:\n{data["description"]}\n'
+ f'Your password:\n{data["generated_password"]}',
+ chat_id=message.chat.id,
+ message_id=message_id,
+ parse_mode="HTML",
+ )
+ await message.from_user.bot.edit_message_reply_markup(
+ chat_id=message.chat.id,
+ message_id=message_id,
+ reply_markup=third_generator_keyboard,
+ )
+ except ValueError:
+ await message.from_user.bot.edit_message_text(
+ text="Invalid input!\nToo many characters entered.",
+ chat_id=message.chat.id,
+ message_id=message_id,
+ )
+ await message.from_user.bot.edit_message_reply_markup(
+ chat_id=message.chat.id,
+ message_id=message_id,
+ reply_markup=third_generator_keyboard,
+ )
+ except mysql.connector.errors.IntegrityError:
+ await message.from_user.bot.edit_message_text(
+ text="Invalid input!\nDescription already in the table!!!",
+ chat_id=message.chat.id,
+ message_id=message_id,
+ )
+ await message.from_user.bot.edit_message_reply_markup(
+ chat_id=message.chat.id,
+ message_id=message_id,
+ reply_markup=third_generator_keyboard,
+ )
+
+ await state.finish()
+ await storage.set_data(data)
+
+
+@dp.callback_query_handler(text=["next_to_second"])
+async def next_to_second(call: CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ data = await storage.get_data()
+ necessary_keys = ["bot_message_id", "password_contains"]
+
+ if "password_length" not in data:
+ if all(key in data for key in necessary_keys):
+ await call.message.edit_text(
+ "Choose the password difficulty:\n\n",
+ reply_markup=second_generator_keyboard,
+ parse_mode="HTML",
+ )
+ else:
+ await call.message.edit_text(
+ text="Choose what your password can contain:",
+ parse_mode="HTML",
+ )
+ await call.message.edit_reply_markup(reply_markup=first_generator_keyboard)
+ await call.answer("You should select any of these before continue!", True)
+ else:
+ await call.message.edit_text(
+ "Choose the password difficulty:\n\n", parse_mode="HTML"
+ )
+ await call.message.edit_reply_markup(reply_markup=second_generator_keyboard)
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["next_to_third"])
+async def next_to_third(call: CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ data = await storage.get_data()
+ necessary_keys = ["bot_message_id", "password_contains", "password_length"]
+
+ if "generated_password" in data:
+ if "description" in data:
+ await call.message.edit_text(
+ text=f"Password saved successfully!!!\n\n"
+ f'Your description:\n{data["description"]}\n'
+ f'Your password:\n{data["generated_password"]}',
+ reply_markup=third_generator_keyboard,
+ parse_mode="HTML",
+ )
+ else:
+ await call.message.edit_text(
+ text=f'Your password:\n{data["generated_password"]}',
+ reply_markup=third_generator_keyboard,
+ parse_mode="HTML",
+ )
+ else:
+ if all(key in data for key in necessary_keys):
+ await call.message.edit_text(
+ "Generate your password and write it to the database! \n\n",
+ reply_markup=third_generator_keyboard,
+ parse_mode="HTML",
+ )
+ else:
+ await call.message.edit_text(
+ text="Choose the password difficulty:", parse_mode="HTML"
+ )
+ await call.message.edit_reply_markup(reply_markup=second_generator_keyboard)
+ await call.answer(
+ "You need to choose what will contain your password and choose its difficulty!",
+ True,
+ )
+
+
+@dp.callback_query_handler(text=["main_generator_menu"])
+async def main_generator_menu(call: CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ await storage.finish()
+
+ default_content_keyboard()
+ LengthController().back_to_default_keyboard()
+
+ await call.message.edit_text(
+ "Choose menu option: \n\n",
+ reply_markup=password_telegram_keyboard,
+ parse_mode="HTML",
+ )
+
+
+@dp.callback_query_handler(text=["back_to_first"])
+async def back_to_first(call: CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ await storage.get_data()
+
+ message_text = "Choose what your password can contain:"
+
+ await call.message.edit_text(
+ text=message_text, parse_mode="HTML", reply_markup=first_generator_keyboard
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["back_to_second"])
+async def back_to_second(call: CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ data = await storage.get_data()
+ data.get("password_length")
+
+ message_text = "Choose the password difficulty:\n\n"
+
+ await call.message.edit_text(text=message_text, parse_mode="HTML")
+ await call.message.edit_reply_markup(reply_markup=second_generator_keyboard)
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["return_to_update_menu"])
+async def return_to_update_menu(call: CallbackQuery):
+ await call.message.edit_text(
+ "What do you want to change?:",
+ parse_mode="HTML",
+ reply_markup=password_generator_update_keyboard,
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["back_to_telegram_generator"])
+async def back_to_telegram_generator(call: CallbackQuery):
+ storage = dp.current_state(chat=call.message.chat.id, user=call.from_user.id)
+ await storage.finish()
+
+ default_content_keyboard()
+ LengthController().back_to_default_keyboard()
+
+ await call.message.edit_text(
+ "Choose menu option: ",
+ parse_mode="HTML",
+ reply_markup=password_telegram_keyboard,
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["back_to_main_menu"])
+async def back_to_main_menu(call: CallbackQuery):
+ await call.message.edit_text(
+ "Welcome to password generator 🔒\n\n"
+ ""
+ "As you can see below, there are two buttons. By clicking on the first button, "
+ "you will go to the password generator embedded in me. After clicking the second button, "
+ "you will be presented with options to download the application to your local machine."
+ "",
+ parse_mode="HTML",
+ reply_markup=password_start_keyboard,
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["close_app"])
+async def close_app(call: CallbackQuery):
+ await close_keyboard(call)
diff --git a/bot/commands/start_command.py b/bot/commands/start_command.py
new file mode 100644
index 0000000..3110630
--- /dev/null
+++ b/bot/commands/start_command.py
@@ -0,0 +1,13 @@
+from aiogram import types
+
+from ..config import dp
+from ..enums import Command
+
+
+@dp.message_handler(state="*", commands=Command.START)
+async def start_command_handler(message: types.Message):
+ await message.reply(
+ f"{message.from_user.full_name}, I am activated. "
+ f"Type commands or keywords. Use /help command to see the list of possible actions.",
+ parse_mode="HTML"
+ )
diff --git a/bot/commands/task_scheduler_command.py b/bot/commands/task_scheduler_command.py
new file mode 100644
index 0000000..3160bee
--- /dev/null
+++ b/bot/commands/task_scheduler_command.py
@@ -0,0 +1,163 @@
+import mysql.connector
+
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+
+from .commands_utils.message_delete_exception import message_delete_control
+from .commands_utils.states import TaskScheduler
+from .commands_utils import check_for_comma_and_dot, check_for_id_in_table
+from ..config import dp, unique_table
+from ..enums import Command
+from ..keyboards.keyboard_close import close_keyboard
+from ..keyboards.task_scheduler import back_to_planner_keyboard
+from ..keyboards.task_scheduler.scheduler_keyboard import scheduler_keyboard
+
+
+@dp.message_handler(state="*", commands=Command.TASK_SCHEDULER)
+async def scheduler_call(message: types.Message):
+ chat_id = message.chat.id
+ user_id = message.from_id
+ username = message.from_user.username
+ full_name = message.from_user.full_name
+ # main_objects_initialization.store_users_data.connect_to_db(
+ # user_id, username, full_name, chat_id
+ # )
+
+ await message.reply(
+ "Choose a task scheduler function:", reply_markup=scheduler_keyboard
+ )
+
+
+@dp.callback_query_handler(text=["select"])
+async def option_select(call: types.CallbackQuery):
+ select_result = unique_table.select_table(
+ "table_" + str(call.from_user.id)
+ )
+ if not select_result:
+ await call.message.edit_text(
+ "Table has no records",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+ else:
+ all_tasks = "".join(
+ f"ID{task[0]} | Task №{task_number} | {task[1]}\n"
+ for task_number, task in enumerate(select_result, 1)
+ )
+
+ await call.message.edit_text(
+ all_tasks, reply_markup=back_to_planner_keyboard.return_keyboard
+ )
+
+
+@dp.callback_query_handler(text=["insert"])
+async def option_insert(call: types.CallbackQuery):
+ await TaskScheduler.insert.set()
+ await call.answer("Enter the task text...", True)
+ await call.message.delete()
+
+
+@dp.message_handler(state=TaskScheduler.insert)
+async def process_insert(message: types.Message, state: FSMContext):
+ try:
+ if len(message.text) > 768:
+ raise ValueError
+
+ unique_table.insert_into_table(
+ ("table_" + str(message.from_user.id)), message.text
+ )
+ await message.answer(
+ "Data added successfully!",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+ except ValueError:
+ await message.answer(
+ "Too many characters entered!",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+
+ await message_delete_control(message)
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=["update"])
+async def option_update(call: types.CallbackQuery):
+ await TaskScheduler.update.set()
+ await call.answer('Enter information in the format: "ID value", "task text"', True)
+ await call.message.delete()
+
+
+@dp.message_handler(state=TaskScheduler.update)
+async def process_update(message: types.Message, state: FSMContext):
+ try:
+ user_data = check_for_comma_and_dot.check_user_data(message.text)
+ user_id = check_for_comma_and_dot.check_user_id(message.text)
+
+ if int(user_id) not in check_for_id_in_table.check_for_id(message):
+ raise ValueError
+
+ unique_table.update_table(
+ f"table_{message.from_user.id}", user_data, int(user_id)
+ )
+ await message.answer(
+ "Data has been changed!",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+ except ValueError:
+ await message.answer(
+ "Data is entered incorrectly!",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+ except mysql.connector.errors.IntegrityError:
+ await message.answer(
+ "You have already added this task",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+
+ await message_delete_control(message)
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=["delete"])
+async def option_delete(call: types.CallbackQuery):
+ await TaskScheduler.delete.set()
+ await call.answer("Enter the ID of the task you want to delete...", True)
+ await call.message.delete()
+
+
+@dp.message_handler(state=TaskScheduler.delete)
+async def process_delete(message: types.Message, state: FSMContext):
+ try:
+ if int(message.text) not in check_for_id_in_table.check_for_id(message):
+ raise ValueError
+
+ unique_table.delete_from_table(
+ ("table_" + str(message.from_user.id)), message.text
+ )
+ await message.answer(
+ "Data deleted successfully!",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+ except ValueError:
+ await message.answer(
+ "Task ID was entered incorrectly!",
+ reply_markup=back_to_planner_keyboard.return_keyboard,
+ )
+
+ await message_delete_control(message)
+
+ await state.finish()
+
+
+@dp.callback_query_handler(text=["back_to_scheduler"])
+async def back_to_main_menu(call: types.CallbackQuery):
+ await call.message.edit_text(
+ "Choose a task scheduler function:", reply_markup=scheduler_keyboard
+ )
+ await call.answer()
+
+
+@dp.callback_query_handler(text=["close_scheduler"])
+async def close_scheduler(call: types.CallbackQuery):
+ await close_keyboard(call)
diff --git a/bot/commands/time_command.py b/bot/commands/time_command.py
new file mode 100644
index 0000000..059af02
--- /dev/null
+++ b/bot/commands/time_command.py
@@ -0,0 +1,31 @@
+from datetime import date, datetime
+from aiogram import types
+
+from ..config import dp
+from ..enums import Command
+
+
+@dp.message_handler(state="*", commands=Command.TIME)
+async def time_command_handler(message: types.Message):
+ utc_time = datetime.utcnow()
+ current_year = utc_time.timetuple().tm_year
+ current_month = utc_time.timetuple().tm_mon
+ current_day = utc_time.timetuple().tm_mday
+
+ delta = date(current_year, current_month, current_day) - date(2022, 2, 24)
+ days_of_unity = date(current_year, current_month, current_day) - date(1919, 1, 21)
+
+ await message.reply(
+ f'Current date:\t{utc_time.strftime("%d.%m.%Y UTC")} 📅\n'
+ f'Current time:\t{utc_time.strftime("%H:%M:%S UTC")} 🕔\n'
+ f'Day of the week:\t{(utc_time.strftime("%A")).upper()}\n'
+ f"Day of the year:\t{utc_time.timetuple().tm_yday} 🌞\n"
+ f"Number of days after russian full scale invasion to Ukraine:\t{delta.days + 1} 🕊\n"
+ f"Day of Unity of Ukraine:\t{days_of_unity.days} 🤝"
+ )
+
+ if current_month == 1 and current_day == 22:
+ anniversary = current_year - 1919
+ await message.answer(
+ f"WOW, {anniversary}TH ANNIVERSARY OF THE UNITY OF UKRAINE"
+ )
diff --git a/bot/commands/weather_command.py b/bot/commands/weather_command.py
new file mode 100644
index 0000000..6a6b419
--- /dev/null
+++ b/bot/commands/weather_command.py
@@ -0,0 +1,43 @@
+import requests
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+from .commands_utils.states import WeatherInfo
+from .commands_utils.parse_temprature import (
+ find_average_temp_between_two,
+ parse_minmax_temp,
+ average_day_temp,
+ parse_average_precipitation_probability,
+)
+from ..config import dp
+from ..enums import Command
+
+
+@dp.message_handler(state="*", commands=Command.CURRENT_WEATHER)
+async def weather_command_handler(message: types.Message):
+ await WeatherInfo.place.set()
+ await message.reply("Enter the name of the town...")
+
+
+@dp.message_handler(state=WeatherInfo.place)
+async def process_weather_command(message: types.Message, state: FSMContext):
+ input_place = message.text
+
+ url = f"https://ua.sinoptik.ua/погода-{input_place}"
+ answer = requests.get(url)
+
+ if answer.status_code == 200:
+ temp_list = find_average_temp_between_two(url)
+ await message.reply(
+ f"{parse_minmax_temp(url)}"
+ f"{average_day_temp(url)}"
+ f"Night temperature: {temp_list[0]}\n"
+ f"Morning temperature: {temp_list[1]}\n"
+ f"Day temperature: {temp_list[2]}\n"
+ f"Evening temperature: {temp_list[3]}\n\n"
+ f"Maximum chance of precipitation: {parse_average_precipitation_probability(url)}%",
+ parse_mode="Markdown",
+ )
+ else:
+ await message.reply("Can not find such town name!")
+
+ await state.finish()
diff --git a/bot/config.py b/bot/config.py
new file mode 100644
index 0000000..94c81dd
--- /dev/null
+++ b/bot/config.py
@@ -0,0 +1,33 @@
+import os
+
+from aiogram import Bot, Dispatcher
+from aiogram.contrib.fsm_storage.memory import MemoryStorage
+from dotenv import load_dotenv, find_dotenv
+
+from .db_utils.unique_tables_creator import UniqueTablesCreator
+
+load_dotenv(find_dotenv())
+BOT_TOKEN = os.getenv("API_TOKEN")
+
+COMMANDS_DESCRIPTION = """
+/start - check if bot is working
+/time - current date, time and other time info
+/date_calculator - calculate number of days from one date to another
+/current_weather - show the weather in the entered settlement
+/image_editor - adding text to photo, change photo size
+/currency_converter - currency converter
+/password_generator - random password generator
+/task_scheduler - create a list of tasks
+/keyword_list - list of keywords that the bot responds to
+/help - command list
+"""
+
+CURRENCY_URL = "https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=11"
+BITCOIN_URL = "https://api.coindesk.com/v1/bpi/currentprice.json"
+
+# PROXY_URL = "http://proxy.server:3128"
+# bot = Bot(token=BOT_TOKEN, proxy=PROXY_URL)
+bot = Bot(token=BOT_TOKEN)
+storage = MemoryStorage()
+dp = Dispatcher(bot, storage=storage)
+unique_table = UniqueTablesCreator()
diff --git a/bot/db_utils/__init__.py b/bot/db_utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/db_utils/db_connector.py b/bot/db_utils/db_connector.py
new file mode 100644
index 0000000..da8eb35
--- /dev/null
+++ b/bot/db_utils/db_connector.py
@@ -0,0 +1,14 @@
+import os
+import mysql
+import mysql.connector
+
+
+class DatabaseConnector:
+ def __init__(self):
+ self.con = mysql.connector.connect(
+ host=os.getenv("HOST"),
+ user=os.getenv("DB_USER"),
+ passwd=os.getenv("PASSWORD"),
+ database=os.getenv("DATABASE"),
+ )
+ self.cur = self.con.cursor()
diff --git a/bot/bot_main/bot_classes/UniqueTablesForUsers.py b/bot/db_utils/unique_tables_creator.py
similarity index 67%
rename from bot/bot_main/bot_classes/UniqueTablesForUsers.py
rename to bot/db_utils/unique_tables_creator.py
index 261a026..eaf3fc2 100644
--- a/bot/bot_main/bot_classes/UniqueTablesForUsers.py
+++ b/bot/db_utils/unique_tables_creator.py
@@ -1,35 +1,32 @@
-from bot.bot_main.bot_classes.ConnectionDB import ConnectionDB
-from bot.other_functions.get_id_and_convert import get_id_from_str
+from .db_connector import DatabaseConnector
+from bot.commands.commands_utils.get_id_and_convert import get_id_from_str
-class UniqueTablesForUsers(ConnectionDB):
+class UniqueTablesCreator(DatabaseConnector):
def __init__(self):
super().__init__()
- self.cur = self.con.cursor()
- self.date_id_procedure()
- self.month_avg_statistics_view()
-
+ # self.date_id_procedure()
+ # self.month_avg_statistics_view()
def date_id_procedure(self):
self.cur.execute(
- '''
+ """
CREATE PROCEDURE IF NOT EXISTS add_date_and_id(IN get_user_id INT)
BEGIN
DECLARE date_now DATE;
SET date_now = CURDATE();
-
+
INSERT IGNORE INTO statistics(usage_date, user_id)
VALUES (date_now, get_user_id);
END
- '''
+ """
)
self.con.commit()
-
def month_avg_statistics_view(self):
self.cur.execute(
- '''
+ """
CREATE OR REPLACE VIEW statistics_view AS
SELECT u.start_msg_date,
(SELECT s2.usage_date
@@ -48,181 +45,170 @@ def month_avg_statistics_view(self):
JOIN users_info u ON s.user_id = u.user_id
WHERE DATEDIFF(s.usage_date, u.start_msg_date) < 31
GROUP BY s.user_id;
- '''
+ """
)
self.con.commit()
-
-########################################################################################################################
-#################################################### TASK SCHEDULER ####################################################
-########################################################################################################################
+ ########################################################################################################################
+ #################################################### TASK SCHEDULER ####################################################
+ ########################################################################################################################
def create_table(self, user_id):
self.cur.execute(
- '''
+ """
CREATE TABLE IF NOT EXISTS `%s` (
id INT PRIMARY KEY AUTO_INCREMENT,
user_data VARCHAR(768),
CONSTRAINT unique_data UNIQUE(user_data)
)
- ''', (user_id,)
+ """,
+ (user_id,),
)
-
def drop_remake_task_table(self, user_id):
- table_name = f'table_{user_id}'
- self.cur.execute('DROP TABLE IF EXISTS `%s`', (table_name,))
+ table_name = f"table_{user_id}"
+ self.cur.execute("DROP TABLE IF EXISTS `%s`", (table_name,))
self.create_table(table_name)
-
def trigger_on_delete_task(self, trigger_name, table_name, user_id):
# self.cur.execute('DROP TRIGGER IF EXISTS delete_from_task')
self.cur.execute(
- '''
+ """
CREATE TRIGGER IF NOT EXISTS `%s`
BEFORE DELETE ON `%s`
FOR EACH ROW
BEGIN
DECLARE date_now DATE;
DECLARE get_user_id INT;
-
+
SET date_now = CURDATE();
SET get_user_id = %s;
-
+
UPDATE statistics
SET task_sched_delete_num = task_sched_delete_num + 1
WHERE usage_date = date_now AND user_id = get_user_id;
END
- ''', (trigger_name, table_name, user_id)
+ """,
+ (trigger_name, table_name, user_id),
)
-
- def trigger_change_task(self, trigger_name, table_name, user_id):
+ def trigger_change_task(self, trigger_name, table_name, user_id):
# self.cur.execute('DROP TRIGGER IF EXISTS change_task')
self.cur.execute(
- '''
+ """
CREATE TRIGGER IF NOT EXISTS `%s`
BEFORE UPDATE ON `%s`
FOR EACH ROW
BEGIN
DECLARE date_now DATE;
DECLARE get_user_id INT;
-
+
SET date_now = CURDATE();
SET get_user_id = %s;
-
+
UPDATE statistics
SET task_sched_update_num = task_sched_update_num + 1
WHERE usage_date = date_now AND user_id = get_user_id;
END
- ''', (trigger_name, table_name, user_id)
+ """,
+ (trigger_name, table_name, user_id),
)
-
def trigger_add_task(self, trigger_name, table_name, user_id):
# self.cur.execute('DROP TRIGGER IF EXISTS add_task')
self.cur.execute(
- '''
+ """
CREATE TRIGGER IF NOT EXISTS `%s`
BEFORE INSERT ON `%s`
FOR EACH ROW
BEGIN
DECLARE date_now DATE;
DECLARE get_user_id INT;
-
+
SET date_now = CURDATE();
SET get_user_id = %s;
-
+
UPDATE statistics
SET task_sched_insert_num = task_sched_insert_num + 1
WHERE usage_date = date_now AND user_id = get_user_id;
END
- ''', (trigger_name, table_name, user_id)
+ """,
+ (trigger_name, table_name, user_id),
)
-
def select_table(self, user_id):
self.create_table(user_id)
self.cur.execute(
- '''
+ """
SELECT id, user_data FROM `%s`
ORDER BY id;
- ''', (user_id,)
+ """,
+ (user_id,),
)
result = self.cur.fetchall()
return result
-
def delete_from_table(self, user_id, table_id):
self.create_table(user_id)
- converted_id = get_id_from_str(user_id)
- trigger_name = f'delete_task_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_on_delete_task(trigger_name, user_id, converted_id)
+ # converted_id = get_id_from_str(user_id)
+ # trigger_name = f"delete_task_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_on_delete_task(trigger_name, user_id, converted_id)
self.cur.execute(
- '''
+ """
DELETE FROM `%s`
WHERE id = %s
- ''',
- (user_id, table_id)
+ """,
+ (user_id, table_id),
)
self.con.commit()
-
def update_table(self, user_id, user_data, table_id):
self.create_table(user_id)
converted_id = get_id_from_str(user_id)
- trigger_name = f'change_task_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_change_task(trigger_name, user_id, converted_id)
+ # trigger_name = f"change_task_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_change_task(trigger_name, user_id, converted_id)
self.cur.execute(
- '''
+ """
UPDATE `%s`
SET user_data = %s
WHERE id = %s;
- ''',
- (user_id, user_data, table_id)
+ """,
+ (user_id, user_data, table_id),
)
self.con.commit()
-
def insert_into_table(self, user_id, user_data):
self.create_table(user_id)
converted_id = get_id_from_str(user_id)
- trigger_name = f'add_task_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_add_task(trigger_name, user_id, converted_id)
+ # trigger_name = f"add_task_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_add_task(trigger_name, user_id, converted_id)
self.cur.execute(
- 'INSERT IGNORE INTO `%s` (user_data) VALUES (%s)',
- (user_id, user_data)
+ "INSERT IGNORE INTO `%s` (user_data) VALUES (%s)", (user_id, user_data)
)
self.con.commit()
-########################################################################################################################
-################################################## PASSWORD GENERATOR ##################################################
-########################################################################################################################
+ ########################################################################################################################
+ ################################################## PASSWORD GENERATOR ##################################################
+ ########################################################################################################################
def create_pass_gen_table(self, user_id):
self.cur.execute(
- '''
+ """
CREATE TABLE IF NOT EXISTS `%s` (
id INT PRIMARY KEY AUTO_INCREMENT,
password_description VARCHAR(384) COLLATE utf8mb4_bin,
@@ -231,86 +217,86 @@ def create_pass_gen_table(self, user_id):
has_repetetive BOOLEAN,
CONSTRAINT unique_data UNIQUE(password_description)
)
- ''', (user_id,)
+ """,
+ (user_id,),
)
-
def drop_remake_password_table(self, user_id):
- table_name = f'pass_gen_table_{user_id}'
- self.cur.execute('DROP TABLE IF EXISTS `%s`', (table_name,))
+ table_name = f"pass_gen_table_{user_id}"
+ self.cur.execute("DROP TABLE IF EXISTS `%s`", (table_name,))
self.create_pass_gen_table(table_name)
-
def trigger_on_delete_password(self, trigger_name, table_name, user_id):
self.cur.execute(
- '''
+ """
CREATE TRIGGER IF NOT EXISTS `%s`
BEFORE DELETE ON `%s`
FOR EACH ROW
BEGIN
DECLARE date_now DATE;
DECLARE get_user_id INT;
-
+
SET date_now = CURDATE();
SET get_user_id = %s;
-
+
UPDATE statistics
SET pass_gen_delete_num = pass_gen_delete_num + 1
WHERE usage_date = date_now AND user_id = get_user_id;
END
- ''', (trigger_name, table_name, user_id)
+ """,
+ (trigger_name, table_name, user_id),
)
-
def trigger_change_desc_password(self, trigger_name, table_name, user_id):
self.cur.execute(
- '''
+ """
CREATE TRIGGER IF NOT EXISTS `%s`
BEFORE UPDATE ON `%s`
FOR EACH ROW
BEGIN
DECLARE date_now DATE;
DECLARE get_user_id INT;
-
+
SET date_now = CURDATE();
SET get_user_id = %s;
-
+
UPDATE statistics
SET pass_gen_update_num = pass_gen_update_num + 1
WHERE usage_date = date_now AND user_id = get_user_id;
END
- ''', (trigger_name, table_name, user_id)
+ """,
+ (trigger_name, table_name, user_id),
)
-
def trigger_add_password(self, trigger_name, table_name, user_id):
self.cur.execute(
- '''
+ """
CREATE TRIGGER IF NOT EXISTS `%s`
BEFORE INSERT ON `%s`
FOR EACH ROW
BEGIN
DECLARE date_now DATE;
DECLARE get_user_id INT;
-
+
SET date_now = CURDATE();
SET get_user_id = %s;
-
+
UPDATE statistics
SET pass_gen_insert_num = pass_gen_insert_num + 1
WHERE usage_date = date_now AND user_id = get_user_id;
END
- ''', (trigger_name, table_name, user_id)
+ """,
+ (trigger_name, table_name, user_id),
)
-
def select_pass_gen_table(self, user_id):
self.create_pass_gen_table(user_id)
self.cur.execute(
- '''
+ """
SELECT * FROM `%s`
ORDER BY id;
- ''', (user_id,)
+ """,
+ (user_id,),
)
table_rows = self.cur.fetchall()
@@ -320,123 +306,113 @@ def select_pass_gen_table(self, user_id):
def select_pass_gen_id(self, user_id):
self.create_pass_gen_table(user_id)
self.cur.execute(
- '''
+ """
SELECT id FROM `%s`
ORDER BY id;
- ''', (user_id,)
+ """,
+ (user_id,),
)
result = self.cur.fetchall()
return result
-
def select_description(self, user_id):
self.create_pass_gen_table(user_id)
self.cur.execute(
- '''
+ """
SELECT password_description FROM `%s`
ORDER BY id;
- ''', (user_id,)
+ """,
+ (user_id,),
)
result = self.cur.fetchall()
return str(result)
-
def delete_from_password_table(self, user_id, table_id):
self.create_table(user_id)
converted_id = get_id_from_str(user_id)
- trigger_name = f'delete_password_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_on_delete_password(trigger_name, user_id, converted_id)
+ # trigger_name = f"delete_password_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_on_delete_password(trigger_name, user_id, converted_id)
self.cur.execute(
- '''
+ """
DELETE FROM `%s`
WHERE id = %s
- ''',
- (user_id, table_id)
+ """,
+ (user_id, table_id),
)
self.con.commit()
-
def update_password_desc(self, user_id, user_desc, table_id):
self.create_pass_gen_table(user_id)
- converted_id = get_id_from_str(user_id)
- trigger_name = f'change_password_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_change_desc_password(trigger_name, user_id, converted_id)
+ # converted_id = get_id_from_str(user_id)
+ # trigger_name = f"change_password_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_change_desc_password(trigger_name, user_id, converted_id)
self.cur.execute(
- '''
+ """
UPDATE `%s`
SET password_description = %s
WHERE id = %s;
- ''',
- (user_id, user_desc, table_id)
+ """,
+ (user_id, user_desc, table_id),
)
self.con.commit()
-
def update_password(self, user_id, user_password, length, has_repetetive, table_id):
self.create_pass_gen_table(user_id)
- converted_id = get_id_from_str(user_id)
- trigger_name = f'change_password_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_change_desc_password(trigger_name, user_id, converted_id)
+ # converted_id = get_id_from_str(user_id)
+ # trigger_name = f"change_password_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_change_desc_password(trigger_name, user_id, converted_id)
self.cur.execute(
- '''
+ """
UPDATE `%s`
SET generated_password = %s, password_length = %s, has_repetetive = %s
WHERE id = %s;
- ''',
- (user_id, user_password, length, has_repetetive, table_id)
+ """,
+ (user_id, user_password, length, has_repetetive, table_id),
)
self.con.commit()
-
- def insert_password_data(self, user_id, user_desc, generated_pass, password_length, has_repetetive):
+ def insert_password_data(
+ self, user_id, user_desc, generated_pass, password_length, has_repetetive
+ ):
self.create_pass_gen_table(user_id)
- converted_id = get_id_from_str(user_id)
- trigger_name = f'add_password_{converted_id}'
- self.cur.execute(
- 'CALL add_date_and_id(%s)', (converted_id,)
- )
- self.trigger_add_password(trigger_name, user_id, converted_id)
+ # converted_id = get_id_from_str(user_id)
+ # trigger_name = f"add_password_{converted_id}"
+ # self.cur.execute("CALL add_date_and_id(%s)", (converted_id,))
+ # self.trigger_add_password(trigger_name, user_id, converted_id)
self.cur.execute(
- '''
+ """
INSERT INTO `%s` (password_description, generated_password, password_length, has_repetetive)
VALUES (%s, %s, %s, %s)
- ''',
- (user_id, user_desc, generated_pass, password_length, has_repetetive)
+ """,
+ (user_id, user_desc, generated_pass, password_length, has_repetetive),
)
self.con.commit()
-
def check_tables_to_allow_token(self, user_id):
- self.cur.execute('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = %s', ('bot_db',))
+ self.cur.execute(
+ "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = %s",
+ ("bot_db",),
+ )
all_tables_tuple_lst = self.cur.fetchall()
all_tables_lst = [table_tuple[0] for table_tuple in all_tables_tuple_lst]
table_name = f"'pass_gen_table_{user_id}'"
- if table_name in all_tables_lst:
- return True
- else:
- return False
+ return table_name in all_tables_lst
diff --git a/bot/enums/__init__.py b/bot/enums/__init__.py
new file mode 100644
index 0000000..61bdc6c
--- /dev/null
+++ b/bot/enums/__init__.py
@@ -0,0 +1,5 @@
+from .commands import Command
+
+__all__ = (
+ "Command"
+)
diff --git a/bot/enums/commands.py b/bot/enums/commands.py
new file mode 100644
index 0000000..b7bc7da
--- /dev/null
+++ b/bot/enums/commands.py
@@ -0,0 +1,18 @@
+from enum import Enum
+
+
+class Command(str, Enum):
+ START = "start"
+ HELP = "help"
+ TIME = "time"
+ CURRENT_WEATHER = "current_weather"
+ CURRENCY_CONVERTER = "currency_converter"
+ DATE_CALCULATOR = "date_calculator"
+ TASK_SCHEDULER = "task_scheduler"
+ IMAGE_EDITOR = "image_editor"
+ PASSWORD_GENERATOR = "password_generator"
+ KEYWORD_LIST = "keyword_list"
+
+ @classmethod
+ def __len__(cls) -> int:
+ return len(cls._member_names_)
diff --git a/bot/enums/currencies.py b/bot/enums/currencies.py
new file mode 100644
index 0000000..c8bd880
--- /dev/null
+++ b/bot/enums/currencies.py
@@ -0,0 +1,19 @@
+from enum import Enum, auto
+from typing import Type, Literal
+
+
+class Currency(str, Enum):
+ HRYVNIAS = auto()
+ DOLLARS = auto()
+ EUROS = auto()
+ BITCOINS = auto()
+
+ def __str__(self) -> str:
+ return self.name.lower()
+
+ @classmethod
+ def __len__(cls) -> int:
+ return len(cls._member_names_)
+
+
+# CurrencyType: Type[Currency] = Literal[Currency.DOLLARS, Currency.HRYVNIAS, Currency.EUROS, Currency.BITCOINS]
diff --git a/bot/enums/keyboard_callbacks.py b/bot/enums/keyboard_callbacks.py
new file mode 100644
index 0000000..6bde200
--- /dev/null
+++ b/bot/enums/keyboard_callbacks.py
@@ -0,0 +1,28 @@
+from enum import Enum
+
+
+class CallbackDataStorage(str, Enum):
+ @classmethod
+ def __len__(cls) -> int:
+ return len(cls._member_names_)
+
+ def __str__(self):
+ return self.value
+
+
+class ConverterCallbackStorage(CallbackDataStorage):
+ CONVERT_DOLLAR = "convert_dollar"
+ CONVERT_EURO = "convert_euro"
+ CONVERT_HRYVNIA = "convert_hryvnia"
+ CURRENCY_RATE = "currency_rate"
+ CURRENCY_CONVERTER = "currency_converter"
+ CLOSE_CONVERTER = "close_converter"
+ MAIN_CONVERTER_MENU_BACK = "main_converter_menu_back"
+
+
+class GeneratorCallbackStorage(CallbackDataStorage):
+ ...
+
+
+class SchedulerCallbackStorage(CallbackDataStorage):
+ ...
diff --git a/bot/enums/keywords.py b/bot/enums/keywords.py
new file mode 100644
index 0000000..1b33540
--- /dev/null
+++ b/bot/enums/keywords.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class Keyword(str, Enum):
+
+
+ @classmethod
+ def __len__(cls) -> int:
+ return len(cls._member_names_)
diff --git a/bot/keyboards/__init__.py b/bot/keyboards/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/keyboards/currency_converter/converter_keyboard.py b/bot/keyboards/currency_converter/converter_keyboard.py
deleted file mode 100644
index 27e7e2e..0000000
--- a/bot/keyboards/currency_converter/converter_keyboard.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-from bot.keyboards.currency_converter.return_keyboard import return_button
-
-converter_keyboard = InlineKeyboardMarkup()
-
-dollar_button = InlineKeyboardButton(text='Dollar conversion', callback_data='convert_dollar')
-euro_button = InlineKeyboardButton(text='Euro conversion', callback_data='convert_euro')
-hryvnia_button = InlineKeyboardButton(text='Hryvnia conversion', callback_data='convert_hryvnia')
-
-converter_keyboard.add(dollar_button).add(euro_button).add(hryvnia_button).add(return_button)
diff --git a/bot/keyboards/currency_converter/currency_keyboard.py b/bot/keyboards/currency_converter/currency_keyboard.py
deleted file mode 100644
index 61c0d81..0000000
--- a/bot/keyboards/currency_converter/currency_keyboard.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-
-currency_keyboard = InlineKeyboardMarkup()
-
-exchange_rate_button = InlineKeyboardButton(text='Exchange rate', callback_data='currency_rate')
-converter_button = InlineKeyboardButton(text='Converter', callback_data='currency_converter')
-close_button = InlineKeyboardButton(text='Close keyboard', callback_data='close_converter')
-
-currency_keyboard.add(exchange_rate_button).insert(converter_button).add(close_button)
diff --git a/bot/keyboards/currency_converter/return_keyboard.py b/bot/keyboards/currency_converter/return_keyboard.py
deleted file mode 100644
index 449e533..0000000
--- a/bot/keyboards/currency_converter/return_keyboard.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-
-return_keyboard = InlineKeyboardMarkup()
-
-return_button = InlineKeyboardButton(text='Back', callback_data='back_to_menu')
-
-return_keyboard.add(return_button)
diff --git a/bot/keyboards/currency_converter_keyboard.py b/bot/keyboards/currency_converter_keyboard.py
new file mode 100644
index 0000000..4a4898a
--- /dev/null
+++ b/bot/keyboards/currency_converter_keyboard.py
@@ -0,0 +1,16 @@
+from .keyboard_base import KeyboardBuilder
+from ..enums.keyboard_callbacks import ConverterCallbackStorage
+
+main_converter_menu = KeyboardBuilder()
+main_converter_menu.add_column_button("Exchange rate", ConverterCallbackStorage.CURRENCY_RATE)
+main_converter_menu.add_row_button("Converter", ConverterCallbackStorage.CURRENCY_CONVERTER)
+main_converter_menu.add_column_button("Close keyboard", ConverterCallbackStorage.CLOSE_CONVERTER)
+
+currency_rate_menu = KeyboardBuilder()
+currency_rate_menu.add_column_button("Back", ConverterCallbackStorage.MAIN_CONVERTER_MENU_BACK)
+
+convert_menu = KeyboardBuilder()
+convert_menu.add_column_button("Dollar conversion", ConverterCallbackStorage.CONVERT_DOLLAR)
+convert_menu.add_column_button("Euro conversion", ConverterCallbackStorage.CONVERT_EURO)
+convert_menu.add_column_button("Hryvnia conversion", ConverterCallbackStorage.CONVERT_HRYVNIA)
+convert_menu.add_column_button("Back", ConverterCallbackStorage.MAIN_CONVERTER_MENU_BACK)
diff --git a/bot/keyboards/keyboard_base.py b/bot/keyboards/keyboard_base.py
new file mode 100644
index 0000000..65a67cf
--- /dev/null
+++ b/bot/keyboards/keyboard_base.py
@@ -0,0 +1,14 @@
+from typing import Any
+
+from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
+
+
+class KeyboardBuilder(InlineKeyboardMarkup):
+ def _make_button(self, text: str, callback_data: str) -> InlineKeyboardButton:
+ return InlineKeyboardButton(text=text, callback_data=callback_data)
+
+ def add_column_button(self, text: str, callback_data: str) -> Any:
+ return self.add(self._make_button(text, callback_data))
+
+ def add_row_button(self, text: str, callback_data: str) -> Any:
+ return self.insert(self._make_button(text, callback_data))
diff --git a/bot/other_functions/message_attribute_error.py b/bot/keyboards/keyboard_close.py
similarity index 65%
rename from bot/other_functions/message_attribute_error.py
rename to bot/keyboards/keyboard_close.py
index 3ba3935..30b2ff7 100644
--- a/bot/other_functions/message_attribute_error.py
+++ b/bot/keyboards/keyboard_close.py
@@ -8,3 +8,9 @@ async def message_attribute_control(call):
pass
except aiogram.utils.exceptions.MessageCantBeDeleted:
pass
+
+
+async def close_keyboard(call):
+ await message_attribute_control(call)
+ await call.message.delete()
+ await call.answer()
diff --git a/bot/keyboards/password_generator/back_keyboard.py b/bot/keyboards/password_generator/back_keyboard.py
index 48303f9..9c02c1d 100644
--- a/bot/keyboards/password_generator/back_keyboard.py
+++ b/bot/keyboards/password_generator/back_keyboard.py
@@ -2,6 +2,8 @@
back_to_telegram_generator_keyboard = InlineKeyboardMarkup()
-back_button = InlineKeyboardButton(text='Back', callback_data='back_to_telegram_generator')
+back_button = InlineKeyboardButton(
+ text="Back", callback_data="back_to_telegram_generator"
+)
-back_to_telegram_generator_keyboard.add(back_button)
\ No newline at end of file
+back_to_telegram_generator_keyboard.add(back_button)
diff --git a/bot/keyboards/password_generator/download_app_keyboard.py b/bot/keyboards/password_generator/download_app_keyboard.py
index 5334176..f4b932e 100644
--- a/bot/keyboards/password_generator/download_app_keyboard.py
+++ b/bot/keyboards/password_generator/download_app_keyboard.py
@@ -4,7 +4,11 @@
download_app_keyboard = InlineKeyboardMarkup()
-windows_button = InlineKeyboardButton(text='Windows installer', callback_data='windows_installer')
-linux_mac_button = InlineKeyboardButton(text='Linux/MacOS binary', callback_data='linux_macos')
+windows_button = InlineKeyboardButton(
+ text="Windows installer", callback_data="windows_installer"
+)
+linux_mac_button = InlineKeyboardButton(
+ text="Linux/MacOS binary", callback_data="linux_macos"
+)
download_app_keyboard.add(windows_button).insert(linux_mac_button).add(return_button)
diff --git a/bot/keyboards/password_generator/generation_keyboard.py b/bot/keyboards/password_generator/generation_keyboard.py
index d6de65b..d4b99ba 100644
--- a/bot/keyboards/password_generator/generation_keyboard.py
+++ b/bot/keyboards/password_generator/generation_keyboard.py
@@ -2,13 +2,18 @@
third_generator_keyboard = InlineKeyboardMarkup()
-generate_button = InlineKeyboardButton(text='Generate password', callback_data='generate_password')
-write_to_db_button = InlineKeyboardButton(text='Store in database', callback_data='store_in_db')
-back_button = InlineKeyboardButton(text='Back', callback_data='back_to_second')
-return_to_start = InlineKeyboardButton(text='Main generator menu', callback_data='main_generator_menu')
+generate_button = InlineKeyboardButton(
+ text="Generate password", callback_data="generate_password"
+)
+write_to_db_button = InlineKeyboardButton(
+ text="Store in database", callback_data="store_in_db"
+)
+back_button = InlineKeyboardButton(text="Back", callback_data="back_to_second")
+return_to_start = InlineKeyboardButton(
+ text="Main generator menu", callback_data="main_generator_menu"
+)
third_generator_keyboard.add(generate_button)
third_generator_keyboard.insert(write_to_db_button)
third_generator_keyboard.add(back_button)
third_generator_keyboard.insert(return_to_start)
-
diff --git a/bot/keyboards/password_generator/main_keyboard.py b/bot/keyboards/password_generator/main_keyboard.py
index 7f42283..58bd37a 100644
--- a/bot/keyboards/password_generator/main_keyboard.py
+++ b/bot/keyboards/password_generator/main_keyboard.py
@@ -2,8 +2,12 @@
password_start_keyboard = InlineKeyboardMarkup()
-telegram_app_button = InlineKeyboardButton(text='Move to telegram generator', callback_data='telegram_password')
-app_button = InlineKeyboardButton(text='Download desktop application', callback_data='password_app')
-close_button = InlineKeyboardButton(text='Close this menu', callback_data='close_app')
+telegram_app_button = InlineKeyboardButton(
+ text="Move to telegram generator", callback_data="telegram_password"
+)
+app_button = InlineKeyboardButton(
+ text="Download desktop application", callback_data="password_app"
+)
+close_button = InlineKeyboardButton(text="Close this menu", callback_data="close_app")
password_start_keyboard.add(telegram_app_button).insert(app_button).add(close_button)
diff --git a/bot/keyboards/password_generator/radio_keyboard.py b/bot/keyboards/password_generator/radio_keyboard.py
index 3a6aedd..7a1ac4e 100644
--- a/bot/keyboards/password_generator/radio_keyboard.py
+++ b/bot/keyboards/password_generator/radio_keyboard.py
@@ -3,13 +3,19 @@
first_generator_keyboard = InlineKeyboardMarkup()
-all_button = InlineKeyboardButton(text='All characters', callback_data='all_characters')
-letters_button = InlineKeyboardButton(text='Only letters', callback_data='only_letters')
-digits_button = InlineKeyboardButton(text='Only digits', callback_data='only_digits')
-let_dig_button = InlineKeyboardButton(text='Letters & digits', callback_data='letters_digits')
-let_sig_button = InlineKeyboardButton(text='Letters & signs', callback_data='letters_signs')
-dig_sig_button = InlineKeyboardButton(text='Digits & signs', callback_data='digits_signs')
-next_button = InlineKeyboardButton(text='Next', callback_data='next_to_second')
+all_button = InlineKeyboardButton(text="All characters", callback_data="all_characters")
+letters_button = InlineKeyboardButton(text="Only letters", callback_data="only_letters")
+digits_button = InlineKeyboardButton(text="Only digits", callback_data="only_digits")
+let_dig_button = InlineKeyboardButton(
+ text="Letters & digits", callback_data="letters_digits"
+)
+let_sig_button = InlineKeyboardButton(
+ text="Letters & signs", callback_data="letters_signs"
+)
+dig_sig_button = InlineKeyboardButton(
+ text="Digits & signs", callback_data="digits_signs"
+)
+next_button = InlineKeyboardButton(text="Next", callback_data="next_to_second")
first_generator_keyboard.add(all_button)
first_generator_keyboard.add(letters_button)
diff --git a/bot/keyboards/password_generator/return_keyboard.py b/bot/keyboards/password_generator/return_keyboard.py
index 92d598f..23b4a55 100644
--- a/bot/keyboards/password_generator/return_keyboard.py
+++ b/bot/keyboards/password_generator/return_keyboard.py
@@ -2,6 +2,8 @@
return_app_keyboard = InlineKeyboardMarkup()
-return_button = InlineKeyboardButton(text='Return to main menu', callback_data='back_to_main_menu')
+return_button = InlineKeyboardButton(
+ text="Return to main menu", callback_data="back_to_main_menu"
+)
-return_app_keyboard.add(return_button)
\ No newline at end of file
+return_app_keyboard.add(return_button)
diff --git a/bot/keyboards/password_generator/return_to_update_keyboard.py b/bot/keyboards/password_generator/return_to_update_keyboard.py
index 9fb0d1b..d079b8a 100644
--- a/bot/keyboards/password_generator/return_to_update_keyboard.py
+++ b/bot/keyboards/password_generator/return_to_update_keyboard.py
@@ -2,6 +2,6 @@
password_generator_return_to_update = InlineKeyboardMarkup()
-return_button = InlineKeyboardButton(text='Back', callback_data='return_to_update_menu')
+return_button = InlineKeyboardButton(text="Back", callback_data="return_to_update_menu")
-password_generator_return_to_update.add(return_button)
\ No newline at end of file
+password_generator_return_to_update.add(return_button)
diff --git a/bot/keyboards/password_generator/set_length_keyboard.py b/bot/keyboards/password_generator/set_length_keyboard.py
index 5028fc2..37e4349 100644
--- a/bot/keyboards/password_generator/set_length_keyboard.py
+++ b/bot/keyboards/password_generator/set_length_keyboard.py
@@ -2,15 +2,17 @@
second_generator_keyboard = InlineKeyboardMarkup()
-very_easy_button = InlineKeyboardButton(text='Very easy', callback_data='very_easy')
-easy_button = InlineKeyboardButton(text='Easy', callback_data='easy')
-normal_button = InlineKeyboardButton(text='Normal', callback_data='normal')
-hard_button = InlineKeyboardButton(text='Hard', callback_data='hard')
-very_hard_button = InlineKeyboardButton(text='Very hard', callback_data='very_hard')
-unbreakable_button = InlineKeyboardButton(text='Unbreakable', callback_data='unbreakable')
+very_easy_button = InlineKeyboardButton(text="Very easy", callback_data="very_easy")
+easy_button = InlineKeyboardButton(text="Easy", callback_data="easy")
+normal_button = InlineKeyboardButton(text="Normal", callback_data="normal")
+hard_button = InlineKeyboardButton(text="Hard", callback_data="hard")
+very_hard_button = InlineKeyboardButton(text="Very hard", callback_data="very_hard")
+unbreakable_button = InlineKeyboardButton(
+ text="Unbreakable", callback_data="unbreakable"
+)
-back_button = InlineKeyboardButton(text='Back', callback_data='back_to_first')
-next_button = InlineKeyboardButton(text='Next', callback_data='next_to_third')
+back_button = InlineKeyboardButton(text="Back", callback_data="back_to_first")
+next_button = InlineKeyboardButton(text="Next", callback_data="next_to_third")
second_generator_keyboard.add(very_easy_button)
diff --git a/bot/keyboards/password_generator/telegram_keyboard.py b/bot/keyboards/password_generator/telegram_keyboard.py
index 679958f..a730838 100644
--- a/bot/keyboards/password_generator/telegram_keyboard.py
+++ b/bot/keyboards/password_generator/telegram_keyboard.py
@@ -4,11 +4,21 @@
password_telegram_keyboard = InlineKeyboardMarkup()
-show_passwords_button = InlineKeyboardButton(text='Show all passwords', callback_data='show_password')
-create_password_button = InlineKeyboardButton(text='Generate password', callback_data='create_password')
-change_password_button = InlineKeyboardButton(text='Change password', callback_data='change_desc_pass')
-delete_password_button = InlineKeyboardButton(text='Delete password', callback_data='delete_password')
-get_json_button = InlineKeyboardButton(text='Get all passwords in JSON', callback_data='json_password')
+show_passwords_button = InlineKeyboardButton(
+ text="Show all passwords", callback_data="show_password"
+)
+create_password_button = InlineKeyboardButton(
+ text="Generate password", callback_data="create_password"
+)
+change_password_button = InlineKeyboardButton(
+ text="Change password", callback_data="change_desc_pass"
+)
+delete_password_button = InlineKeyboardButton(
+ text="Delete password", callback_data="delete_password"
+)
+get_json_button = InlineKeyboardButton(
+ text="Get all passwords in JSON", callback_data="json_password"
+)
password_telegram_keyboard.add(show_passwords_button)
password_telegram_keyboard.insert(create_password_button)
diff --git a/bot/keyboards/password_generator/update_keyboard.py b/bot/keyboards/password_generator/update_keyboard.py
index a118372..b81a986 100644
--- a/bot/keyboards/password_generator/update_keyboard.py
+++ b/bot/keyboards/password_generator/update_keyboard.py
@@ -4,8 +4,12 @@
password_generator_update_keyboard = InlineKeyboardMarkup()
-change_description_button = InlineKeyboardButton(text='Change description', callback_data='change_description')
-create_password_button = InlineKeyboardButton(text='Change password', callback_data='change_password')
+change_description_button = InlineKeyboardButton(
+ text="Change description", callback_data="change_description"
+)
+create_password_button = InlineKeyboardButton(
+ text="Change password", callback_data="change_password"
+)
password_generator_update_keyboard.add(change_description_button)
password_generator_update_keyboard.insert(create_password_button)
diff --git a/bot/keyboards/random_number/random_numbers_keyboard.py b/bot/keyboards/random_number/random_numbers_keyboard.py
deleted file mode 100644
index 17fd9c8..0000000
--- a/bot/keyboards/random_number/random_numbers_keyboard.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-
-random_number_keyboard = InlineKeyboardMarkup()
-
-random_button1 = InlineKeyboardButton(text='Range 0 — 10', callback_data='random_value_1')
-random_button2 = InlineKeyboardButton(text='Range 0 — 100', callback_data='random_value_2')
-random_button3 = InlineKeyboardButton(text='Range -100 — 100', callback_data='random_value_3')
-random_button4 = InlineKeyboardButton(text='Range -1000000 — 1000000', callback_data='random_value_4')
-close_button = InlineKeyboardButton(text='Close keyboard', callback_data='close_random')
-
-random_number_keyboard.add(random_button1)
-random_number_keyboard.insert(random_button2)
-random_number_keyboard.add(random_button3)
-random_number_keyboard.insert(random_button4)
-random_number_keyboard.add(close_button)
diff --git a/bot/keyboards/task_scheduler/back_to_planner_keyboard.py b/bot/keyboards/task_scheduler/back_to_planner_keyboard.py
index 33d6bad..710e76a 100644
--- a/bot/keyboards/task_scheduler/back_to_planner_keyboard.py
+++ b/bot/keyboards/task_scheduler/back_to_planner_keyboard.py
@@ -2,6 +2,6 @@
return_keyboard = InlineKeyboardMarkup()
-return_button = InlineKeyboardButton(text='Back', callback_data='back_to_scheduler')
+return_button = InlineKeyboardButton(text="Back", callback_data="back_to_scheduler")
return_keyboard.add(return_button)
diff --git a/bot/keyboards/task_scheduler/scheduler_keyboard.py b/bot/keyboards/task_scheduler/scheduler_keyboard.py
index 20d38bf..46aebbe 100644
--- a/bot/keyboards/task_scheduler/scheduler_keyboard.py
+++ b/bot/keyboards/task_scheduler/scheduler_keyboard.py
@@ -2,10 +2,14 @@
scheduler_keyboard = InlineKeyboardMarkup()
-task_button1 = InlineKeyboardButton(text='View tasks', callback_data='select')
-task_button2 = InlineKeyboardButton(text='Add task', callback_data='insert')
-task_button3 = InlineKeyboardButton(text='Change task', callback_data='update')
-task_button4 = InlineKeyboardButton(text='Delete task', callback_data='delete')
-close_scheduler = InlineKeyboardButton(text='Close keyboard', callback_data='close_scheduler')
+task_button1 = InlineKeyboardButton(text="View tasks", callback_data="select")
+task_button2 = InlineKeyboardButton(text="Add task", callback_data="insert")
+task_button3 = InlineKeyboardButton(text="Change task", callback_data="update")
+task_button4 = InlineKeyboardButton(text="Delete task", callback_data="delete")
+close_scheduler = InlineKeyboardButton(
+ text="Close keyboard", callback_data="close_scheduler"
+)
-scheduler_keyboard.add(task_button1).add(task_button2).insert(task_button3).add(task_button4).add(close_scheduler)
+scheduler_keyboard.add(task_button1).add(task_button2).insert(task_button3).add(
+ task_button4
+).add(close_scheduler)
diff --git a/bot/keyboards/token/token_keyboard.py b/bot/keyboards/token/token_keyboard.py
deleted file mode 100644
index 92bdb5d..0000000
--- a/bot/keyboards/token/token_keyboard.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-
-token_keyboard = InlineKeyboardMarkup()
-
-new_token_button = InlineKeyboardButton(text='Generate new token', callback_data='add_token')
-delete_token_button = InlineKeyboardButton(text='Remove token', callback_data='delete_token')
-close_keyboard_button = InlineKeyboardButton(text='Close keyboard', callback_data='close_token')
-
-token_keyboard.add(new_token_button).insert(delete_token_button).add(close_keyboard_button)
diff --git a/bot/keywords/__init__.py b/bot/keywords/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/keywords/keyword_utils/__init__.py b/bot/keywords/keyword_utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/keywords/keyword_utils/check_date_words.py b/bot/keywords/keyword_utils/check_date_words.py
new file mode 100644
index 0000000..64cdba8
--- /dev/null
+++ b/bot/keywords/keyword_utils/check_date_words.py
@@ -0,0 +1,10 @@
+def check_year(years: int) -> str:
+ return "year" if years == 1 else "years"
+
+
+def check_month(months: int) -> str:
+ return "month" if months == 1 else "months"
+
+
+def check_day(days: int) -> str:
+ return "day" if days == 1 else "days"
diff --git a/bot/keywords/keyword_utils/check_document_extension.py b/bot/keywords/keyword_utils/check_document_extension.py
new file mode 100644
index 0000000..a80fe2d
--- /dev/null
+++ b/bot/keywords/keyword_utils/check_document_extension.py
@@ -0,0 +1,65 @@
+import os
+from typing import Union, Optional, Tuple
+
+from aiogram import types
+from aiogram.types import ContentType, PhotoSize, Document
+
+
+def get_conversion_path_name(
+ dir_type: str,
+ user_id: int,
+ file_ext: Optional[str] = None,
+ filename: Optional[str] = None,
+) -> str:
+ path = os.path.join(
+ "temp_data",
+ f"temp_user_{dir_type}_{user_id}",
+ f"{filename}.{file_ext}" if file_ext and filename else "",
+ )
+ return os.path.abspath(path)
+
+
+def get_file_conversion_extension(file_ext: str) -> Optional[str]:
+ doc_extensions = ("docx", "doc")
+ pdf_extension = ("pdf",)
+ document_extension = file_ext
+
+ if document_extension in doc_extensions:
+ return pdf_extension[0]
+ elif document_extension in pdf_extension:
+ return doc_extensions[0]
+ return None
+
+
+def is_file_ext_allowed(
+ message: types.Message, allowed_extensions: Tuple[str, str, str]
+) -> bool:
+ document_extension = message.document.file_name.split(".")[-1]
+ return document_extension in allowed_extensions
+
+
+def is_document(message: types.Message) -> bool:
+ return message.content_type == ContentType.DOCUMENT
+
+
+def is_photo(message: types.Message) -> bool:
+ return message.content_type == ContentType.PHOTO
+
+
+def file_option_selector(
+ message: types.Message,
+) -> Union[PhotoSize, Optional[Document]]:
+ if is_photo(message):
+ return message.photo[-1]
+
+ if is_file_ext_allowed(message, allowed_extensions=("jpg", "jpeg", "png")):
+ return message.document
+
+
+def parse_args(message: types.Message, keyword: str) -> Optional[int]:
+ arg_list = message.text.strip().lower().split(keyword)
+ try:
+ compression_level = int(arg_list[1])
+ return compression_level
+ except ValueError:
+ return None
diff --git a/bot/keywords/keyword_utils/file_size_contoller.py b/bot/keywords/keyword_utils/file_size_contoller.py
new file mode 100644
index 0000000..86f9199
--- /dev/null
+++ b/bot/keywords/keyword_utils/file_size_contoller.py
@@ -0,0 +1,34 @@
+from typing import Optional
+
+from aiogram import types
+from aiogram.types import InputFile
+from aiogram.utils.exceptions import NetworkError
+
+from bot.config import bot
+
+
+async def is_document_sent(
+ message: types.Message, file_to_send: str, new_filename: Optional[str] = None
+) -> bool:
+ try:
+ await bot.send_document(
+ chat_id=message.chat.id,
+ document=InputFile(file_to_send, filename=new_filename),
+ )
+ except NetworkError:
+ return False
+
+ return True
+
+
+async def send_documents(message: types.Message, pdf_path: str, docx_path: str) -> str:
+ pdf_sent = await is_document_sent(message, pdf_path)
+ docx_sent = await is_document_sent(message, docx_path)
+
+ if pdf_sent and docx_sent:
+ return "Conversion was successfully ended!"
+ elif pdf_sent and not docx_sent:
+ return "Conversion error: converted DOCX is to large (must be less than 50 MB)!"
+ elif not pdf_sent and docx_sent:
+ return "Conversion error: converted PDF is to large (must be less than 50 MB)!"
+ return "Conversion failed because converted files are larger than 50MB!"
diff --git a/bot/keywords/keyword_utils/image_to_file_converter.py b/bot/keywords/keyword_utils/image_to_file_converter.py
new file mode 100644
index 0000000..cbdc181
--- /dev/null
+++ b/bot/keywords/keyword_utils/image_to_file_converter.py
@@ -0,0 +1,191 @@
+import os
+import shutil
+
+from abc import ABC, abstractmethod
+from typing import List, Dict, Tuple, Optional
+
+from PIL import Image
+
+from reportlab.lib.pagesizes import letter
+from reportlab.pdfgen import canvas
+from docx import Document
+from docx.shared import Inches
+from aiogram import types
+from bot.config import bot
+
+
+class FileLoader(ABC):
+ TEMP_DATA_DIR = os.path.abspath("temp_data")
+
+ def __init__(self, message: types.Message, dir_type: str):
+ self.message = message
+ self.user_id = message.from_user.id
+ self.temp_dir = os.path.join(
+ self.TEMP_DATA_DIR, f"temp_user_{dir_type}_{self.user_id}"
+ )
+ self.create_temp_dir()
+
+ def create_temp_dir(self) -> None:
+ if not os.path.exists(self.temp_dir):
+ os.makedirs(self.temp_dir)
+
+ def remove_temp_dir(self) -> None:
+ if os.path.exists(self.temp_dir):
+ shutil.rmtree(self.temp_dir)
+
+ @abstractmethod
+ async def save_files(self, file_ids: List[str], **kwargs) -> List[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ async def _files_processing(self, file_ids: List[str], **kwargs) -> List[str]:
+ raise NotImplementedError
+
+
+class ImageLoader(FileLoader):
+ async def save_files(self, image_ids: List[str], **kwargs) -> List[str]:
+ photo_list = await self._files_processing(image_ids)
+ return photo_list
+
+ async def _files_processing(self, image_ids: List[str], **kwargs) -> List[str]:
+ photo_path_list = []
+
+ for image_index, file_id in enumerate(image_ids):
+ image_info = await bot.get_file(file_id)
+ image_path = image_info.file_path
+ image = await bot.download_file(image_path)
+
+ filename = f"image_{image_index}.jpg"
+ temp_image_path = os.path.join(self.temp_dir, filename)
+ with open(temp_image_path, "wb") as temp_image_file:
+ temp_image_file.write(image.getvalue())
+
+ photo_path_list.append(temp_image_path)
+
+ return photo_path_list
+
+
+class ImageQualityReducer:
+ def __init__(self, image_path: str):
+ self.image_path = image_path
+
+ def reduce(
+ self, quality: int, file_specifier: Optional[str] = None
+ ) -> Dict[str, str | Image.Image]:
+ image = Image.open(self.image_path)
+ image = image.convert("RGB")
+ new_path = self.change_path(file_specifier)
+ image.save(fp=f"{new_path}", quality=quality)
+ return {"reduced_image": image, "new_file_path": new_path}
+
+ def change_path(self, file_specifier: str) -> str:
+ dot_symbol = "."
+ sep_ext = self.image_path.split(dot_symbol)
+ new_path = (
+ f"{sep_ext[0]}_reduced_{file_specifier}"
+ if file_specifier
+ else f"{sep_ext[0]}_reduced"
+ )
+ full_path = new_path + dot_symbol + sep_ext[1]
+ return full_path
+
+
+class FileConverter(ABC):
+ @abstractmethod
+ def convert(
+ self, conversion_file_path: str, image_paths: List[str], **kwargs
+ ) -> None:
+ raise NotImplementedError
+
+ def compress_check(
+ self, image_file: str, compression_level: Optional[int], **kwargs
+ ) -> Tuple[str, int, int]:
+ file_extension = kwargs["file_extension"]
+ if compression_level:
+ img_reducer = ImageQualityReducer(image_file)
+ img_obj = img_reducer.reduce(
+ quality=compression_level, file_specifier=file_extension
+ )
+ image_file = img_obj["new_file_path"]
+ img = img_obj["reduced_image"]
+ width, height = img.size
+ else:
+ img = Image.open(image_file)
+ width, height = img.size
+
+ return image_file, width, height
+
+
+class PDFConverter(FileConverter):
+ def convert(
+ self,
+ conversion_file_path: str,
+ image_paths: List[str],
+ compression_level: Optional[int] = None,
+ ):
+ conversion_canvas = canvas.Canvas(conversion_file_path, pagesize=letter)
+
+ for image_file in image_paths:
+ img_data = self.compress(image_file, compression_level)
+ image_file, width, height = img_data
+ aspect_ratio = height / float(width)
+ new_width = letter[0]
+ new_height = letter[0] * aspect_ratio
+ conversion_canvas.drawImage(
+ image_file, 0, 0, width=new_width, height=new_height
+ )
+ conversion_canvas.showPage()
+
+ conversion_canvas.save()
+
+ def compress(
+ self, image_file: str, compression_level: Optional[int]
+ ) -> Tuple[str, int, int]:
+ return self.compress_check(image_file, compression_level, file_extension="pdf")
+
+
+class DOCXConverter(FileConverter):
+ PHONE_IMAGE_RATIO = 0.5
+ DEFAULT_WIDTH_INCHES = Inches(6)
+ TALL_IMAGES_HEIGHT_INCHES = Inches(9)
+
+ def convert(
+ self,
+ conversion_file_path: str,
+ image_paths: List[str],
+ compression_level: Optional[int] = None,
+ ):
+ doc = Document()
+ for image_file in image_paths:
+ img_data = self.compress(image_file, compression_level)
+ image_file, width, height = img_data
+
+ if self.is_image_sides_ratio_tall(image_file):
+ print("Tall image")
+ self.add_picture(doc, image_file, self.TALL_IMAGES_HEIGHT_INCHES)
+ else:
+ print("Normal image")
+ self.add_picture(doc, image_file)
+
+ doc.save(conversion_file_path)
+
+ def add_picture(
+ self, doc: Document, image_file: str, height: Optional[Inches] = None
+ ) -> None:
+ if height:
+ doc.add_paragraph().add_run().add_picture(image_file, height=height)
+ return None
+
+ doc.add_paragraph().add_run().add_picture(
+ image_file, width=self.DEFAULT_WIDTH_INCHES
+ )
+
+ def is_image_sides_ratio_tall(self, image_path: str) -> bool:
+ with Image.open(image_path) as img:
+ aspect_ratio = img.width / img.height
+ return aspect_ratio < self.PHONE_IMAGE_RATIO
+
+ def compress(
+ self, image_file: str, compression_level: Optional[int]
+ ) -> Tuple[str, int, int]:
+ return self.compress_check(image_file, compression_level, file_extension="docx")
diff --git a/bot/keywords/keyword_utils/morse_alphabet.py b/bot/keywords/keyword_utils/morse_alphabet.py
new file mode 100644
index 0000000..9389843
--- /dev/null
+++ b/bot/keywords/keyword_utils/morse_alphabet.py
@@ -0,0 +1,100 @@
+MORSE_CODE_DICT = {
+ "EN": {
+ "A": "•-",
+ "B": "-•••",
+ "C": "-•-•",
+ "D": "-••",
+ "E": "•",
+ "F": "••-•",
+ "G": "--•",
+ "H": "••••",
+ "I": "••",
+ "J": "•---",
+ "K": "-•-",
+ "L": "•-••",
+ "M": "--",
+ "N": "-•",
+ "O": "---",
+ "P": "•--•",
+ "Q": "--•-",
+ "R": "•-•",
+ "S": "•••",
+ "T": "-",
+ "U": "••-",
+ "V": "•••-",
+ "W": "•--",
+ "X": "-••-",
+ "Y": "-•--",
+ "Z": "--••",
+ },
+ "CYRILLIC": {
+ "А": "•-",
+ "Б": "-•••",
+ "В": "•--",
+ "Г": "••••",
+ "Д": "-••",
+ "Е": "•",
+ "Ж": "•••-",
+ "З": "--••",
+ "И": "-•--",
+ "Й": "•---",
+ "К": "-•-",
+ "Л": "•-••",
+ "М": "--",
+ "Н": "-•",
+ "О": "---",
+ "П": "•--•",
+ "Р": "•-•",
+ "С": "•••",
+ "Т": "-",
+ "У": "••-",
+ "Ф": "••-•",
+ "Х": "----",
+ "Ц": "-•-•",
+ "Ч": "---•",
+ "Ш": "--•-",
+ "Щ": "--•--",
+ "Ь": "-••-",
+ "Ю": "••--",
+ "Я": "•-•-",
+ },
+ "UA": {
+ "Ґ": "--•",
+ "Є": "••-••",
+ "І": "••",
+ "Ї": "•---•",
+ },
+ "RU": {
+ "Ъ": "•--•-•",
+ "Ы": "-•---",
+ "Э": "•••-•••",
+ "Ё": "•---•-",
+ },
+ "OTHER": {
+ "1": "•----",
+ "2": "••---",
+ "3": "•••--",
+ "4": "••••-",
+ "5": "•••••",
+ "6": "-••••",
+ "7": "--•••",
+ "8": "---••",
+ "9": "----•",
+ "0": "-----",
+ ",": "•-•-•-",
+ ".": "••••••",
+ "?": "••--••",
+ "/": "-••-•",
+ "-": "-••••-",
+ "!": "--••--",
+ ":": "---•••",
+ ";": "-•-•-•",
+ "ʼ": "-••••-",
+ '"': "•-••-•",
+ "@": "•--•-•-",
+ "$": "•••-••-",
+ "(": "-•--•",
+ ")": "-•--•-",
+ " ": " ",
+ },
+}
diff --git a/bot/keywords/keyword_utils/morse_encryption_decryption.py b/bot/keywords/keyword_utils/morse_encryption_decryption.py
new file mode 100644
index 0000000..ff052da
--- /dev/null
+++ b/bot/keywords/keyword_utils/morse_encryption_decryption.py
@@ -0,0 +1,32 @@
+from typing import Dict, List
+
+
+class MorseCoder:
+ EMPTY_SPACE: str = " "
+ EMPTY_STRING: str = ""
+ UNKNOWN_SYMBOL: str = "�"
+
+ def encrypt(self, text_data: str, morse_alphabet: Dict[str, str]) -> str:
+ text_data: str = text_data.upper()
+
+ encrypted_message = [
+ self.EMPTY_SPACE
+ if char == self.EMPTY_SPACE
+ else (morse_alphabet.get(char, self.UNKNOWN_SYMBOL) + self.EMPTY_SPACE)
+ for char in text_data
+ ]
+ return "".join(encrypted_message).strip()
+
+ def decrypt(self, encrypted_data: str, morse_alphabet: Dict[str, str]) -> str:
+ encrypted_data_list: List[str] = encrypted_data.split(self.EMPTY_SPACE)
+
+ decrypted_message = [
+ self.EMPTY_SPACE
+ if enc_char == self.EMPTY_STRING
+ else next(
+ (key for key, val in morse_alphabet.items() if val == enc_char),
+ self.UNKNOWN_SYMBOL.strip(),
+ )
+ for enc_char in encrypted_data_list
+ ]
+ return "".join(decrypted_message)
diff --git a/bot/keywords/keyword_utils/morse_language_selector.py b/bot/keywords/keyword_utils/morse_language_selector.py
new file mode 100644
index 0000000..0986c28
--- /dev/null
+++ b/bot/keywords/keyword_utils/morse_language_selector.py
@@ -0,0 +1,40 @@
+from typing import Dict
+
+from .morse_alphabet import MORSE_CODE_DICT
+
+
+class MorseAlphabetCreator:
+ ENGLISH: str = "EN"
+ CYRILLIC_DEFAULT: str = "CYRILLIC"
+ UKRAINIAN: str = "UA"
+ RUSSIAN: str = "RU"
+ OTHER: str = "OTHER"
+
+ def __init__(self):
+ self.language = None
+
+ def alphabet_selector(self, user_input: str) -> Dict[str, str]:
+ data = user_input.upper()
+ data_list = [char for char in data]
+
+ english_alphabet = self.alphabet_creator(self.ENGLISH)
+ ukrainian_alphabet = self.alphabet_creator(self.UKRAINIAN)
+ russian_alphabet = self.alphabet_creator(self.RUSSIAN)
+
+ if all(char in ukrainian_alphabet for char in data_list):
+ return ukrainian_alphabet
+ elif all(char in russian_alphabet for char in data_list):
+ return russian_alphabet
+ return english_alphabet
+
+ def alphabet_creator(self, language_choice: str) -> Dict[str, str]:
+ result_dict = {}
+
+ if language_choice == self.ENGLISH:
+ result_dict.update(MORSE_CODE_DICT[language_choice])
+ else:
+ result_dict.update(MORSE_CODE_DICT[self.CYRILLIC_DEFAULT])
+ result_dict.update(MORSE_CODE_DICT[language_choice])
+
+ result_dict.update(MORSE_CODE_DICT[self.OTHER])
+ return result_dict
diff --git a/bot/keywords/keyword_utils/morse_options.py b/bot/keywords/keyword_utils/morse_options.py
new file mode 100644
index 0000000..3481027
--- /dev/null
+++ b/bot/keywords/keyword_utils/morse_options.py
@@ -0,0 +1,47 @@
+import re
+from typing import Optional
+
+from bot.keywords.keyword_utils.morse_encryption_decryption import MorseCoder
+from bot.keywords.keyword_utils.morse_language_selector import MorseAlphabetCreator
+
+DEFAULT_DOT_SYMBOL = "."
+PRETTY_DOT_SYMBOL = "•"
+DEFAULT_DASH_SYMBOL = "-"
+PRETTY_DASH_SYMBOL = "—"
+
+
+def decoding(user_input: str, morse_data: MorseAlphabetCreator, morse_coder: MorseCoder, language: str) -> str:
+ language_decryption = morse_data.alphabet_creator(language)
+ return morse_coder.decrypt(user_input, language_decryption)
+
+
+def get_option(
+ decryption_match: Optional[re.Match[str]],
+ user_input: str,
+ morse_data: MorseAlphabetCreator,
+ morse_coder: MorseCoder,
+) -> str:
+ if decryption_match:
+ user_input = user_input.replace(DEFAULT_DOT_SYMBOL, PRETTY_DOT_SYMBOL)
+ user_input = user_input.replace(DEFAULT_DOT_SYMBOL, DEFAULT_DASH_SYMBOL)
+ english_result = decoding(user_input, morse_data, morse_coder, morse_data.ENGLISH)
+ ukrainian_result = decoding(user_input, morse_data, morse_coder, morse_data.UKRAINIAN)
+ russian_result = decoding(user_input, morse_data, morse_coder, morse_data.RUSSIAN)
+
+ return "".join(
+ f"ENGLISH DECODING: {english_result}\n"
+ f"УКРАЇНСЬКЕ ДЕКОДУВАННЯ: {ukrainian_result}\n"
+ f"РУССКОЕ ДЕКОДИРОВАНИЕ: {russian_result}\n"
+ )
+
+ alphabet = morse_data.alphabet_selector(user_input)
+ return f"{morse_coder.encrypt(user_input, alphabet)}"
+
+
+def get_result(user_input: str) -> str:
+ morse_data = MorseAlphabetCreator()
+ morse_coder = MorseCoder()
+
+ decryption_pattern = re.compile("^[-—•.\\s]+$")
+ decryption_match = re.match(decryption_pattern, user_input)
+ return get_option(decryption_match, user_input, morse_data, morse_coder)
diff --git a/bot/keywords/keyword_utils/pdf_docx_converter.py b/bot/keywords/keyword_utils/pdf_docx_converter.py
new file mode 100644
index 0000000..c319ab2
--- /dev/null
+++ b/bot/keywords/keyword_utils/pdf_docx_converter.py
@@ -0,0 +1,116 @@
+import asyncio
+import os
+
+from typing import List, Tuple
+
+from pdf2docx import Converter
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions
+
+from bot.config import bot
+from bot.keywords.keyword_utils.image_to_file_converter import FileConverter, FileLoader
+
+
+class DocumentLoader(FileLoader):
+ async def save_files(self, file_ids: List[str], **kwargs) -> List[str]:
+ file_extension = kwargs["file_extension"]
+ document_list = await self._files_processing(
+ file_ids, file_extension=file_extension
+ )
+ return document_list
+
+ async def _files_processing(
+ self, file_ids: List[str], file_extension: str = None
+ ) -> List[str]:
+ file_path_list = []
+
+ for file_index, file_id in enumerate(file_ids):
+ file_info = await bot.get_file(file_id)
+ file_path = file_info.file_path
+ file = await bot.download_file(file_path)
+
+ filename = f"document_{file_index}.{file_extension}"
+ temp_file_path = os.path.join(self.temp_dir, filename)
+ with open(temp_file_path, "wb") as temp_file:
+ temp_file.write(file.getvalue())
+
+ file_path_list.append(temp_file_path)
+
+ return file_path_list
+
+
+class DocxToPdf(FileConverter):
+ LOAD_TIMEOUT = 10
+
+ async def convert(
+ self, conversion_file_path: str, paths: List[str], **kwargs
+ ) -> None:
+ await self._parse_page(conversion_file_path, paths)
+
+ async def _parse_page(self, conversion_file_path: str, paths: List[str]) -> None:
+ docx_file = os.path.join(conversion_file_path)
+ dir_name = paths[0]
+
+ chrome_options = webdriver.ChromeOptions()
+ chrome_options.add_experimental_option(
+ "prefs",
+ {
+ "download.default_directory": dir_name,
+ },
+ )
+ chrome_options.add_argument("--no-sandbox")
+ chrome_options.add_argument("--headless")
+ chrome_options.add_argument("--disable-gpu")
+
+ driver = webdriver.Chrome(options=chrome_options)
+ driver.get("https://smallpdf.com/word-to-pdf")
+
+ upload_file_button_xpath = "//input[@type='file']"
+
+ file_input = WebDriverWait(driver, self.LOAD_TIMEOUT).until(
+ expected_conditions.presence_of_element_located(
+ (By.XPATH, upload_file_button_xpath)
+ )
+ )
+ file_input.send_keys(docx_file)
+
+ download_file_button_xpath = "//div//span[contains(text(), 'Download')]"
+ file_output = WebDriverWait(driver, self.LOAD_TIMEOUT).until(
+ expected_conditions.element_to_be_clickable(
+ (By.XPATH, download_file_button_xpath)
+ )
+ )
+ file_output.click()
+ await asyncio.sleep(self.LOAD_TIMEOUT)
+ self.rename_pdf(docx_file)
+
+ @staticmethod
+ def rename_pdf(docx_file_path: str) -> None:
+ pdf_file_path = os.path.splitext(docx_file_path)[0] + ".pdf"
+ if os.path.exists(pdf_file_path):
+ new_pdf_file_path = os.path.join(
+ os.path.dirname(pdf_file_path), "result.pdf"
+ )
+ os.rename(pdf_file_path, new_pdf_file_path)
+
+ def compress(
+ self, image_file: str, compression_allowed: bool
+ ) -> Tuple[str, int, int] | str:
+ raise NotImplementedError
+
+
+class PdfToDocx(FileConverter):
+ def convert(self, conversion_file_path: str, paths: List[str], **kwargs) -> None:
+ pdf_file = os.path.join(conversion_file_path)
+ docx_file = os.path.join(paths[0], "result.docx")
+
+ conversion_file = Converter(pdf_file)
+ conversion_file.convert(docx_file)
+ conversion_file.close()
+
+ def compress(
+ self, image_file: str, compression_allowed: bool
+ ) -> Tuple[str, int, int] | str:
+ raise NotImplementedError
diff --git a/bot/other_functions/remove_start_keyword.py b/bot/keywords/keyword_utils/remove_start_keyword.py
similarity index 100%
rename from bot/other_functions/remove_start_keyword.py
rename to bot/keywords/keyword_utils/remove_start_keyword.py
diff --git a/bot/bot_main/bot_classes/WeatherInfo.py b/bot/keywords/keyword_utils/states.py
similarity index 55%
rename from bot/bot_main/bot_classes/WeatherInfo.py
rename to bot/keywords/keyword_utils/states.py
index 9750bdd..6fb9252 100644
--- a/bot/bot_main/bot_classes/WeatherInfo.py
+++ b/bot/keywords/keyword_utils/states.py
@@ -1,5 +1,5 @@
from aiogram.dispatcher.filters.state import StatesGroup, State
-class WeatherInfo(StatesGroup):
- place = State()
+class ImageSaver(StatesGroup):
+ image = State()
diff --git a/bot/keywords/keywords_for_interaction.py b/bot/keywords/keywords_for_interaction.py
new file mode 100644
index 0000000..cb16f6e
--- /dev/null
+++ b/bot/keywords/keywords_for_interaction.py
@@ -0,0 +1,343 @@
+import asyncio
+import re
+from datetime import datetime, timedelta
+
+from aiogram import types
+from aiogram.dispatcher import FSMContext
+from aiogram.dispatcher.filters import IsReplyFilter
+from aiogram.types import ContentType, InputFile
+
+from bot.commands.commands_utils.for_photo_creation.remake_user_photo import (
+ create_new_photo_auto_config,
+)
+from .keyword_utils.states import ImageSaver
+from ..config import (
+ dp,
+ bot,
+ unique_table,
+)
+from ..commands.commands_utils import currency_cost as cc
+from .keyword_utils.check_date_words import check_day
+from .keyword_utils.check_document_extension import (
+ file_option_selector,
+ parse_args,
+ get_conversion_path_name,
+)
+from .keyword_utils.file_size_contoller import send_documents
+from bot.commands.commands_utils.get_days_and_date_num import (
+ extract_from_user_input_days_and_date,
+ extract_from_user_input_days_num,
+)
+from .keyword_utils.image_to_file_converter import (
+ ImageLoader,
+ PDFConverter,
+ DOCXConverter,
+)
+from .keyword_utils.morse_options import get_result
+from .keyword_utils.remove_start_keyword import (
+ remove_mem_from_start,
+ remove_rand_mem_from_start,
+)
+
+
+# from bot.other_functions.image_to_file_converter import ImageQualityReducer
+
+# reducer = ImageQualityReducer("test.jpg")
+# reducer.reduce(100, "jpg")
+
+
+# @dp.message_handler(IsReplyFilter(True), regexp="^audio$|^аудіо$", content_types=ContentType.AUDIO)
+# async def process_audio(message: types.Message):
+# print(message.audio)
+# if message.reply_to_message.audio:
+# print(message.reply_to_message.audio)
+# else:
+# print("BEBRA")
+
+
+@dp.message_handler(regexp="^morsec|^морзек")
+async def morse_encryptor(message: types.Message):
+ main_regex_len = 6
+ user_input = message.text
+
+ if len(user_input) == main_regex_len:
+ return
+
+ fixed_input = user_input[main_regex_len:].strip()
+ result = get_result(fixed_input)
+ await message.reply(text=result, parse_mode="HTML")
+
+
+@dp.message_handler(regexp="^dtr delete tasks$|^дтр видали завдання$")
+async def tasks_delete(message: types.Message):
+ user_id = message.from_id
+ unique_table.drop_remake_task_table(user_id)
+ await message.reply(text="All tasks successfully deleted!!!")
+
+
+@dp.message_handler(regexp="^dtr delete passwords$|^дтр видали паролі$")
+async def passwords_delete(message: types.Message):
+ user_id = message.from_id
+ unique_table.drop_remake_password_table(user_id)
+ await message.reply(text="All passwords successfully deleted!!!")
+
+
+@dp.message_handler(regexp="^location$|^місцезнаходження$")
+async def bot_location(message: types.Message):
+ await bot.send_location(message.chat.id, latitude=49.924394, longitude=27.746868)
+
+
+@dp.message_handler(regexp="^bitcoin$|^біткоін$|^біток$")
+async def bitcoin_price(message: types.Message):
+ await message.reply(
+ f'Bitcoin price right now: {cc.bitcoin_price():.2f} $\n',
+ parse_mode="HTML",
+ )
+
+
+@dp.message_handler(regexp="^dtr example$|^дтр приклад$")
+async def help_with_photo(message: types.Message):
+ await message.reply(
+ "Below you can see examples how to use command /photo\n\n"
+ 'example - only one argument. Text "example" '
+ "will be printed in the top left corner, font size = 20.\n\n"
+ 'example//40 - two arguments. Text "example" '
+ "will be printed in the top left corner, font size = 40.\n\n"
+ "100//200 - two arguments. Resize photo: width = 100, height = 200\n\n"
+ 'example//100//200 - three arguments. Text "example" '
+ "will be printed in position where coordinate X = 100 "
+ "and coordinate Y = 200, font size = 20.\n\n"
+ 'example//100//200//40 - four arguments. Text "example" '
+ "will be printed in position where coordinate X = 100 "
+ "and coordinate Y = 200, font size = 40.\n\n"
+ 'example//100//200//1000//2000//50 - six arguments. Text "example" '
+ "will be printed in position where coordinate X = 100 "
+ "and coordinate Y = 200. New width will be equal to 1000 and height - 2000, font size = 50.\n",
+ parse_mode="HTML",
+ )
+
+
+@dp.message_handler(regexp="^random mem|^рандом мем", content_types=ContentType.PHOTO)
+async def send_auto_config_photo_with_rand_text_clr(message: types.Message):
+ photo = message.photo[-1]
+ await photo.download(destination_file="imgs/test_auto_conf.jpg")
+ get_user_photo_caption = message.caption
+ formatted_text = remove_rand_mem_from_start(get_user_photo_caption)
+
+ create_new_photo_auto_config(False, formatted_text)
+
+ result_photo = InputFile("imgs/result_auto_conf.jpg")
+ await bot.send_photo(message.chat.id, photo=result_photo)
+
+
+@dp.message_handler(regexp="^mem|^мем", content_types=ContentType.PHOTO)
+async def send_auto_config_photo_with_text(message: types.Message):
+ photo = message.photo[-1]
+ await photo.download(destination_file="imgs/test_auto_conf.jpg")
+ get_user_photo_caption = message.caption
+ formatted_text = remove_mem_from_start(get_user_photo_caption)
+
+ create_new_photo_auto_config(True, formatted_text)
+
+ result_photo = InputFile("imgs/result_auto_conf.jpg")
+ await bot.send_photo(message.chat.id, photo=result_photo)
+
+
+@dp.message_handler(
+ IsReplyFilter(True),
+ regexp=re.compile(
+ "^reduce\\s*([1-9][0-9]?|100)?$|^знизити\\s*([1-9][0-9]?|100)?$", re.IGNORECASE
+ ),
+)
+async def reduce_image(message: types.Message):
+ replied_message = message.reply_to_message.content_type
+ allowed_content = (ContentType.PHOTO, ContentType.DOCUMENT)
+
+ if replied_message not in allowed_content:
+ return None
+
+ print(message.reply_to_message)
+
+
+@dp.message_handler(content_types=[ContentType.PHOTO, ContentType.DOCUMENT])
+async def image_pdf_converter_filter(message: types.Message, state: FSMContext):
+ first_photo = file_option_selector(message)
+ if not first_photo:
+ return
+
+ await state.update_data(
+ photo_0=first_photo,
+ photo_counter=0,
+ allowed_message_id_list=[message.message_id],
+ )
+ await ImageSaver.image.set()
+ print(await state.get_data())
+
+
+@dp.message_handler(content_types=[ContentType.PHOTO, ContentType.DOCUMENT], state=ImageSaver.image)
+async def image_pdf_converter_helper(message: types.Message, state: FSMContext):
+ photo = file_option_selector(message)
+ print(await state.get_data())
+ if not photo:
+ await state.finish()
+ return
+
+ async with state.proxy() as data:
+ data["photo_counter"] += 1
+ photo_counter = data["photo_counter"]
+ data[f"photo_{photo_counter}"] = photo
+ data[f"allowed_message_id_list"].append(message.message_id)
+
+ if data["photo_counter"] > 20:
+ await state.finish()
+
+
+@dp.message_handler(
+ IsReplyFilter(True),
+ regexp=re.compile(
+ "^pdf\\s*([1-9][0-9]?|100)?$|^пдф\\s*([1-9][0-9]?|100)?$", re.IGNORECASE
+ ),
+ state=ImageSaver.image,
+)
+async def image_pdf_converter_end_state_handler(
+ message: types.Message, state: FSMContext
+):
+ state_data = await state.get_data()
+ user_message_ids = state_data["allowed_message_id_list"]
+ check_message_id = message.reply_to_message.message_id
+ replied_message = message.reply_to_message.content_type
+ allowed_content = (ContentType.PHOTO, ContentType.DOCUMENT)
+
+ if replied_message in allowed_content and check_message_id in user_message_ids:
+ images_server_ids = [
+ state_data[key]["file_id"]
+ for key in state_data
+ if key.startswith("photo") and not isinstance(state_data[key], int)
+ ]
+ file_loader = ImageLoader(message, "images")
+ image_path_list = await file_loader.save_files(images_server_ids)
+
+ bot_message = await message.reply("PDF and DOCX conversion started...")
+
+ compression_level = parse_args(message, "pdf")
+ print(compression_level)
+ user_id = message.from_user.id
+ conversion_pdf_path = get_conversion_path_name(
+ "images", user_id, "pdf", filename="result"
+ )
+ pdf_converter = PDFConverter()
+ pdf_converter.convert(
+ conversion_pdf_path, image_path_list, compression_level=compression_level
+ )
+
+ conversion_docx_path = get_conversion_path_name(
+ "images", user_id, "docx", filename="result"
+ )
+ docx_converter = DOCXConverter()
+ docx_converter.convert(
+ conversion_docx_path, image_path_list, compression_level=compression_level
+ )
+
+ bot_message_data = await send_documents(
+ message, conversion_pdf_path, conversion_docx_path
+ )
+ await bot_message.edit_text(text=bot_message_data)
+
+ file_loader.remove_temp_dir()
+ await state.finish()
+
+
+@dp.message_handler(state="*", regexp="^clear states$|^очистити стани$")
+async def clear_states(message: types.Message, state: FSMContext):
+ bot_message = await message.reply("Clearing states...")
+ await asyncio.sleep(0.5)
+ await state.finish()
+ await bot.edit_message_text(
+ "States are cleared.",
+ chat_id=message.chat.id,
+ message_id=bot_message.message_id,
+ )
+
+
+@dp.message_handler(IsReplyFilter(True), regexp="^msgd$|пвдв$")
+async def delete_two_messages(message: types.Message):
+ if message.reply_to_message.from_user.id == bot.id:
+ replied_msg_id = message.reply_to_message.message_id
+ await bot.delete_message(chat_id=message.chat.id, message_id=replied_msg_id)
+ await message.delete()
+
+
+@dp.message_handler(
+ regexp=re.compile(
+ "^(Яка дата|Який день) буде через [1-9]+[0-9]* (день|днів|дня)[?]*$|"
+ "^What (date|day) will be after [1-9]+[0-9]* (day|days)[?]*$",
+ re.IGNORECASE,
+ )
+)
+async def find_date_after_days_from_current_date(message: types.Message):
+ user_input = message.text
+ extracted_days = extract_from_user_input_days_num(user_input)
+
+ today = datetime.now()
+ answer = today + timedelta(days=extracted_days)
+ format_answer = answer.strftime("%d.%m.%Y")
+
+ await message.reply(
+ f"After {extracted_days} {check_day(extracted_days)} date will be {format_answer}!"
+ )
+
+
+@dp.message_handler(
+ regexp=re.compile(
+ "^(Яка дата|Який день) був [1-9]+[0-9]* (день|днів|дня) тому[?]*$|"
+ "^What (date|day) was (it )?[1-9]+[0-9]* (day|days) ago[?]*$",
+ re.IGNORECASE,
+ )
+)
+async def find_date_before_days_from_current_date(message: types.Message):
+ user_input = message.text
+ extracted_days = extract_from_user_input_days_num(user_input)
+
+ today = datetime.now()
+ answer = today - timedelta(days=extracted_days)
+ format_answer = answer.strftime("%d.%m.%Y")
+
+ await message.reply(
+ f"{extracted_days} {check_day(extracted_days)} ago it was {format_answer}!"
+ )
+
+
+@dp.message_handler(
+ regexp=re.compile(
+ "^(Яка дата|Який день) буде через [1-9]+[0-9]* (день|днів|дня),* якщо починати з "
+ "([1-9]|0[1-9]|[1-2][0-9]|3[0-1]).([1-9]|0[1-9]|1[0-2]).([1-9]+.*[0-9]+)[?]*$|"
+ "^What (date|day) will be after [1-9]+[0-9]* (day|days) if we start from "
+ "([1-9]|0[1-9]|[1-2][0-9]|3[0-1]).([1-9]|0[1-9]|1[0-2]).([1-9]+.*[0-9]+)[?]*$",
+ re.IGNORECASE,
+ )
+)
+async def find_date_after_days(message: types.Message):
+ user_input = message.text
+ extracted_data = extract_from_user_input_days_and_date(user_input)
+ days_num = extracted_data[0]
+ month_day = extracted_data[1][0]
+ month = extracted_data[1][1]
+ year = extracted_data[1][2]
+
+ try:
+ user_date = datetime(day=month_day, month=month, year=year)
+ format_user_date = user_date.strftime("%d.%m.%Y")
+ answer = user_date + timedelta(days=days_num)
+ format_answer = answer.strftime("%d.%m.%Y")
+
+ await message.reply(
+ f"If start from {format_user_date} after "
+ f"{days_num} {check_day(days_num)}, date will be {format_answer}!"
+ )
+ except ValueError:
+ await message.reply(
+ f"Some data is entered incorrectly!!!\n\n"
+ f"I think the problem is the {month_day} (first number in your date) because "
+ f"the month you entered does not contain such day of the month.",
+ parse_mode="HTML",
+ )
diff --git a/bot/other_functions/cancel_states.py b/bot/other_functions/cancel_states.py
deleted file mode 100644
index 76c4648..0000000
--- a/bot/other_functions/cancel_states.py
+++ /dev/null
@@ -1,4 +0,0 @@
-async def cancel_all_states_if_they_were(state):
- current_state = await state.get_state()
- if current_state is not None:
- await state.finish()
diff --git a/bot/other_functions/check_date_words.py b/bot/other_functions/check_date_words.py
deleted file mode 100644
index 0ab6232..0000000
--- a/bot/other_functions/check_date_words.py
+++ /dev/null
@@ -1,25 +0,0 @@
-def check_year(years):
- if years == 1:
- years_message = 'year'
- else:
- years_message = 'years'
-
- return years_message
-
-
-def check_month(months):
- if months == 1:
- months_message = 'month'
- else:
- months_message = 'months'
-
- return months_message
-
-
-def check_day(days):
- if days == 1:
- days_message = 'day'
- else:
- days_message = 'days'
-
- return days_message
diff --git a/bot/other_functions/check_for_comma_and_dot.py b/bot/other_functions/check_for_comma_and_dot.py
deleted file mode 100644
index a3fa447..0000000
--- a/bot/other_functions/check_for_comma_and_dot.py
+++ /dev/null
@@ -1,26 +0,0 @@
-def check_user_id(message):
- user_id = ''
- if ', ' in message:
- user_id = message[:message.find(',')]
- elif ',' in message:
- user_id = message[:message.find(',')]
- elif '. ' in message:
- user_id = message[:message.find('.')]
- elif '.' in message:
- user_id = message[:message.find('.')]
-
- return user_id
-
-
-def check_user_data(message):
- user_data = ''
- if ', ' in message:
- user_data = message[message.find(',') + 2:]
- elif ',' in message:
- user_data = message[message.find(',') + 1:]
- elif '. ' in message:
- user_data = message[message.find('.') + 2:]
- elif '.' in message:
- user_data = message[message.find('.') + 1:]
-
- return user_data
diff --git a/bot/other_functions/check_length_keyboard.py b/bot/other_functions/check_length_keyboard.py
deleted file mode 100644
index abcc22c..0000000
--- a/bot/other_functions/check_length_keyboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-def keyboard_length_choice(user_choice) -> int:
- if user_choice == 'very_easy':
- return 10
- elif user_choice == 'easy':
- return 20
- elif user_choice == 'normal':
- return 30
- elif user_choice == 'hard':
- return 40
- elif user_choice == 'very_hard':
- return 50
- elif user_choice == 'unbreakable':
- return 100
diff --git a/bot/other_functions/check_radio_keyboard.py b/bot/other_functions/check_radio_keyboard.py
deleted file mode 100644
index 8fd8485..0000000
--- a/bot/other_functions/check_radio_keyboard.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from string import digits, ascii_letters
-
-from bot.bot_main.for_password_generation.generate_password import exclude_invalid_symblols_for_markup
-
-
-def keyboard_content_choice(user_choice) -> str:
- allowed_punctuation = exclude_invalid_symblols_for_markup()
- if user_choice == 'all_characters':
- return digits + ascii_letters + allowed_punctuation
- elif user_choice == 'only_letters':
- return ascii_letters
- elif user_choice == 'only_digits':
- return digits
- elif user_choice == 'letters_digits':
- return digits + ascii_letters
- elif user_choice == 'letters_signs':
- return ascii_letters + allowed_punctuation
- elif user_choice == 'digits_signs':
- return digits + allowed_punctuation
diff --git a/bot/other_functions/close_keyboard.py b/bot/other_functions/close_keyboard.py
deleted file mode 100644
index a7fbae9..0000000
--- a/bot/other_functions/close_keyboard.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from bot.other_functions.message_attribute_error import message_attribute_control
-
-
-async def close_keyboard(call):
- await message_attribute_control(call)
- await call.message.delete()
- await call.answer()
diff --git a/bot/other_functions/currency_cost.py b/bot/other_functions/currency_cost.py
deleted file mode 100644
index 60d3134..0000000
--- a/bot/other_functions/currency_cost.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import json
-from config import CURRENCY_URL, BITCOIN_URL
-from urllib.request import urlopen
-
-response = urlopen(CURRENCY_URL)
-data_json_list = json.loads(response.read())
-
-bitcoin_response = urlopen(BITCOIN_URL)
-bitcoin_json = json.loads(bitcoin_response.read())
-
-
-def find_dollars_buy_in_hryvnias() -> float:
- return 1 / float(data_json_list[1]['buy'])
-
-
-def find_euros_buy_in_hryvnias() -> float:
- return 1 / float(data_json_list[0]['buy'])
-
-
-def find_dollars_buy_in_euros() -> float:
- return float(data_json_list[0]['buy']) / float(data_json_list[1]['buy'])
-
-
-def find_hryvnias_buy_in_euros() -> float:
- return float(data_json_list[0]['buy'])
-
-
-def find_euros_buy_in_dollars() -> float:
- return float(data_json_list[1]['buy']) / float(data_json_list[0]['buy'])
-
-
-def find_hryvnias_buy_in_dollars() -> float:
- return float(data_json_list[1]['buy'])
-
-
-def find_dollars_sale_in_hryvnias() -> float:
- return 1 / float(data_json_list[1]['sale'])
-
-
-def find_euros_sale_in_hryvnias() -> float:
- return 1 / float(data_json_list[0]['sale'])
-
-
-def find_dollars_sale_in_euros() -> float:
- return float(data_json_list[0]['sale']) / float(data_json_list[1]['sale'])
-
-
-def find_hryvnias_sale_in_euros() -> float:
- return float(data_json_list[0]['sale'])
-
-
-def find_euros_sale_in_dollars() -> float:
- return float(data_json_list[1]['sale']) / float(data_json_list[0]['sale'])
-
-
-def find_hryvnias_sale_in_dollars() -> float:
- return float(data_json_list[1]['sale'])
-
-
-def bitcoin_price() -> float:
- return float(bitcoin_json['price'])
diff --git a/bot/other_functions/encryption_decryption.py b/bot/other_functions/encryption_decryption.py
deleted file mode 100644
index c1e7a03..0000000
--- a/bot/other_functions/encryption_decryption.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import base64
-
-
-def encrypt(data) -> str:
- encode = base64.b85encode(data.encode('UTF-16'))
- sub_start_symbols = (encode.decode()).split('|N', 1)[1] # -> into database
- return sub_start_symbols
-
-def decrypt(data) -> str:
- add_start_symbols = '|N' + data
- decode = base64.b85decode(add_start_symbols).decode('UTF-16') # -> from database
- return decode
diff --git a/bot/other_functions/extract_random_data.py b/bot/other_functions/extract_random_data.py
deleted file mode 100644
index 2fdb884..0000000
--- a/bot/other_functions/extract_random_data.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from random import choice
-
-
-def get_random_data(data_arg):
- result = choice(data_arg)
- return result[0]
diff --git a/bot/other_functions/get_days_and_date_num.py b/bot/other_functions/get_days_and_date_num.py
deleted file mode 100644
index fd2ef76..0000000
--- a/bot/other_functions/get_days_and_date_num.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import re
-
-
-def exctract_from_user_input_days_num(user_input):
- days_num_pattern = re.compile('[1-9]+[0-9]*')
-
- days_num = re.findall(days_num_pattern, user_input)
- return int(days_num[0])
-
-
-def exctract_from_user_input_days_and_date(user_input):
- days_num = exctract_from_user_input_days_num(user_input)
- date_pattern = re.compile('([1-9]|0[1-9]|[1-2][0-9]|3[0-1]).([1-9]|0[1-9]|1[0-2]).([1-9]+.*[0-9]+)')
-
- result_date = re.findall(date_pattern, user_input)
-
- int_list = []
- for date_element in result_date[0]:
- int_list.append(int(date_element))
- result_date = int_list
-
- return days_num, result_date
diff --git a/bot/other_functions/get_id_and_convert.py b/bot/other_functions/get_id_and_convert.py
deleted file mode 100644
index 05e1476..0000000
--- a/bot/other_functions/get_id_and_convert.py
+++ /dev/null
@@ -1,8 +0,0 @@
-def get_id_from_str(table_name):
- return int(
- ''.join(
- decimal
- for decimal in table_name
- if decimal.isdecimal()
- )
- )
diff --git a/bot/other_functions/work_with_json.py b/bot/other_functions/work_with_json.py
deleted file mode 100644
index b355dc5..0000000
--- a/bot/other_functions/work_with_json.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import os
-
-from bot.bot_main.main_objects_initialization import unique_table
-from bot.other_functions.write_password_data_to_json import write_to_json
-
-
-async def send_json(call):
- make_json_dir()
- user_id = call.from_user.id
- password_data_list = unique_table.select_pass_gen_table(f'pass_gen_table_{user_id}')
- write_to_json(user_id, password_data_list)
-
- with open(f'users_json_files/user_json_{user_id}.json', 'rb') as json_file:
- json_content = json_file.read()
-
- sent_message = await call.bot.send_document(
- chat_id=call.message.chat.id,
- document=('Passwords.json', json_content),
- caption=f'Json file for @{call.from_user.username}',
- parse_mode='HTML'
- )
-
- return sent_message
-
-
-def make_json_dir():
- path = f'users_json_files'
-
- if not os.path.exists(path):
- os.makedirs(path)
-
-
-def remove_json(call):
- path_to_json = f'users_json_files/user_json_{call.from_user.id}.json'
-
- if os.path.exists(path_to_json):
- os.remove(path_to_json)
diff --git a/bot/other_functions/write_password_data_to_json.py b/bot/other_functions/write_password_data_to_json.py
deleted file mode 100644
index caf209f..0000000
--- a/bot/other_functions/write_password_data_to_json.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import base64
-import os
-import json
-
-from bot.other_functions.encryption_decryption import decrypt
-
-NUMBER_OF_JSON_IDENTS = 12
-
-def write_to_json(user_id, table_rows):
- data = [
- {
- 'Password №': password_number,
- 'ID': password_data[0],
- 'Password description': password_data[1],
- 'Password': decrypt(password_data[2]),
- 'Length': password_data[3],
- 'Has repetitive?': password_data[4],
- }
- for password_number, password_data in enumerate(table_rows, 1)
- ]
-
- directory = 'users_json_files'
- file_path = os.path.join(directory, f'user_json_{user_id}.json')
-
- if not os.path.exists(directory):
- os.makedirs(directory)
-
- with open(file_path, 'w') as outfile:
- json.dump(data, outfile, indent=NUMBER_OF_JSON_IDENTS, ensure_ascii=False)
diff --git a/bot/parsing/parse_link.py b/bot/parsing/parse_link.py
deleted file mode 100644
index cf93dbe..0000000
--- a/bot/parsing/parse_link.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from youtubesearchpython import VideosSearch
-
-
-def parse_link(i: int, serch_term: str) -> str:
- videos_search = VideosSearch(f'{serch_term}', limit=i)
- return str(videos_search.result())
-
-
-def links_split(serch_term: str) -> list:
- list_of_links = []
- for i in range(1, 6):
- result_str = parse_link(i, serch_term)
- result_str = result_str.split("'link': ")[-1]
- result_str = result_str.split(",")[0]
- result_str = result_str.strip("'")
- list_of_links.append(result_str)
-
- return list_of_links
diff --git a/bot/parsing/parse_temprature.py b/bot/parsing/parse_temprature.py
deleted file mode 100644
index 2dfd74c..0000000
--- a/bot/parsing/parse_temprature.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import re
-import requests
-from bs4 import BeautifulSoup as Bs
-
-
-def parse_temp_at_time(url: str) -> str:
- r = requests.get(url)
- html = Bs(r.content, 'html.parser')
- time = html.select('.imgBlock p')
- temp_at_time = html.select('.today-temp')
-
- return time[0].text + ': ' + temp_at_time[0].text
-
-
-def parse_minmax_temp(url: str) -> str:
- r = requests.get(url)
- html = Bs(r.content, 'html.parser')
- min_temp = html.select('.min span')
- max_temp = html.select('.max span')
-
- return 'Min temprature: ' + min_temp[0].text + 'C\n' + \
- 'Max temprature: ' + max_temp[0].text + 'C\n'
-
-
-def parse_today_temp(url: str) -> list:
- r = requests.get(url)
- html = Bs(r.content, 'html.parser')
-
- today_temp = html.select('.temperature td')
-
- new_list = []
- for td in today_temp:
- new_list.append(td.text)
-
- return new_list
-
-
-def list_of_tuples(url: str) -> list:
- initial_list = parse_today_temp(url)
- new_list = [(initial_list[i - 1], initial_list[i]) for i in range(1, len(initial_list), 2)]
- return new_list
-
-
-def parse_avarage_precipitation_probability(url: str) -> int:
- r = requests.get(url)
- html = Bs(r.content, 'html.parser')
-
- precipitation_probability = html.select('tr:nth-child(8) td')
- count = 0
- temp = 0
- precipitation_probability_list = []
- for td in precipitation_probability:
- if td.text == '-':
- precipitation_probability_list.append(0)
- temp = temp
- else:
- precipitation_probability_list.append(int(td.text))
- temp += int(td.text)
- count += 1
-
- return max(precipitation_probability_list)
-
-
-def check_for_negative_temp(temprature: float) -> str:
- if temprature > 0:
- return '+' + str(temprature) + '°C'
- else:
- return str(temprature) + '°C'
-
-
-def find_avarage_temp_between_two(url: str) -> list:
- temp_lst = list_of_tuples(url)
- result_list = []
-
- for i in temp_lst:
- sum_of_two = 0
-
- for j in range(2):
- result = int(re.search(r'-*\d+', i[j]).group())
- sum_of_two += result
-
- result_list.append(check_for_negative_temp(sum_of_two / 2))
-
- return result_list
-
-
-def avarage_day_temp(url: str) -> str:
- avarage_temp = 0
- count = 0
- for i in parse_today_temp(url):
- avarage_temp += int(re.search(r'-*\d+', i).group())
- count += 1
-
- return 'Avarage temprature: ' + check_for_negative_temp(avarage_temp / count) + '\n\n'
diff --git a/config.py b/config.py
deleted file mode 100644
index 0fb3446..0000000
--- a/config.py
+++ /dev/null
@@ -1,56 +0,0 @@
-API_TOKEN = '' # add your Telegram BOT API into quotes
-
-UA_COMMAND_DESC_LIST = '''
-/start - перевірка роботи бота
-/random - генерація випадкового цілого числа
-/eugene - функція пошуку відео
-/token - генерація токенів
-/time - дата, поточний час та інша інформація стосовно часу
-/sticker - відправляє випадковий стікер
-/currency - конвертер валют
-/weather - погода в заданому населеному пункті
-/birthday - обрахунок днів до заданої дати
-/taskscheduler - планувальник завдань
-/photo - додавання тексту на фото, зміна розмірів фото
-/password - генератор випадкових паролів
-/keywords - список ключових слів, на які реагує бот
-/help - список команд
-'''
-
-EN_COMMAND_DESC_LIST = '''
-/start - check if bot is working
-/random - generate random integer number
-/eugene - youtube video search
-/token - token generation
-/time - current date, time and other time info
-/sticker - send a random sticker
-/currency - currency converter
-/weather - show the weather in the entered settlement
-/birthday - calculate number of days from current date to another
-/taskscheduler - create a list of tasks
-/photo - adding text to photo, change photo size
-/password - random password generator
-/keywords - list of keywords that the bot responds to
-/help - command list
-'''
-
-COMMAND_LIST = [
- '/start',
- '/random',
- '/eugene',
- '/id',
- '/time',
- '/sticker',
- '/currency',
- '/weather',
- '/birthday',
- '/taskscheduler',
- '/photo',
- '/password',
- '/keywords',
- '/help',
-]
-
-# CURRENCY_URL = 'https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5'
-CURRENCY_URL = 'https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=11'
-BITCOIN_URL = 'https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT'
diff --git a/imgs/result.jpg b/imgs/result.jpg
index 8cccfb8..14b2089 100644
Binary files a/imgs/result.jpg and b/imgs/result.jpg differ
diff --git a/imgs/result_auto_conf.jpg b/imgs/result_auto_conf.jpg
index 5b2261e..f325fe7 100644
Binary files a/imgs/result_auto_conf.jpg and b/imgs/result_auto_conf.jpg differ
diff --git a/imgs/test.jpg b/imgs/test.jpg
index 0214c5f..dbc351f 100644
Binary files a/imgs/test.jpg and b/imgs/test.jpg differ
diff --git a/imgs/test_auto_conf.jpg b/imgs/test_auto_conf.jpg
index 2f90a69..229118c 100644
Binary files a/imgs/test_auto_conf.jpg and b/imgs/test_auto_conf.jpg differ
diff --git a/main.py b/main.py
index 31b7cb5..a386119 100644
--- a/main.py
+++ b/main.py
@@ -1,18 +1,10 @@
-from aiogram import executor
-
-from bot.bot_main import main_objects_initialization
-from bot.bot_main.commands_and_keywords import main_commands, random_numbers_generation_command, user_info_command, \
- sticker_command, keywords_command, time_commands, add_text_to_photo_command, weather_command, \
- youtube_search_command, currency_converter_command, task_scheduler_command, password_generator_command, \
- keywords_for_interaction
-
-bot = main_objects_initialization.bot
-dp = main_objects_initialization.dp
+from bot import MyBot
def main():
- executor.start_polling(dp, skip_updates=True)
+ custom_bot = MyBot()
+ custom_bot.start()
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/password_generator_app/Password_Generator_Linux_MacOS.zip b/password_generator_app/Password_Generator_Linux_MacOS.zip
deleted file mode 100644
index 0292fbb..0000000
Binary files a/password_generator_app/Password_Generator_Linux_MacOS.zip and /dev/null differ
diff --git a/password_generator_app/Password_Generator_v2_Linux.zip b/password_generator_app/Password_Generator_v2_Linux.zip
new file mode 100644
index 0000000..b0fe929
Binary files /dev/null and b/password_generator_app/Password_Generator_v2_Linux.zip differ
diff --git a/requirements.txt b/requirements.txt
index a256816..9f6a42a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,12 +1,14 @@
aiogram==2.23.1
-beautifulsoup4==4.11.1
-mysql-connector-python==8.0.31
+mysql-connector-python==8.1.0
mysql==0.0.3
-mysqlclient==2.1.1
-Pillow==9.3.0
-pip==22.3.1
-urllib3==1.26.13
-requests==2.28.1
-youtube-search-python==1.6.6
-config~=0.5.1
-pytz~=2022.7
\ No newline at end of file
+mysqlclient==2.2.0
+urllib3>=2.0.6
+Pillow>=10.0.1
+requests==2.31.0
+python-dotenv~=1.0.0
+beautifulsoup4~=4.12.2
+reportlab~=4.0.6
+python-docx~=1.0.0
+# youtube-search-python==1.6.6
+# pdf2docx~=0.5.6
+# selenium~=4.13.0