From 34f375989c8ea18a1989ed436fb87ad2ecf75fbe Mon Sep 17 00:00:00 2001 From: bvn13 Date: Thu, 6 Jun 2024 00:08:10 +0300 Subject: [PATCH] readme and docker-compose --- Dockerfile | 13 +++++++++ README.md | 45 +++++++++++++++++++++++++++++ docker-compose.yaml | 19 +++++++++++++ src/main.py | 69 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 144 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e69de29..70ccccd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.10 + +RUN apt-get install libpq-dev -y + +ADD requirements.txt requirements.txt + +RUN pip install -r requirements.txt + +ADD src /app + +WORKDIR /app + +ENTRYPOINT [ "gunicorn", "--chdir=/app/src", "--bond=127.0.0.1:8080", "main:app" ] \ No newline at end of file diff --git a/README.md b/README.md index e69de29..704f52e 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,45 @@ +# JeDoIst клиент + +## Что это? + +- это клиент для телеграм-бота [JeDoIst](https://t.me/jedoist) + +## Зачем вам это? + +- [JeDoIst](https://t.me/jedoist) - это автоматизатор входящей очереди задач + +Не так давно я прочитал книгу Максима Дорофеева "[Джедайские техники](https://t.me/bvn13_blog/108)", данный проект основан на идеях, описанных в данной книге. + +## Какие идеи? + +- каждый человек работает с задачами +- чтобы быть успешным, нужно **правильно** работать со своими задачами +- очередь входящий задач должна быть одна: должно быть одно место, куда складываются все входящие задачи +- **работа** заключается в постоянной проработке и декомпозиции этой очереди по правилам, описанным в книге, по принципу "для того, чтобы обезьянке стало проще выполнять" + +### Улучшения + +- часто идеи приходят в совершенно неподходящий для записывания "от руки" время +- хочется иметь инструмент, который послужит входящей очередью, в которую можно буквально **надиктовать** задачу +- но очередь задач должна быть одна, поэтому... +- голосовые записи должны быть расшифрованы и сложены в единую очередь + +## Поэтому + +- [JeDoIst](https://t.me/jedoist) является этой входящей очередью, в которую можно "надиктовать" свою идею или задачу +- он бережно расшифрует голосовое сообщение и передаст его в callback-е на любой удаленный сервер +- с одним ограничением: протокол диктует [JeDoIst](https://t.me/jedoist), и должна быть авторизация + +### И поэтому... + +- этот репозиторий предназначен для быстрого развертывания веб-сервера, в который будут отправляться ваши задачи + +## Но! + +- Вопрос: но где же опубликовать этот веб-сервер? +- Ответ: на любом VPS +- Вопрос: как сохраняются задачи? +- Ответ: в одном файле в формате Markdown +- Вопрос: файл на сервере, я работаю на ноутбуке - где логика? +- Ответ: есть Synkthing, который позволит синхронизировать файл с задачми на сервере и на ноутбуке. И на смартфоне. + diff --git a/docker-compose.yaml b/docker-compose.yaml index e69de29..77a516d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -0,0 +1,19 @@ +version: '3.3' + +networks: + jedoist: + external: true + +services: + + bot: + # build: ./ + image: localhost:5001/jedoist-client:${APP_VERSION} + environment: + - TODO_PATH=/mnt/todo + - TODO_FILE=${TODO_FILE} + volumes: + - ${TODO_PATH}:/mnt/todo + restart: on-failure + networks: + - jedoist \ No newline at end of file diff --git a/src/main.py b/src/main.py index da45ecd..96b8567 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,41 @@ import falcon import json +import os + +# https://falcon.readthedocs.io/en/stable/user/quickstart.html#learning-by-example + +class AuthMiddleware: + def process_request(self, req, resp): + token = req.get_header('Authorization') + account_id = req.get_header('Account-ID') + + challenges = ['Token type="Fernet"'] + + if token is None: + description = 'Please provide an auth token as part of the request.' + + raise falcon.HTTPUnauthorized( + title='Auth token required', + description=description, + challenges=challenges, + href='http://docs.example.com/auth', + ) + + if not self._token_is_valid(token, account_id): + description = ( + 'The provided auth token is not valid. ' + 'Please request a new token and try again.' + ) + + raise falcon.HTTPUnauthorized( + title='Authentication required', + description=description, + challenges=challenges, + href='http://docs.example.com/auth', + ) + + def _token_is_valid(self, token, account_id): + return os.environ["TOKEN"] # Suuuuuure it's valid... class RequireJSON: @@ -17,6 +53,7 @@ class RequireJSON: href='http://docs.examples.com/api/json', ) + class JSONTranslator: # NOTE: Normally you would simply use req.media and resp.media for # this particular use case; this example serves only to illustrate @@ -72,18 +109,46 @@ def max_body(limit): return hook +######################################################################################################## + class CreateTaskResource: + + TODO_HEADER = '# ToDo' + + def __init__(self) -> None: + self.__todo_path = "{0}/{1}".format(os.environ['TODO_PATH'], os.environ['TODO_FILE']) + def on_post(self, req, resp): + task = req.context.doc['task'] + self.__create_task(task) quote = { 'title': 'Получена задача', - 'description': req.context.doc['task'] + 'description': task } - resp.media = quote + def __create_task(self, text: str) -> None: + content = [] + section_found = False + is_waiting_empty_line = False + with open(self.__todo_path, 'r', encoding='UTF-8') as file: + while line := file.readline(): + if section_found == False: + if line.strip() == CreateTaskResource.TODO_HEADER: + section_found = True + is_waiting_empty_line = True + content.append(line) + if section_found and is_waiting_empty_line == True and line.strip() == '': + content.append("- [ ] {0}\n".format(text)) + is_waiting_empty_line = False + with open(self.__todo_path, 'w', encoding='utf-8') as file: + file.writelines(content) + +######################################################################################################## app = falcon.App( middleware=[ + AuthMiddleware(), RequireJSON(), JSONTranslator() ]