Docker build push
- Pushed a docker image - Changed some .env variables' names(check config.py) - Fixed minioPopulator.py - Added more NoneType checks for bot handlers
This commit is contained in:
16
.dockerignore
Normal file
16
.dockerignore
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#.dockerignore
|
||||||
|
# Gitea
|
||||||
|
.gitea
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.dockerignore
|
||||||
|
dockerfile
|
||||||
|
compose.yaml
|
||||||
|
compose.yml
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
example.env
|
||||||
|
|
||||||
|
minioPopulator.py
|
||||||
48
.gitea/workflows/docker-build-push.yaml
Normal file
48
.gitea/workflows/docker-build-push.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
name: Build and Push Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.frik.su
|
||||||
|
IMAGE_NAME: ${{ gitea.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Docker
|
||||||
|
run: curl -fsSL https://get.docker.com | sh
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract Docker tags from release
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=tag
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
182
.gitignore
vendored
182
.gitignore
vendored
@ -1,8 +1,178 @@
|
|||||||
.venv/
|
# ---> Python
|
||||||
.idea/
|
# Byte-compiled / optimized / DLL files
|
||||||
|
|
||||||
.env
|
|
||||||
.DS_Store
|
|
||||||
.vscode/
|
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# UV
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
#uv.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||||
|
.pdm.toml
|
||||||
|
.pdm-python
|
||||||
|
.pdm-build/
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
# Ruff stuff:
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# PyPI configuration file
|
||||||
|
.pypirc
|
||||||
|
|
||||||
|
# Other
|
||||||
test.py
|
test.py
|
||||||
|
|||||||
27
README.md
27
README.md
@ -2,19 +2,22 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
This is a simple (yet-to-be-made) telegram bot that sends photos of cats to a user, with an option to subscribe for daily cat photos. User will also be able to configure subscription cat pictures quantity.
|
This is a simple telegram bot that sends photos of cats to a user, with an option to subscribe for daily cat photos. User also is able to configure subscription cat pictures quantity.
|
||||||
|
|
||||||
## Authors
|
|
||||||
|
|
||||||
- [Ilya](https://git.frik.su/n0one)
|
### Bot uses:
|
||||||
- [Grisha](https://git.frik.su/zeroGRMh)
|
- MinIO object storage
|
||||||
- [Zhenya](https://git.frik.su/EugeneBee)
|
- PostgreSQL database
|
||||||
|
- Telegram API
|
||||||
|
- Docker container management platform
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
- API keys configuration
|
### Setup:
|
||||||
- Frontend
|
- Get your telegram API bot token
|
||||||
- Backend
|
- Copy compose.yml file and change environment variables as you need
|
||||||
- DB configuration
|
- `docker compose up`
|
||||||
- Documentation
|
- Enjoy your own instance of the bot!
|
||||||
- Licensing
|
|
||||||
|
|
||||||
|
### Uploading pictures to MinIO:
|
||||||
|
For more comfortable pictures uploading you can use `minioPopulator.py` script.
|
||||||
|
|||||||
70
compose.yml
Normal file
70
compose.yml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
catbot:
|
||||||
|
image: git.frik.su/n0one/catbot:latest
|
||||||
|
container_name: catbot-bot
|
||||||
|
environment:
|
||||||
|
TG_TOKEN: your_telegram_token
|
||||||
|
IS_address: minio:9000
|
||||||
|
MINIO_ACCESS_KEY: minio
|
||||||
|
MINIO_SECRET_KEY: minio
|
||||||
|
MINIO_ROOT_USER: minio
|
||||||
|
BUCKET_NAME: catbot
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
LOGGING_LEVEL: info
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
minio:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:latest
|
||||||
|
container_name: catbot-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
PGDATA: /var/lib/postgresql/data/pgdata
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- ./pgdata:/var/lib/postgresql/data/pgdata
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
tty: true
|
||||||
|
stdin_open: true
|
||||||
|
|
||||||
|
minio:
|
||||||
|
image: minio/minio:latest
|
||||||
|
container_name: catbot-minio
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
environment:
|
||||||
|
MINIO_ACCESS_KEY: minio
|
||||||
|
MINIO_SECRET_KEY: minio
|
||||||
|
MINIO_ROOT_USER: minio
|
||||||
|
MINIO_ROOT_PASSWORD: minio
|
||||||
|
MINIO_BROWSER: "on"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata:
|
||||||
|
driver: local
|
||||||
13
dockerfile
Normal file
13
dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends libpq-dev build-essential
|
||||||
|
|
||||||
|
COPY requirements.txt ./
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["python", "src/main.py"]
|
||||||
@ -1,9 +1,9 @@
|
|||||||
from minio import Minio
|
|
||||||
from minio.error import S3Error
|
|
||||||
from src import config
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from minio import Minio
|
||||||
|
from minio.error import S3Error
|
||||||
|
|
||||||
|
|
||||||
def upload_image(bucket_name, object_name, file_path):
|
def upload_image(bucket_name, object_name, file_path):
|
||||||
try:
|
try:
|
||||||
@ -24,6 +24,8 @@ def upload_image(bucket_name, object_name, file_path):
|
|||||||
print(f'Error during MinIO operation: {e}')
|
print(f'Error during MinIO operation: {e}')
|
||||||
|
|
||||||
|
|
||||||
|
# Main
|
||||||
|
|
||||||
print('Please enter your MinIO instance cloud address:')
|
print('Please enter your MinIO instance cloud address:')
|
||||||
IS_address = input()
|
IS_address = input()
|
||||||
print('Please enter MinIO access key:')
|
print('Please enter MinIO access key:')
|
||||||
@ -33,10 +35,6 @@ secret_key = input()
|
|||||||
print('Please enter bucket name:')
|
print('Please enter bucket name:')
|
||||||
bucket = input()
|
bucket = input()
|
||||||
|
|
||||||
# IS_address = config.IS_address
|
|
||||||
# access_key = config.acc_key
|
|
||||||
# secret_key = config.sec_key
|
|
||||||
# bucket = config.bucket_name
|
|
||||||
|
|
||||||
client = Minio(
|
client = Minio(
|
||||||
IS_address,
|
IS_address,
|
||||||
@ -82,11 +80,12 @@ for i in range(len(paths)):
|
|||||||
break
|
break
|
||||||
|
|
||||||
starting_weekday = 0
|
starting_weekday = 0
|
||||||
|
prev_obj_len = -1
|
||||||
for n in range(1, 8):
|
for n in range(1, 8):
|
||||||
objects = client.list_objects(config.bucket_name, prefix=str(n) + '/')
|
objects = client.list_objects(bucket, prefix=str(n) + '/')
|
||||||
obj_len = sum(1 for _ in objects)
|
obj_len = sum(1 for _ in objects)
|
||||||
if n == 1:
|
if n == 1:
|
||||||
if obj_len < sum(1 for _ in client.list_objects(config.bucket_name, prefix='7/')):
|
if obj_len < sum(1 for _ in client.list_objects(bucket, prefix='7/')):
|
||||||
starting_weekday = n - 1
|
starting_weekday = n - 1
|
||||||
elif prev_obj_len > obj_len:
|
elif prev_obj_len > obj_len:
|
||||||
starting_weekday = n - 1
|
starting_weekday = n - 1
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from src import config
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from src import config
|
||||||
|
|
||||||
logging_level = config.logging_level
|
logging_level = config.logging_level
|
||||||
logger.add(
|
logger.add(
|
||||||
@ -25,17 +25,18 @@ def get_last_id(cursor):
|
|||||||
def set_connection():
|
def set_connection():
|
||||||
try:
|
try:
|
||||||
connection = psycopg2.connect(
|
connection = psycopg2.connect(
|
||||||
dbname = config.db_name,
|
dbname = config.db_name,
|
||||||
user = config.postgres_user,
|
user = config.postgres_user,
|
||||||
password = config.postgres_password,
|
password = config.postgres_password,
|
||||||
host = config.host_name,
|
host = config.host_name,
|
||||||
port = config.port
|
port = config.port
|
||||||
)
|
)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
logger.info('Successfully set connection to the PostgreSQL DB')
|
logger.info('Successfully set connection to the PostgreSQL DB')
|
||||||
return cursor, connection
|
return cursor, connection
|
||||||
except psycopg2.Error as e:
|
except psycopg2.Error as e:
|
||||||
logger.error(f'Failed to set connection to the PostgreSQL DB: {e.pgerror}')
|
logger.error(f'Failed to set connection to the PostgreSQL DB: {e.pgerror}')
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def close_connection(connection, cursor):
|
def close_connection(connection, cursor):
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
from minio import Minio, S3Error
|
|
||||||
from random import randint
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from src import config
|
from random import randint
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
from minio import Minio, S3Error
|
||||||
|
|
||||||
|
from src import config
|
||||||
|
|
||||||
logging_level = config.logging_level
|
logging_level = config.logging_level
|
||||||
logger.add(
|
logger.add(
|
||||||
@ -17,9 +18,9 @@ logger.add(
|
|||||||
def _setClient():
|
def _setClient():
|
||||||
try:
|
try:
|
||||||
minio_client = Minio(
|
minio_client = Minio(
|
||||||
config.IS_address,
|
config.IS_address, # type: ignore
|
||||||
access_key = config.acc_key,
|
access_key = config.acc_key, # type: ignore
|
||||||
secret_key = config.sec_key,
|
secret_key = config.sec_key, # type: ignore
|
||||||
secure = False
|
secure = False
|
||||||
)
|
)
|
||||||
logger.info('Successfully set connection to the MinIO bucket')
|
logger.info('Successfully set connection to the MinIO bucket')
|
||||||
@ -50,20 +51,28 @@ def getImageName(currentDay, client):
|
|||||||
if maxFiles == 0:
|
if maxFiles == 0:
|
||||||
return None
|
return None
|
||||||
fileNumber = randint(1, maxFiles)
|
fileNumber = randint(1, maxFiles)
|
||||||
fileExtension = '.' + getObjectExtension(client, currentDay, fileNumber)
|
shortExtension = getObjectExtension(client, currentDay, fileNumber)
|
||||||
|
fileExtension = ''
|
||||||
|
if shortExtension is not None:
|
||||||
|
fileExtension = '.' + shortExtension
|
||||||
desiredFile = str(currentDay) + '/' + str(fileNumber) + fileExtension
|
desiredFile = str(currentDay) + '/' + str(fileNumber) + fileExtension
|
||||||
return desiredFile
|
return desiredFile
|
||||||
|
|
||||||
|
|
||||||
def getDownloadURL(currentDay):
|
def getDownloadURL(currentDay):
|
||||||
client = _setClient()
|
client = _setClient()
|
||||||
|
if client is None:
|
||||||
|
logger.error("Failed to set MinIO client")
|
||||||
|
return None
|
||||||
|
|
||||||
object_name = getImageName(currentDay, client)
|
object_name = getImageName(currentDay, client)
|
||||||
if object_name is None:
|
if object_name is None:
|
||||||
logger.error(f"Can't generate a URL: no files in current MinIO directory({currentDay})")
|
logger.error(f"Can't generate a URL: no files in current MinIO directory({currentDay})")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
url = client.presigned_get_object(
|
url = client.presigned_get_object(
|
||||||
config.bucket_name,
|
config.bucket_name, # type: ignore
|
||||||
object_name,
|
object_name,
|
||||||
expires=timedelta(days=1)
|
expires=timedelta(days=1)
|
||||||
)
|
)
|
||||||
return url
|
return url
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
-- Table: public.users
|
|
||||||
|
|
||||||
-- DROP TABLE IF EXISTS public.users;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.users
|
|
||||||
(
|
|
||||||
id integer NOT NULL DEFAULT nextval('users_id_seq'::regclass),
|
|
||||||
chat_id bigint NOT NULL,
|
|
||||||
images_amount bigint,
|
|
||||||
CONSTRAINT users_pkey PRIMARY KEY (id),
|
|
||||||
CONSTRAINT chat_id_unique UNIQUE (chat_id)
|
|
||||||
)
|
|
||||||
|
|
||||||
TABLESPACE pg_default;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.users
|
|
||||||
OWNER to postgres_user;
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
version: '3.9'
|
|
||||||
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:latest
|
|
||||||
container_name: postgres_container
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: postgres_user
|
|
||||||
POSTGRES_PASSWORD: postgres_password
|
|
||||||
POSTGRES_DB: postgres_db
|
|
||||||
PGDATA: /var/lib/postgresql/data/pgdata
|
|
||||||
ports:
|
|
||||||
- "5430:5432"
|
|
||||||
volumes:
|
|
||||||
- ./pgdata:/var/lib/postgresql/data/pgdata
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.50'
|
|
||||||
memory: 512M
|
|
||||||
reservations:
|
|
||||||
cpus: '0.25'
|
|
||||||
memory: 256M
|
|
||||||
command: >
|
|
||||||
postgres -c max_connections=1000
|
|
||||||
-c shared_buffers=256MB
|
|
||||||
-c effective_cache_size=768MB
|
|
||||||
-c maintenance_work_mem=64MB
|
|
||||||
-c checkpoint_completion_target=0.7
|
|
||||||
-c wal_buffers=16MB
|
|
||||||
-c default_statistics_target=100
|
|
||||||
healthcheck:
|
|
||||||
test: [ "CMD-SHELL", "pg_isready -U postgres_user -d postgres_db" ]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 5
|
|
||||||
restart: unless-stopped
|
|
||||||
tty: true
|
|
||||||
stdin_open: true
|
|
||||||
minio:
|
|
||||||
image: minio/minio:latest
|
|
||||||
ports:
|
|
||||||
- "9000:9000"
|
|
||||||
- "9001:9001"
|
|
||||||
environment:
|
|
||||||
MINIO_ACCESS_KEY: minioadmin
|
|
||||||
MINIO_SECRET_KEY: minioadmin
|
|
||||||
MINIO_ROOT_USER: minioadmin
|
|
||||||
MINIO_ROOT_PASSWORD: minioadmin
|
|
||||||
MINIO_BROWSER: "on"
|
|
||||||
volumes:
|
|
||||||
- ./data:/data
|
|
||||||
command: server /data --console-address ":9001"
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 20s
|
|
||||||
retries: 3
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
||||||
driver: local
|
|
||||||
@ -1,23 +1,29 @@
|
|||||||
from loguru import logger
|
|
||||||
import psycopg2
|
|
||||||
from src import config
|
|
||||||
from src.Backend import DBwork
|
|
||||||
from src.Backend import ISwork
|
|
||||||
from aiogram import Bot, Dispatcher
|
|
||||||
from aiogram.fsm.storage.memory import MemoryStorage
|
|
||||||
from aiogram.filters import Command, CommandObject
|
|
||||||
from aiogram.types import Message, URLInputFile, BotCommand, BotCommandScopeAllPrivateChats, BotCommandScopeAllGroupChats
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.methods import DeleteMyCommands
|
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
from aiogram import Bot, Dispatcher
|
||||||
|
from aiogram.client.default import DefaultBotProperties
|
||||||
|
from aiogram.enums import ParseMode
|
||||||
|
from aiogram.filters import Command, CommandObject
|
||||||
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
|
from aiogram.types import (
|
||||||
|
BotCommand,
|
||||||
|
BotCommandScopeAllGroupChats,
|
||||||
|
BotCommandScopeAllPrivateChats,
|
||||||
|
Message,
|
||||||
|
URLInputFile,
|
||||||
|
)
|
||||||
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from src import config
|
||||||
|
from src.Backend import DBwork, ISwork
|
||||||
|
|
||||||
current_day = datetime.now().weekday()
|
current_day = datetime.now().weekday()
|
||||||
|
|
||||||
scheduler = AsyncIOScheduler(timezone = 'Europe/Moscow')
|
scheduler = AsyncIOScheduler(timezone = 'Europe/Moscow')
|
||||||
|
|
||||||
|
|
||||||
logging_level = config.logging_level
|
logging_level = config.logging_level
|
||||||
logger.add(
|
logger.add(
|
||||||
"sys.stdout",
|
"sys.stdout",
|
||||||
@ -26,33 +32,46 @@ logger.add(
|
|||||||
level=logging_level
|
level=logging_level
|
||||||
)
|
)
|
||||||
|
|
||||||
bot = Bot(token = config.TG_token , default = DefaultBotProperties(parse_mode = ParseMode.HTML))
|
|
||||||
|
bot = Bot(token = config.TG_token , default = DefaultBotProperties(parse_mode = ParseMode.HTML)) # type: ignore
|
||||||
dp = Dispatcher(storage = MemoryStorage())
|
dp = Dispatcher(storage = MemoryStorage())
|
||||||
|
|
||||||
|
|
||||||
schema_name = 'catbot'
|
schema_name = 'catbot'
|
||||||
table_name = 'Users'
|
table_name = 'Users'
|
||||||
|
|
||||||
|
|
||||||
@dp.message(Command('start'))
|
@dp.message(Command('start'))
|
||||||
async def cmd_start(message: Message):
|
async def cmd_start(message: Message):
|
||||||
await message.answer('''
|
await message.answer(
|
||||||
This is a bot that sends images of cats.
|
text='''
|
||||||
|
This is a bot that sends images of cats.
|
||||||
|
|
||||||
List of available commands:
|
List of available commands:
|
||||||
/cat - request an image of a cat from today's pool of images
|
/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
|
/subscribe - subscribe to daily cat images sent at 12:00 UTC+3
|
||||||
/subscription_modify <number> - change the amount of images sent daily
|
/subscription_modify <number> - change the amount of images sent daily
|
||||||
/unsubscribe - cancel your subscription(but why would you want to? :3)
|
/unsubscribe - cancel your subscription (but why would you want to? :3)
|
||||||
''', parse_mode=None)
|
''',
|
||||||
|
parse_mode=None
|
||||||
|
)
|
||||||
logger.info(f'Command /start executed successfully. ChatID: {message.chat.id}')
|
logger.info(f'Command /start executed successfully. ChatID: {message.chat.id}')
|
||||||
|
|
||||||
|
|
||||||
@dp.message(Command('cat'))
|
@dp.message(Command('cat'))
|
||||||
async def cmd_cat(message: Message):
|
async def cmd_cat(message: Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
image_link = URLInputFile(ISwork.getDownloadURL(current_day), filename=datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
|
download_url = ISwork.getDownloadURL(current_day)
|
||||||
|
if download_url is not None:
|
||||||
|
image_link = URLInputFile(
|
||||||
|
download_url,
|
||||||
|
filename=datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
image_link = None
|
||||||
|
|
||||||
if image_link is None:
|
if image_link is None:
|
||||||
await bot.send_message(chat_id=chat_id, text="We are sorry, but there seems to be a problem with finding images for today.")
|
await bot.send_message(chat_id=chat_id, text='We are sorry, but there seems to be a problem with finding images for today.')
|
||||||
else:
|
else:
|
||||||
await message.answer_photo(image_link, caption='Look, a cat :3')
|
await message.answer_photo(image_link, caption='Look, a cat :3')
|
||||||
logger.info(f'Command /cat executed successfully. ChatID: {chat_id}')
|
logger.info(f'Command /cat executed successfully. ChatID: {chat_id}')
|
||||||
@ -61,14 +80,16 @@ async def cmd_cat(message: Message):
|
|||||||
@dp.message(Command('subscription_modify'))
|
@dp.message(Command('subscription_modify'))
|
||||||
async def subscription_modify(message: Message, command: CommandObject):
|
async def subscription_modify(message: Message, command: CommandObject):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
if command.args is None or command.args.isdigit() == False:
|
if command.args is None or not command.args.isdigit():
|
||||||
await message.answer('Please write the number of images you would like\n'
|
await message.answer(
|
||||||
'to receive in the same message as a command.\n'
|
'Please write the number of images you would like\n'
|
||||||
'Example:'
|
'to receive in the same message as a command.\n'
|
||||||
'/subscription_modify <number of daily images>')
|
'Example:'
|
||||||
|
'/subscription_modify <number of daily images>'
|
||||||
|
)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
cursor, connection = DBwork.set_connection()
|
cursor, connection = DBwork.set_connection() # type: ignore
|
||||||
amount = command.args
|
amount = command.args
|
||||||
DBwork.change_images_amount(chat_id, amount, connection, cursor)
|
DBwork.change_images_amount(chat_id, amount, connection, cursor)
|
||||||
DBwork.close_connection(connection, cursor)
|
DBwork.close_connection(connection, cursor)
|
||||||
@ -99,9 +120,9 @@ async def cmd_subscribe(message: Message):
|
|||||||
logger.error(f'PostgreSQL error occurred. ChatID: {chat_id}. Error: {str(e.pgerror)}')
|
logger.error(f'PostgreSQL error occurred. ChatID: {chat_id}. Error: {str(e.pgerror)}')
|
||||||
return
|
return
|
||||||
await message.answer('''
|
await message.answer('''
|
||||||
You have successfully subscribed to daily cat photos!
|
You have successfully subscribed to daily cat photos!
|
||||||
You will get 1 photo a day by default,
|
You will get 1 photo a day by default,
|
||||||
use /subscription_modify to change that amount.
|
use /subscription_modify to change that amount.
|
||||||
''')
|
''')
|
||||||
logger.info(f'Command /subscribe executed successfully. ChatID: {chat_id}')
|
logger.info(f'Command /subscribe executed successfully. ChatID: {chat_id}')
|
||||||
|
|
||||||
@ -132,7 +153,12 @@ async def send_daily_images():
|
|||||||
chat_id = DBwork.get_chat_id(id, cursor)
|
chat_id = DBwork.get_chat_id(id, cursor)
|
||||||
images_amount = DBwork.get_images_amount(chat_id, cursor)
|
images_amount = DBwork.get_images_amount(chat_id, cursor)
|
||||||
for _ in range(images_amount):
|
for _ in range(images_amount):
|
||||||
image_link = URLInputFile(ISwork.getDownloadURL(current_day), filename=datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
|
url = ISwork.getDownloadURL(current_day)
|
||||||
|
if url is not None:
|
||||||
|
image_link = URLInputFile(url, filename=datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
|
||||||
|
else:
|
||||||
|
image_link = None
|
||||||
|
|
||||||
if image_link is None:
|
if image_link is None:
|
||||||
await bot.send_message(chat_id=chat_id, text="We are sorry, but there seems to be a problem with finding images for today.")
|
await bot.send_message(chat_id=chat_id, text="We are sorry, but there seems to be a problem with finding images for today.")
|
||||||
else:
|
else:
|
||||||
@ -142,8 +168,6 @@ async def send_daily_images():
|
|||||||
|
|
||||||
|
|
||||||
async def set_commands_for_menu():
|
async def set_commands_for_menu():
|
||||||
# await bot(DeleteMyCommands(scope=BotCommandScopeDefault()))
|
|
||||||
logger.info('Bot command list cleared')
|
|
||||||
commands = [
|
commands = [
|
||||||
BotCommand(command='start', description='Get info about the bot and its commands'),
|
BotCommand(command='start', description='Get info about the bot and its commands'),
|
||||||
BotCommand(command='cat', description='Request an image of a cat from today\'s pool of images'),
|
BotCommand(command='cat', description='Request an image of a cat from today\'s pool of images'),
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
Here should be frontend (commands interactions)
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from Frontend.createbot import main
|
from Frontend.createbot import main
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
@ -1,18 +1,17 @@
|
|||||||
from decouple import config
|
from decouple import config
|
||||||
|
|
||||||
|
TG_token = config('TG_TOKEN')
|
||||||
|
|
||||||
IS_address = config('IS_ADDRESS')
|
IS_address = config('IS_ADDRESS')
|
||||||
acc_key = config('MINIO_ACCESS_KEY')
|
acc_key = config('MINIO_ACCESS_KEY')
|
||||||
sec_key = config('MINIO_SECRET_KEY')
|
sec_key = config('MINIO_SECRET_KEY')
|
||||||
root_user = config('MINIO_ROOT_USER')
|
root_user = config('MINIO_ROOT_USER')
|
||||||
|
bucket_name = config('BUCKET_NAME')
|
||||||
|
|
||||||
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')
|
||||||
host_name = config('HOST_NAME')
|
host_name = config('POSTGRES_HOST_NAME')
|
||||||
port = config('PORT')
|
port = config('POSTGRES_PORT')
|
||||||
bucket_name = config('BUCKET_NAME')
|
|
||||||
|
|
||||||
TG_token = config('TG_TOKEN')
|
|
||||||
# ADMINS = [int(admin_id) for admin_id in config('ADMINS').split(',')]
|
|
||||||
|
|
||||||
logging_level = config('LOGGING_LEVEL')
|
logging_level = config('LOGGING_LEVEL')
|
||||||
Reference in New Issue
Block a user