diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..155a9e82 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,26 @@ +name: docs +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v3 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: pip install -r requirements-docs.txt + - run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 559786d1..995afb83 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__ *.swp .env* +site/ diff --git a/README.md b/README.md index 86074239..dc3badba 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Hammett Hammett is a framework whose main goal is to simplify building *commercial* Telegram bots with clear code and a good architecture. By commercial bots are meant such bots that require the support of -* several roles of users (admin, beta testers, moderators, etc.) to manage the visibility of some parts of the user interface; -* the permissions mechanism to implement a **maintenance mode**, **paywall**, etc. -* storing the users state in and restoring it from Redis. + + * several roles of users (admin, beta testers, moderators, etc.) to manage the visibility of some parts of the user interface; + * the permissions mechanism to implement a **maintenance mode**, **paywall**, etc. + * storing the users state in and restoring it from Redis.

- Hammett + Hammett

## Authors diff --git a/logo/1500x1299.png b/docs/assets/images/logo.png similarity index 100% rename from logo/1500x1299.png rename to docs/assets/images/logo.png diff --git a/docs/docs/application.md b/docs/docs/application.md new file mode 100644 index 00000000..c3cfdf77 --- /dev/null +++ b/docs/docs/application.md @@ -0,0 +1,3 @@ +# Hammett Core + +::: hammett.core diff --git a/docs/docs/exceptions.md b/docs/docs/exceptions.md new file mode 100644 index 00000000..b58e6c48 --- /dev/null +++ b/docs/docs/exceptions.md @@ -0,0 +1,3 @@ +# Exceptions + +::: hammett.core.exceptions diff --git a/docs/docs/hiders.md b/docs/docs/hiders.md new file mode 100644 index 00000000..197382db --- /dev/null +++ b/docs/docs/hiders.md @@ -0,0 +1,3 @@ +# Hiders + +::: hammett.core.hiders diff --git a/docs/docs/permissions.md b/docs/docs/permissions.md new file mode 100644 index 00000000..a8700669 --- /dev/null +++ b/docs/docs/permissions.md @@ -0,0 +1,3 @@ +# Permissions + +::: hammett.core.permissions diff --git a/docs/docs/screen.md b/docs/docs/screen.md new file mode 100644 index 00000000..cb69f0a9 --- /dev/null +++ b/docs/docs/screen.md @@ -0,0 +1,3 @@ +# Screen + +::: hammett.core.screen diff --git a/docs/getting_started/AUTHORS.md b/docs/getting_started/AUTHORS.md new file mode 100644 index 00000000..4dd84414 --- /dev/null +++ b/docs/getting_started/AUTHORS.md @@ -0,0 +1 @@ +--8<-- "AUTHORS.md" diff --git a/docs/getting_started/demos.md b/docs/getting_started/demos.md new file mode 100644 index 00000000..7d3721a4 --- /dev/null +++ b/docs/getting_started/demos.md @@ -0,0 +1,6 @@ +# Demos + +You can find code samples on [GitHub](https://github.com/cusdeb-com/hammett/tree/main/demos). + +If there are ideas of what other demo you would like to see you +can [create an Issue](https://github.com/cusdeb-com/hammett/issues/new) with the suggestion. diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md new file mode 100644 index 00000000..0754fa6f --- /dev/null +++ b/docs/getting_started/index.md @@ -0,0 +1,30 @@ +# Getting started + +## Installation + +### with pip recommended + +Hammett is published as a [Python package] and can be installed with +`pip`, highly recommended to use a [virtual environment]. Open up a terminal and install with: + +``` sh +pip install +``` + +### with git + +Hammett can be installed directly from [GitHub] +``` +git clone https://github.com/cusdeb-com/hammett.git +``` + +After cloning from `git`, you must install all required dependencies with: + +``` +pip install -r requirements.txt +``` + + +[Python package]: https://pypi.org/project// +[GitHub]: https://github.com/cusdeb-com/hammett +[virtual environment]: https://realpython.com/what-is-pip/#using-pip-in-a-python-virtual-environment diff --git a/docs/getting_started/introduction.md b/docs/getting_started/introduction.md new file mode 100644 index 00000000..e7c6a8ed --- /dev/null +++ b/docs/getting_started/introduction.md @@ -0,0 +1,139 @@ +# Introduction + +Hammett зиждется на трех аккордах (или китах, если хотите): Screens, Permissions и Hiders. В простейшем случае +пользователь, работая с ботом, переходит от одного экрана к другому, поэтому Screen'ы являются краеугольным камнем +ботов на Hammett. Продвинутые боты, в свою очередь, могут предусматривать разные роли и разрешения для пользователей. +В этом случае на сцену выходят Hider'ы и Permission'ы. Hider'ы позволяют управлять видимостью кнопок на каждом +Screen'е в зависимости от роли пользователя, а Permission'ы позволяют управлять видимостью экранов в зависимости от +прав пользователя. Теперь давайте разберем каждый из этих аккордов по отдельности. + +## Screens + +Screen – это абстракция над обычным Telegram-сообщением для удобного управления его состоянием. Screen’ы представляются +классом Screen. Основная задача этого класса – предоставлять разработчикам возможности для отрисовки и дальнейшей +перерисовки контента. Контент screen'а может состоять из следующих компонентов: + +1. cover – это изображение, которое будет отправлено вместе с сообщением; +2. description или document. description – это текст сообщения, который может содержать HTML-теги (см. какие теги могут + использоваться в описаниях), а document – это любой файл; +3. keyboard – это одна или более кнопок (см. какие типы кнопок могут использоваться в клавиатурах); + +```python3 +from hammett.core import Screen + + +class SimpleScreen(Screen): + cover = 'https://upload.wikimedia.org/wikipedia/commons/2/28/M_Metallica_Userbox.PNG' + description = 'Some text' +``` + +Screen может существовать при наличии хотя бы одного из этих компонентов. Стоит заметить, что на экране эти компоненты +располагаются строго в том порядке, в котором они перечислены в списке выше. Повлиять на этот порядок нельзя. + +Для управления отрисовкой есть несколько вариантов: + +1. Статически через атрибуты Screen’а. +2. Динамически через специальные методы Screen’а. + +Статичный контент задается в атрибутах Screen’а. Это удобно если вам всегда нужно отправлять текст и/или это изображение +пользователю в чат без каких либо условий. Если же контент должен генерироваться по каким-то условиям или, например, +запрашиваться по API, то вам нужно переопределить методы и уже там указать нужное вам поведение. Для cover соотносится +get_cover, для description соотносится get_description и т.д. + +Также важная задача Screen’а — предоставлять удобный механизм переключения между разнымы Screen’ами. Для этого есть 2 +метода, goto и jump: + +1. goto - используется, когда нужно перейти от текущего Screen'а к другому, перерисовав текущий; +2. jump - используется, когда нужно перейти от одного Screen'а к другому, отрисовав этот экран как новое сообщение. + + … тут пример + +### RenderConfig + +Класс данных (dataclass) - хранит в себе параметры, связанные с отрисовкой. Все статичные и/или динамические параметры +так или иначе попадут в RenderConfig перед вызовом метода отрисовки. Этот класс можно явно указывать при вызове метода +отрисовки, чтобы также влиять на контент и на само поведение отрисовки. + +Тут пример +… + + Приоритет параметров при отрисовке (тут будет красивое изображение): + + RenderConfig -> Static -> Dinamic (if overridden) + +Обычно RenderConfig явно конфигурируется при переопределении методов goto и jump (см. Маршрутизация между Screen’ами). +Если требуется задать одинаковое поведение для всех случаев отрисовки нужно переопределить метод get_config, который +должен вернуть объект RenderConfig: + +Тут пример +… + +### Обработчики + +Есть три типа обработчиков: + +1) обработчик нажатия на кнопку; +2) обработчик пользовательского ввода; +3) обработчик команды (пример: /command). + +Чтобы зарегистрировать метод Screen’а как обработчик нужно обернуть его в декоратор, метод обязательно должен иметь в +качестве аргументов self, update и context. Давайте разберем каждый из этих типов обработчиков по отдельности. + +Обработчик нажатия на кнопку привязывается к кнопке на клавиатуре и срабатывает при нажатии на неё. + +Тут пример… + +Обработчик пользовательского ввода срабатывает на какое-либо сообщение от пользователя, например отправку текстового +сообщения или отправку изображения. Можно конкретизировать на какой контент он может сработать см. Filters. + +Тут пример… + +Обработчик команды срабатывает на отправку команды от пользователя, например /say_hi. + +Тут пример… + +### Скрытие клавиатуры + +Позволяет убирать у предыдущих Screen’ов клавиатуру, оставляя её только у последнего (latest) Screen’а. Это делает +историю сообщений более читаемой и не дает пользователю нажать кнопки, которые в данный момент не нужны. + +После каждой отрисовки Screen’а как новое сообщение будет удалятся клавиатура у предыдущего сообщения и только у него, +больше чем у одного предыдущего сообщения клавиатура удалятся не будет. + +Примечание: чтобы включить эту возможность, нужно установить значение параметра конфигурации SAVE_LATEST_MESSAGE в True: + +Тут пример: + +Ссылка на демо. + +## Permissions + +Позволяют быстро настроить права доступа в боте. Вот пара примеров, в каких случаях могут использоваться Persmission'ы: + +- Регистрация, которую обязан пройти пользователь, чтобы получить разрешение использовать сервис, который + предоставляется ботом. +- Paywall, когда пользователь не имеет разрешения продвинутся дальше, пока не оформит подписку на бота. + + Permission’ы могут накладываться друг на друга, если проверок может быть несколько. В каждом Permission’е нужно + объявить проверку в методе has_permission и что нужно сделать, если доступа нет в методе handle_permission_denied. + +… тут пример + +Все объявленные Permission’ы также должны быть указаны в конфигурационном файле, например: + +```python3 +PERMISSIONS = [ + 'app.permissions.NamePermission', +] +``` + +Можно игнорировать Permission’ы, обернув в декоратор обработчик и указав какие Permission’ы мы хотим проигнорировать. +Важно: декоратор ignore_permissions должнен быть выше чем декоратор для регистрации обработчика: + +```python3 +@ignore_permissions([NamePermission]) +@register_button_handler +async def handle_something(self, update, context): + """Some code""" +``` + diff --git a/docs/getting_started/license.md b/docs/getting_started/license.md new file mode 100644 index 00000000..438ca86d --- /dev/null +++ b/docs/getting_started/license.md @@ -0,0 +1,176 @@ +# License + +Apache License Version 2.0, January 2004 [http://www.apache.org/licenses/](http://www.apache.org/licenses/) + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/docs/getting_started/usage.md b/docs/getting_started/usage.md new file mode 100644 index 00000000..be45438f --- /dev/null +++ b/docs/getting_started/usage.md @@ -0,0 +1,35 @@ +# Usage + +The simplest example using Hammett: + +``` py title="bot.py" linenums="1" +from hammett.core import Application +from hammett.core.screen import Button, RenderConfig, StartScreen +from hammett.core.constants import DEFAULT_STAGE, SourcesTypes +from hammett.utils.autodiscovery import autodiscover_screens + +class MainMenu(StartScreen): + def setup_keyboard(self): + return [ + [ + Button('🎸 Hello, World!', 'https://github.com/cusdeb-com/hammett', + source_type=SourcesTypes.URL_SOURCE_TYPE), + ], + ] + + def start(self, update, context): + config = RenderConfig(as_new_message=True) + await self.render(update, context, config=config) + return DEFAULT_STAGE + +def main(): + name = 'demo' + app = Application( + name, + entry_point=MainMenu, + states={ + DEFAULT_STAGE: autodiscover_screens('bot'), + }, + ) + app.run() +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..6a7bb0fb --- /dev/null +++ b/docs/index.md @@ -0,0 +1,6 @@ +--- +template: home.html +title: Hammett +--- + +--8<-- "README.md" diff --git a/docs/overrides/home.html b/docs/overrides/home.html new file mode 100644 index 00000000..f13965bc --- /dev/null +++ b/docs/overrides/home.html @@ -0,0 +1,18 @@ +{% extends "main.html" %} + +{% block tabs %} + {{ super() }} + +{% endblock %} diff --git a/hammett/core/application.py b/hammett/core/application.py index 36ec70d1..31712e96 100644 --- a/hammett/core/application.py +++ b/hammett/core/application.py @@ -34,10 +34,20 @@ class Application: """The class is a wrapper for the native Application class. + The wrapping solves the following tasks: - - hiding low-level technical details of python-telegram-bot from developers; - - registering handlers; - - configuring logging. + - hiding low-level technical details of python-telegram-bot from developers; + - registering handlers; + - configuring logging. + + Args: + ---- + name: The name will pass for start conversation handler. Uses for persistence. + entry_point: Implement start handler. + native_states (NativeStates, optional): The states from python-telegram-bot library. + persistence (BasePersistence[UD, CD, BD] , optional): Any realization of persistence. + states (States, optional): dict with Stage key and list of Screens. + """ def __init__( diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..12416225 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,97 @@ +site_name: Hammett + +repo_url: https://github.com/cusdeb-com/hammett +repo_name: cusdeb-com/hammett + +extra: + version: + provider: mike + social: + - icon: fontawesome/brands/github + link: https://github.com/cusdeb-com + +plugins: + - mkdocstrings: + handlers: + python: + options: + docstring_options: + ignore_init_summary: true + merge_init_into_class: true + separate_signature: true + show_root_full_path: false + show_signature_annotations: true + show_symbol_type_heading: true + show_symbol_type_toc: true + signature_crossrefs: true + + - search: + lang: en + + - minify: + minify_html: true + +markdown_extensions: + - def_list + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + +nav: + - Home: index.md + - Getting started: + - Installation: getting_started/index.md + - Introduction: getting_started/introduction.md + - Usage: getting_started/usage.md + - Demos: getting_started/demos.md + - Authors: getting_started/AUTHORS.md + - License: getting_started/license.md + - Docs: + - Application: docs/application.md + - Screen: docs/screen.md + - Hiders: docs/hiders.md + - Permissions: docs/permissions.md + - Exceptions: docs/exceptions.md + +theme: + name: "material" + logo: assets/images/logo.png + custom_dir: docs/overrides + + features: + - navigation.footer + - navigation.indexes + - navigation.sections + - navigation.tabs + - navigation.top + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + - content.code.annotate + - content.code.copy + + palette: + - media: "(prefers-color-scheme: light)" + primary: deep purple + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + - media: "(prefers-color-scheme: dark)" + primary: deep purple + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference + + font: + text: Roboto + code: Roboto Mono diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 00000000..55912562 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,4 @@ +mike==1.1.2 +mkdocs==1.5.2 +mkdocs-material==9.1.21 +mkdocs-minify-plugin==0.7.1