This commit is contained in:
Alexander Popov 2025-01-05 02:50:54 +03:00
commit 0aa2d09672
Signed by: iiiypuk
GPG Key ID: E47FE0AB36CD5ED6
19 changed files with 311 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.env

5
HISTORY.md Normal file
View File

@ -0,0 +1,5 @@
# 📑 История версий
## ...
...

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# ...
...

2
TODO.md Normal file
View File

@ -0,0 +1,2 @@
- [ ] `podman` контейнер
- [ ] command 4 stop requests

38
app/__init__.py Normal file
View File

@ -0,0 +1,38 @@
__author__ = 'Alexander Popov'
"""Автор программы"""
__version__ = '1.0.0'
"""Версия программы"""
# Импорт системных модулей
from os import getenv
# Импорт сторонних модулей
from dotenv import load_dotenv
from loguru import logger
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
# Импорт модулей приложения
from .db import DataBase
load_dotenv() # Выполяет загрузку переменных окружения из файла .env
db = DataBase(
getenv('DB_ADDR'),
getenv('DB_PORT'),
getenv('DB_NAME'),
getenv('DB_USER'),
getenv('DB_PWD'),
)
"""Экземпляр класса базы данных"""
logger.add(getenv('LOG_PATH'), compression='zip')
"""Логгер"""
dp = Dispatcher()
"""Диспетчер задач Telegram бота"""
bot = Bot(token=getenv('TOKEN'), default=DefaultBotProperties(parse_mode=ParseMode.MARKDOWN_V2))
"""Клиент Telegram бота"""

29
app/__main__.py Normal file
View File

@ -0,0 +1,29 @@
# Импорт сторонних модулей
import asyncio
# Импорт модулей приложения
from . import logger, db, dp, bot
from .methods import *
async def main() -> None:
# Подключение к базе данных
logger.info('Подключение к базе данных...')
status = db.connect()
if status == False:
logger.error('Ошибка подключения к базе данных...')
exit(-1)
else:
logger.info('Подключение к базе данных выполнено успешно!')
# Запуск Telegram бота
logger.info('Запуск Telegram бота...')
await dp.start_polling(bot)
logger.info('Отключение от базы данных...')
db.close()
if __name__ == '__main__':
asyncio.run(main())

34
app/actions.py Normal file
View File

@ -0,0 +1,34 @@
# Импорт сторонних модулей
from aiogram import F
from aiogram.types import CallbackQuery, FSInputFile
from aiogram.types import InputMediaPhoto
# Импорт модулей приложения
from . import dp
IMAGE = './assets/actions.jpg'
END_MSG = 'Попробуй выполнить команду `/start` ещё раз, чтобы посмотреть на другой результат 😉'
@dp.callback_query(F.data == 'action_gift')
async def action_gift(callback: CallbackQuery) -> None:
msg = 'Спасибо за подарок 🤭\n\n' 'Но я принимаю только ' '⛽ нефть ' '🥃 алкоголь ' ' и 🍫 шоколадки\n' '\n'
msg += END_MSG
await callback.message.edit_media(InputMediaPhoto(media=FSInputFile(path='./assets/gift.jpg'), caption=msg))
@dp.callback_query(F.data == 'action_poo')
async def action_poo(callback: CallbackQuery) -> None:
msg = 'Я увернулся 🫣\n\n'
msg += END_MSG
await callback.message.edit_media(InputMediaPhoto(media=FSInputFile(path='./assets/poo.jpg'), caption=msg))
@dp.callback_query(F.data == 'action_caress')
async def action_caress(callback: CallbackQuery) -> None:
msg = '🤤\n\n'
msg += END_MSG
await callback.message.edit_media(InputMediaPhoto(media=FSInputFile(path='./assets/caress.jpg'), caption=msg))

77
app/db.py Normal file
View File

@ -0,0 +1,77 @@
# Импорт сторонних модулей
import psycopg2
# Импорт модулей приложения
from . import logger
class DataBase(object):
"""..."""
def __init__(self, host: str, port: int, name: str, user: str, password: str):
super(DataBase, self).__init__()
self.host = host
self.port = port
self.name = name
self.user = user
self.password = password
self.conn = None
def connect(self) -> bool:
"""..."""
try:
self.conn = psycopg2.connect(
host=self.host,
port=self.port,
user=self.user,
password=self.password,
database=self.name,
)
return True
except:
return False
def check_issues_count(self, telegram_id: int) -> bool:
"""..."""
sql = 'SELECT id FROM issues WHERE telegram_id = %s;'
cur = self.conn.cursor()
cur.execute(sql, (telegram_id,))
result = cur.fetchall()
if result == None:
return 0
else:
return len(result)
def add_issue(self, telegram_id: int, message: str) -> dict:
"""..."""
sql = 'INSERT INTO issues ' '(telegram_id, message)' ' VALUES (%s, %s);'
total_issues = self.check_issues_count(telegram_id)
if total_issues >= 3:
return {
'status': False,
'message': '⛔ Произошла ошибка.\n' 'Вы оставили много запросов, чтобы я мог оперативно их обработать.',
}
cur = self.conn.cursor()
try:
cur.execute(sql, (telegram_id, message))
self.conn.commit()
return {'status': True, 'message': '🔔 Замечательно, Ваш запросов отправлен автору.\nЖдите ответа 🙃'}
except Exception as e:
logger.error(e)
return {'status': False, 'message': '⛔ Произошла ошибка'}
def close(self) -> None:
self.conn.close()

42
app/methods.py Normal file
View File

@ -0,0 +1,42 @@
# Импорт модулей стандартной библиотеки
from random import choice
# Импорт сторонних модулей
from aiogram import F
from aiogram.filters import CommandStart
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.types import Message, FSInputFile
# Импорт модулей приложения
from . import logger, db, dp
from .actions import *
@dp.message(CommandStart())
async def command_start_handler(message: Message) -> None:
msg = '👋🏻 Привет\\!\n\nОтправь сообщение боту и 🍪 *Печенька* в скором времени ответит тебе 🤭'
actions = [
[InlineKeyboardButton(text='🎁 Сделать подарок', callback_data='action_gift')],
[InlineKeyboardButton(text='💩 Сделать пакость', callback_data='action_poo')],
[InlineKeyboardButton(text='🤤 Погладить', callback_data='action_caress')],
]
await message.answer_photo(
photo=FSInputFile(path='./assets/hello.jpg'),
caption=msg,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[choice(actions)]),
)
@dp.message()
async def echo_handler(message: Message) -> None:
telegram_id = message.from_user.id
msg = message.text
# logger.info('Добавляем запрос от пользователя ...')
status = db.add_issue(telegram_id, msg)
msg = status['message'].replace('.', '\\.')
await message.answer(text=msg)

BIN
assets/caress.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
assets/gift.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
assets/hello.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

BIN
assets/poo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

9
env.example Normal file
View File

@ -0,0 +1,9 @@
TOKEN=""
LOG_PATH="/tmp/a.log"
DB_ADDR=""
DB_PORT=""
DB_NAME=""
DB_USER=""
DB_PWD=""

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[tool.black]
line-length = 123
skip-string-normalization = 1

14
sql/issues.sql Normal file
View File

@ -0,0 +1,14 @@
BEGIN;
-- CREATE TABLE "issues" ---------------------------------------
CREATE TABLE "public"."issues" (
"id" Serial NOT NULL,
"telegram_id" BigInt NOT NULL,
"message" Text NOT NULL,
"date" Timestamp Without Time Zone DEFAULT now() NOT NULL,
"reply" Boolean DEFAULT 'false' NOT NULL,
CONSTRAINT "unique_issues_id" UNIQUE( "id" ) );
;
-- -------------------------------------------------------------
COMMIT;

View File

@ -0,0 +1,14 @@
[Unit]
Description=...
After=network.target network-online.target
[Service]
Type=exec
Environment=APP_DIR=/home/user/feedback_bot
Environment=VIRTUAL_ENV=${APP_DIR}/venv
Environment=PYTHONPATH=${APP_DIR}
Environment=PATH=${VIRTUAL_ENV}/bin:$PATH
ExecStart=/bin/bash -c '${VIRTUAL_ENV}/bin/python3 -m app'
[Install]
WantedBy=multi-user.target