init
This commit is contained in:
commit
0aa2d09672
16
.editorconfig
Normal file
16
.editorconfig
Normal 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
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
5
HISTORY.md
Normal file
5
HISTORY.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 📑 История версий
|
||||
|
||||
## ...
|
||||
|
||||
...
|
24
LICENSE
Normal file
24
LICENSE
Normal 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>
|
2
TODO.md
Normal file
2
TODO.md
Normal file
@ -0,0 +1,2 @@
|
||||
- [ ] `podman` контейнер
|
||||
- [ ] command 4 stop requests
|
38
app/__init__.py
Normal file
38
app/__init__.py
Normal 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
29
app/__main__.py
Normal 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
34
app/actions.py
Normal 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
77
app/db.py
Normal 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
42
app/methods.py
Normal 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
BIN
assets/caress.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 179 KiB |
BIN
assets/gift.jpg
Normal file
BIN
assets/gift.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
BIN
assets/hello.jpg
Normal file
BIN
assets/hello.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 216 KiB |
BIN
assets/poo.jpg
Normal file
BIN
assets/poo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 KiB |
9
env.example
Normal file
9
env.example
Normal 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
3
pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[tool.black]
|
||||
line-length = 123
|
||||
skip-string-normalization = 1
|
14
sql/issues.sql
Normal file
14
sql/issues.sql
Normal 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;
|
14
systemd/user/feedback.service
Normal file
14
systemd/user/feedback.service
Normal 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
|
Loading…
Reference in New Issue
Block a user