GIGACOMMIT
Changes: Frontend: - Added and adjusted command handlers for /start, /cat, /subscribe, /unsubscribe, and /subscription_modify - Implemented mass send function to send images for all subscribed users - Configured scheduler to call function above at 12:00 every day - Added psycopg2 error exception for each handler Backend: - Changed PostgreSQL DB Users table structure(id, chat_id, images_amount) and adjusted all functions' arguments and executes to match these changes - Added a function to fetch selected images_amount for a set chat_id - Slightly optimized GetObjectExtension function - Allowed all functions that work with currentDay to accept int values - Added a script for batch population of MinIO bucket with images from ./files/<day number>/ directory Src: - Updated requirements.txt
This commit is contained in:
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -5,10 +5,11 @@ from src import config
|
|||||||
def get_last_id(cursor):
|
def get_last_id(cursor):
|
||||||
cursor.execute("SELECT MAX(id) FROM Users")
|
cursor.execute("SELECT MAX(id) FROM Users")
|
||||||
id = cursor.fetchall()[0][0]
|
id = cursor.fetchall()[0][0]
|
||||||
if id == None:
|
if id is None:
|
||||||
return 0
|
return 0
|
||||||
return id
|
return id
|
||||||
|
|
||||||
|
|
||||||
def set_connection():
|
def set_connection():
|
||||||
connection = psycopg2.connect(
|
connection = psycopg2.connect(
|
||||||
dbname = config.db_name,
|
dbname = config.db_name,
|
||||||
@ -20,24 +21,35 @@ def set_connection():
|
|||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
return cursor, connection
|
return cursor, connection
|
||||||
|
|
||||||
|
|
||||||
def close_connection(connection, cursor):
|
def close_connection(connection, cursor):
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
#Functions don't close connection automatically, it has to be closed manually
|
#Functions don't close connection automatically, it has to be closed manually
|
||||||
def add_user(username, chat_id, connection, cursor):
|
def add_user(chat_id, connection, cursor):
|
||||||
cursor.execute("INSERT INTO Users VALUES (%s, %s, %s);", (get_last_id(cursor) + 1, username, chat_id))
|
cursor.execute("INSERT INTO Users VALUES (%s, %s, %s);", (get_last_id(cursor) + 1, chat_id, 1))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
def delete_user(username, connection, cursor):
|
|
||||||
cursor.execute("DELETE FROM Users WHERE username = %s;", (username,))
|
def delete_user(chat_id, connection, cursor):
|
||||||
|
cursor.execute("DELETE FROM Users WHERE chat_id = %s;", (chat_id,))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
def change_name(old_username, new_username, connection, cursor):
|
|
||||||
cursor.execute("UPDATE Users SET username = %s WHERE username = %s;", (new_username, old_username))
|
def change_images_amount(chat_id, amount, connection, cursor):
|
||||||
|
cursor.execute('UPDATE Users SET images_amount = %s WHERE chat_id = %s;', (amount, chat_id))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def get_images_amount(chat_id, connection, cursor):
|
||||||
|
cursor.execute('SELECT images_amount FROM Users WHERE chat_id = %s;', (chat_id,))
|
||||||
|
images_amount = cursor.fetchall()[0][0]
|
||||||
|
return images_amount
|
||||||
|
|
||||||
|
|
||||||
def get_chat_id(id, cursor):
|
def get_chat_id(id, cursor):
|
||||||
cursor.execute("SELECT chatid FROM Users WHERE id = %s", (id,))
|
cursor.execute("SELECT chat_id FROM Users WHERE id = %s", (id,))
|
||||||
chat_id = cursor.fetchall()[0][0]
|
chat_id = cursor.fetchall()[0][0]
|
||||||
return chat_id
|
return chat_id
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from minio import Minio
|
from minio import Minio
|
||||||
from random import randint
|
from random import randint
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from src.Backend import DBwork
|
|
||||||
from src import config
|
from src import config
|
||||||
|
|
||||||
|
|
||||||
@ -14,38 +13,36 @@ def _setClient():
|
|||||||
)
|
)
|
||||||
return minio_client
|
return minio_client
|
||||||
|
|
||||||
|
|
||||||
def getNumberofObjects(client, currentDay):
|
def getNumberofObjects(client, currentDay):
|
||||||
objects = client.list_objects(config.bucket_name, prefix=currentDay+'/')
|
objects = client.list_objects(config.bucket_name, prefix=str(currentDay) + '/')
|
||||||
return sum(1 for _ in objects)
|
return sum(1 for _ in objects)
|
||||||
|
|
||||||
|
|
||||||
def getObjectExtension(client, currentDay, fileNumber):
|
def getObjectExtension(client, currentDay, fileNumber):
|
||||||
objects = client.list_objects(config.bucket_name, prefix=currentDay+'/')
|
objects = client.list_objects(config.bucket_name, prefix=str(currentDay) + '/')
|
||||||
for counter, obj in enumerate(objects, start=1):
|
counter = 0
|
||||||
|
object_extension = None
|
||||||
|
for obj in objects:
|
||||||
|
counter += 1
|
||||||
if counter == fileNumber:
|
if counter == fileNumber:
|
||||||
return obj.object_name.split('.')[-1]
|
object_extension = obj.object_name.split('.')[-1]
|
||||||
|
return object_extension
|
||||||
|
|
||||||
def getImageName(currentDay, client):
|
def getImageName(currentDay, client):
|
||||||
maxFiles = getNumberofObjects(client, currentDay)
|
maxFiles = getNumberofObjects(client, currentDay)
|
||||||
fileNumber = randint(1, maxFiles)
|
fileNumber = randint(1, maxFiles)
|
||||||
fileExtension = '.' + getObjectExtension(client, currentDay, fileNumber)
|
fileExtension = '.' + getObjectExtension(client, currentDay, fileNumber)
|
||||||
desiredFile = currentDay + '/' + str(fileNumber) + fileExtension
|
desiredFile = str(currentDay) + '/' + str(fileNumber) + fileExtension
|
||||||
return desiredFile
|
return desiredFile
|
||||||
|
|
||||||
|
|
||||||
def getDownloadURL(currentDay):
|
def getDownloadURL(currentDay):
|
||||||
client = _setClient()
|
client = _setClient()
|
||||||
object_name = getImageName(currentDay, client)
|
object_name = getImageName(currentDay, client)
|
||||||
url = client.presigned_get_object(
|
url = client.presigned_get_object(
|
||||||
config.bucket_name,
|
config.bucket_name,
|
||||||
object_name,
|
object_name,
|
||||||
expires=timedelta(days=1))
|
expires=timedelta(days=1)
|
||||||
|
)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def downloadForAll(currentDay):
|
|
||||||
cur, conn = DBwork.set_connection()
|
|
||||||
max_id = DBwork.get_last_id(cur)
|
|
||||||
for id in range(1, max_id + 1):
|
|
||||||
chat_id = DBwork.get_chat_id(id, cur)
|
|
||||||
image_URL = getDownloadURL(currentDay)
|
|
||||||
# await bot.send_photo(chat_id = chat_id, photo = image_URL
|
|
||||||
DBwork.close_connection(conn, cur)
|
|
||||||
|
|
||||||
|
|||||||
58
src/Backend/minioPopulator.py
Normal file
58
src/Backend/minioPopulator.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from minio import Minio
|
||||||
|
from minio.error import S3Error
|
||||||
|
from src import config
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
access_key = config.acc_key
|
||||||
|
secret_key = config.sec_key
|
||||||
|
bucket = config.bucket_name
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file(access_key, secret_key, bucket_name, object_name, file_path):
|
||||||
|
try:
|
||||||
|
client = Minio(
|
||||||
|
config.IS_address,
|
||||||
|
access_key = access_key,
|
||||||
|
secret_key = secret_key,
|
||||||
|
secure = False
|
||||||
|
)
|
||||||
|
if file_path.split('.')[-1] == 'jpeg':
|
||||||
|
client.fput_object(bucket_name, object_name, file_path, 'image/jpeg')
|
||||||
|
if file_path.split('.')[-1] == 'jpg':
|
||||||
|
client.fput_object(bucket_name, object_name, file_path, 'image/jpg')
|
||||||
|
elif file_path.split('.')[-1] == 'png':
|
||||||
|
client.fput_object(bucket_name, object_name, file_path, 'image/png')
|
||||||
|
elif file_path.split('.')[-1] == 'pjpeg':
|
||||||
|
client.fput_object(bucket_name, object_name, file_path, 'image/pjpeg')
|
||||||
|
print(f'image {object_name} with local path \'{file_path}\' was successfully uploaded')
|
||||||
|
|
||||||
|
except S3Error:
|
||||||
|
print(f'Error during MinIO operation: {S3Error}')
|
||||||
|
|
||||||
|
|
||||||
|
paths = glob.glob('../../files/*/*')
|
||||||
|
shortened_paths = []
|
||||||
|
|
||||||
|
for i in range(len(paths)):
|
||||||
|
paths[i] = os.path.abspath(paths[i]).replace('\\', '/')
|
||||||
|
|
||||||
|
for i in range(len(paths)):
|
||||||
|
shortened_paths.append(paths[i][paths[i].find('files/') + 6:])
|
||||||
|
|
||||||
|
# for i in range(0, len(paths)):
|
||||||
|
# upload_file(access_key, secret_key, bucket, f'{shortened_paths[i]}', f'{paths[i]}')
|
||||||
|
# print(paths[i])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
client = Minio(
|
||||||
|
config.IS_address,
|
||||||
|
access_key = access_key,
|
||||||
|
secret_key = secret_key,
|
||||||
|
secure = False
|
||||||
|
)
|
||||||
|
objects = client.list_objects(config.bucket_name, prefix='1' + '/')
|
||||||
|
for obj in objects:
|
||||||
|
print(obj.object_name)
|
||||||
@ -1,15 +1,21 @@
|
|||||||
|
import psycopg2
|
||||||
|
import Backend.ISwork, Backend.DBwork
|
||||||
from src import config
|
from src import config
|
||||||
import asyncio
|
from src.Backend import DBwork
|
||||||
|
from src.Backend import ISwork
|
||||||
import logging
|
import logging
|
||||||
from aiogram import Bot, Dispatcher, F, Router
|
from aiogram import Bot, Dispatcher, Router
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command, CommandObject
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message, URLInputFile
|
||||||
from aiogram.client.default import DefaultBotProperties
|
from aiogram.client.default import DefaultBotProperties
|
||||||
from aiogram.enums import ParseMode
|
from aiogram.enums import ParseMode
|
||||||
from aiogram.fsm.storage.memory import MemoryStorage
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
current_day = datetime.now().weekday()
|
||||||
|
|
||||||
start_router = Router()
|
start_router = Router()
|
||||||
scheduler = AsyncIOScheduler(timezone = 'Europe/Moscow')
|
scheduler = AsyncIOScheduler(timezone = 'Europe/Moscow')
|
||||||
|
|
||||||
@ -17,21 +23,95 @@ scheduler = AsyncIOScheduler(timezone = 'Europe/Moscow')
|
|||||||
logging.basicConfig(level = logging.INFO, format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
logging.basicConfig(level = logging.INFO, format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
bot = Bot(token = config.TG_TOKEN , default = DefaultBotProperties(parse_mode = ParseMode.HTML))
|
bot = Bot(token = config.TG_token , default = DefaultBotProperties(parse_mode = ParseMode.HTML))
|
||||||
dp = Dispatcher(storage = MemoryStorage())
|
dp = Dispatcher(storage = MemoryStorage())
|
||||||
|
|
||||||
|
|
||||||
@dp.message(Command('start'))
|
@dp.message(Command('start'))
|
||||||
async def cmd_start(message: Message):
|
async def cmd_start(message: Message):
|
||||||
await message.answer('Запуск сообщения по команде /start используя фильтр CommandStart()')
|
await message.answer('''
|
||||||
|
This is a bot that sends images of cats.
|
||||||
|
|
||||||
|
List of available commands:
|
||||||
|
/cat - request an image of a cat from today's pool of images
|
||||||
|
/subscribe - subscribe to daily cat images sent at 12:00 UTC+3
|
||||||
|
/subscription_modify <number> - change the amount of images sent daily
|
||||||
|
/unsubscribe - cancel your subscription(but why would you want to? :3)
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text)
|
@dp.message(Command('cat'))
|
||||||
async def basic_reaction(message: Message):
|
async def cmd_cat(message: Message):
|
||||||
if message.text != '':
|
image_link = URLInputFile(ISwork.getDownloadURL(current_day), filename=datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
|
||||||
await message.answer(message.text)
|
await message.answer_photo(image_link, caption='Look, a cat :3')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(Command('subscription_modify'))
|
||||||
|
async def subscription_modify(message: Message, command: CommandObject):
|
||||||
|
if command.args is None or command.args.isdigit() == False:
|
||||||
|
await message.answer('Please write the number of images you would like\n'
|
||||||
|
'to receive in the same message as a command.\n'
|
||||||
|
'Example:'
|
||||||
|
'/subscription_modify <number of daily images>')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
cursor, connection = DBwork.set_connection()
|
||||||
|
chat_id = message.chat.id
|
||||||
|
amount = command.args
|
||||||
|
DBwork.change_images_amount(chat_id, amount, connection, cursor)
|
||||||
|
DBwork.close_connection(connection, cursor)
|
||||||
|
except psycopg2.Error:
|
||||||
|
await message.answer('You are not yet subscribed.')
|
||||||
|
return
|
||||||
|
await message.answer('Amount of daily images was changed successfully!')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(Command('subscribe'))
|
||||||
|
async def cmd_subscribe(message: Message):
|
||||||
|
try:
|
||||||
|
cursor, connection = DBwork.set_connection()
|
||||||
|
chat_id = message.chat.id
|
||||||
|
DBwork.add_user(chat_id, connection, cursor)
|
||||||
|
DBwork.close_connection(connection, cursor)
|
||||||
|
except psycopg2.Error as e:
|
||||||
|
await message.answer('You are already subscribed.')
|
||||||
|
print(e.pgerror)
|
||||||
|
return
|
||||||
|
await message.answer('''
|
||||||
|
You have successfully subscribed to daily cat photos!
|
||||||
|
You will get 1 photo a day by default,
|
||||||
|
use /subscription_modify to change that amount.
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(Command('unsubscribe'))
|
||||||
|
async def cmd_unsubscribe(message: Message):
|
||||||
|
try:
|
||||||
|
cursor, connection = DBwork.set_connection()
|
||||||
|
chat_id = message.chat.id
|
||||||
|
DBwork.delete_user(chat_id, connection, cursor)
|
||||||
|
DBwork.close_connection(connection, cursor)
|
||||||
|
except psycopg2.Error:
|
||||||
|
await message.answer('You are not yet subscribed.')
|
||||||
|
return
|
||||||
|
await message.answer('You have successfully unsubscribed.')
|
||||||
|
|
||||||
|
|
||||||
|
async def send_daily_images():
|
||||||
|
cursor, connection = DBwork.set_connection()
|
||||||
|
max_id = DBwork.get_last_id(cursor)
|
||||||
|
for id in range(1, max_id + 1):
|
||||||
|
chat_id = DBwork.get_chat_id(id, cursor)
|
||||||
|
images_amount = DBwork.get_images_amount(chat_id, connection, cursor)
|
||||||
|
for _ in range(images_amount):
|
||||||
|
image_link = URLInputFile(ISwork.getDownloadURL(current_day), filename=datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
|
||||||
|
await bot.send_photo(chat_id = chat_id, photo = image_link)
|
||||||
|
DBwork.close_connection(connection, cursor)
|
||||||
|
|
||||||
|
|
||||||
|
scheduler.add_job(send_daily_images, 'cron', hour = 12, minute = 0)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
scheduler.start()
|
||||||
await dp.start_polling(bot)
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,9 @@ from decouple import config
|
|||||||
|
|
||||||
|
|
||||||
IS_address = config('IS_ADDRESS')
|
IS_address = config('IS_ADDRESS')
|
||||||
acc_key = config('ACC_KEY')
|
acc_key = config('MINIO_ACCESS_KEY')
|
||||||
sec_key = config('SEC_KEY')
|
sec_key = config('MINIO_SECRET_KEY')
|
||||||
|
root_user = config('MINIO_ROOT_USER')
|
||||||
db_name = config('DB_NAME')
|
db_name = config('DB_NAME')
|
||||||
postgres_user = config('POSTGRES_USER')
|
postgres_user = config('POSTGRES_USER')
|
||||||
postgres_password = config('POSTGRES_PASSWORD')
|
postgres_password = config('POSTGRES_PASSWORD')
|
||||||
@ -12,5 +13,5 @@ port = config('PORT')
|
|||||||
bucket_name = config('BUCKET_NAME')
|
bucket_name = config('BUCKET_NAME')
|
||||||
|
|
||||||
|
|
||||||
TG_TOKEN = config('TG_TOKEN')
|
TG_token = config('TG_TOKEN')
|
||||||
# ADMINS = [int(admin_id) for admin_id in config('ADMINS').split(',')]
|
# ADMINS = [int(admin_id) for admin_id in config('ADMINS').split(',')]
|
||||||
Reference in New Issue
Block a user