{"id":20154377,"url":"https://github.com/nurislom373/fluent","last_synced_at":"2025-04-09T21:52:45.787Z","repository":{"id":225771877,"uuid":"701847703","full_name":"Nurislom373/Fluent","owner":"Nurislom373","description":"Fluent - Easy Telegram Bots with Spring!","archived":false,"fork":false,"pushed_at":"2024-08-02T04:57:35.000Z","size":3896,"stargazers_count":11,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-09T21:52:35.978Z","etag":null,"topics":["async","java","kotlin","library","maven","spring","telegram-bot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nurislom373.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-07T18:27:11.000Z","updated_at":"2024-08-02T04:57:38.000Z","dependencies_parsed_at":"2024-03-28T17:26:06.331Z","dependency_job_id":"47059a8c-d963-44dd-be43-ddf14926aa10","html_url":"https://github.com/Nurislom373/Fluent","commit_stats":null,"previous_names":["nurislom373/fluent"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nurislom373%2FFluent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nurislom373%2FFluent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nurislom373%2FFluent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nurislom373%2FFluent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nurislom373","download_url":"https://codeload.github.com/Nurislom373/Fluent/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248119401,"owners_count":21050754,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","java","kotlin","library","maven","spring","telegram-bot"],"created_at":"2024-11-13T23:26:25.013Z","updated_at":"2025-04-09T21:52:45.761Z","avatar_url":"https://github.com/Nurislom373.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fluent - Easy Telegram Bots with Spring!\n\n## Overview\n\nThis library provides a high-level abstraction for developing Telegram bots 🤖. The library simplifies the process of\nreceiving updates from Telegram and sending messages 📨. Additionally, you can easily integrate this library into your\nproject and customize it according to your preferences by using its latest version when developing your application 🚀.\n\n## Prerequisites\n\n* Fluent 1.2.2 requires at least Java 17, Spring Boot version 3.0.0 or above.\n\n## Getting Started\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.nurislom373\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-starter-fluent\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```groovy\nimplementation group: 'io.github.nurislom373', name: 'spring-boot-starter-fluent', version: '1.2.2'\n```\n\n## How to Use\n\nAfter adding the dependency, set up your bot by adding the bot token and username in your application.yaml file.\n\n```yaml\nfluent:\n  bot:\n    token: \u003cyour bot token\u003e\n    username: \u003cyour bot username\u003e\n    process-type: update\n```\nOnce you've added the token and username, create a bot controller.\n\n```java\n@UpdateController\npublic class FluentController {\n\n    private final FluentTemplate fluentTemplate;\n\n    public FluentController(FluentTemplate fluentTemplate) {\n        this.fluentTemplate = fluentTemplate;\n    }\n\n    @HandleMessage(\"/start\")\n    public void fluent(Update update) {\n        String text = update.getMessage().getText();\n        fluentTemplate.sendText(text);\n    }\n}\n```\n\nHandling exceptions is easy too! Just create an exception controller.\n\n```java\n@ExceptionController\npublic class ExceptionHandler  {\n\n    private final FluentTemplate fluentTemplate;\n\n    public FluentController(FluentTemplate fluentTemplate) {\n        this.fluentTemplate = fluentTemplate;\n    }\n    \n    @HandleException({RuntimeException.class})\n    void handleRuntimeExceptions(Update update, Throwable throwable) {\n        fluentTemplate.sendText(\"I'm Handle Exception : \" + throwable.getMessage());\n    }\n\n}\n```\n\n# Detailed documentation\n\n## 1. Creating your bot\n\nAs written in the 'Getting Started' section of the tutorial, after adding the necessary dependency to our first program,\nyou need to write the credentials related to the bot in a properties or YAML file. This alone is sufficient for now to\nstart the bot and use it.\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.nurislom373\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-starter-fluent\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```groovy\nimplementation group: 'io.github.nurislom373', name: 'spring-boot-starter-fluent', version: '1.2.2'\n```\n\n### Yaml configuration\n\n```yaml\nfluent:\n  bot:\n    token: \u003cyour bot token\u003e\n    username: \u003cyour bot username\u003e\n    process-type: update\n```\n\n## 2. Receiving updates\n\nLet's see how we can receive requests that have come from Telegram in this section.\n\n### 2.1 Controllers\n\nWe need to apply the `@UpdateController` annotation to classes that process requests coming from Telegram. This is necessary\nbecause when your application starts, the classes with the `@UpdateController` annotation are collected by the Fluent provider,\nwhich then directs incoming requests to the methods of these classes.\n\n```java\n@UpdateController\npublic class FluentController {\n    \n    // ...\n    \n}\n```\n\n### 2.2 Handle message\n\nLet's explore how we can write methods to process requests coming from Telegram in this section.\n\nLet's start by discussing how to handle text messages that come through Telegram.\nTo handle text messages that come through Telegram, we use the @HandleMessage annotation. The @HandleMessage annotation\nallows us to implement various ways of handling text messages, such as using regex or expressions. It provides us with\ndifferent paths to carry out the handling. Let's take a look at all of these.\n\n```java\n@HandleMessage(\"/start\")\npublic void startExample() {\n    fluentTemplate.sendText(\"Hello World!!!\");\n}\n```\n\nThe code written uses the `@HandleMessage` annotation to create a method that catches a message containing `/start` from\nTelegram. This method will only be triggered if the received message is exactly `/start`. If the message contains `/start`\nat the beginning or the end but has additional content, the method will not be triggered.\n\nLet's consider why this happens, meaning the method is triggered only if the received text message exactly matches the\nstring provided in the `@HandleMessage` annotation. We can look inside the `@HandleMessage` annotation to understand this better.\n\n```java\n@ProcessUpdate\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface HandleMessage {\n\n    String value() default \"/\";\n\n    MatchType match() default MatchType.EQUALS;\n}\n```\n\nIn the code above, you might have noticed the match() method within the @HandleMessage annotation has a default value of\nEQUALS. This means that by default, the annotation checks if the incoming Telegram message exactly matches the text\nprovided in the annotation.\n\nWe can change the strategy for checking the incoming message by providing a different MatchType to the match method of\nthis annotation. For example, let's consider handling any message that starts with message:. To achieve this, we need to\nchange the MatchType for the method annotated with @HandleMessage. Follow the code below:\n\n```java\n@HandleMessage(value = \"message:\", match = MatchType.STARTS_WITH)\npublic void startsWithExampleHandler() {\n    fluentTemplate.sendText(\"I handle requests starts with 'message:'\");\n}\n```\n\n### 2.3 Handler method add update parameter\n\nIn this section, we will see how to add the update parameter to handler methods. Follow the code below:\n\n```java\n@HandleMessage(\"/start\")\npublic void startExample(Update update) {\n    fluentTemplate.sendText(\"Hello World!!!\");\n}\n```\n\nAs you can see in the code above, it is possible to add the update parameter to handler methods. If you expect an update\nfrom Telegram as a parameter, the handler method will be called with the update parameter included. This allows you to\nextract the necessary values from the update object.\n\nAdditionally, you can obtain the update object without explicitly receiving it as a parameter. You can use the getCurrentUpdate\nmethod from the FluentContextHolder class for this purpose.\n\n```java\n@HandleAny(type = HandleType.MESSAGE, proceed = Proceed.NOT_PROCEED)\nprivate void handleAnyMessages() {\n    Update currentUpdate = FluentContextHolder.getCurrentUpdate();\n\n    String text = currentUpdate.getMessage().getText();\n    fluentTemplate.sendText(text);\n}\n```\n\n### 2.4 MatchType\n\nIn this section, we will review MatchType strategies.\n\nMatchType strategies are used to change the strategy for checking messages received from Telegram. This allows you to write complex conditions.\n\n* `STARTS_WITH`\n\nSTARTS_WITH - The matching strategy is used to match text messages received from Telegram. It checks if the beginning of \nthe incoming message matches the specified value. For example, it checks if the incoming message starts with 'abs'. \nIf it matches, the handle is triggered; otherwise, it is not.\n\n```java\n@HandleMessage(value = \"abs\", match = MatchType.STARTS_WITH)\npublic void startsWithExampleHandler() {\n    // ...\n}\n```\n\n* `ENDS_WITH`\n\nENDS_WITH - The match strategy is also mainly used for matching text messages, similar to STARTS_WITH. The ENDS_WITH \nstrategy is similar to STARTS_WITH, but instead of checking the beginning, it checks if the end of the message matches \nthe specified value.\n\n```java\n@HandleMessage(value = \"xyz\", match = MatchType.ENDS_WITH)\npublic void endsWithExampleHandler() {\n    // ...\n}\n```\n\n* `CONTAINS`\n\nCONTAINS - The match strategy is also mainly used for matching text messages. The CONTAINS strategy checks if the \nspecified value exists within the incoming message. If the specified value is present in the message received from \nTelegram, the handler is invoked.\n\n```java\n@HandleMessage(value = \"hello\", match = MatchType.CONTAINS)\npublic void endsWithExampleHandler() {\n    // ...\n}\n```\n\n* `EQUALS`\n\nEQUALS - The match strategy is the default match strategy. The EQUALS strategy triggers the handler only if the message\nreceived from Telegram exactly matches the specified value.\n\n```java\n@HandleMessage(value = \"/start\", match = MatchType.EQUALS)\nprivate void startExample(Update update) {\n    // ...\n}\n```\n\n* `REGEX`\n\nREGEX - The match strategy checks if the message received from Telegram matches the specified regex value. \nConsider the code below. In this code, the handler is triggered only if the file type is `jpeg`, `png`, or `pdf`.\n\n```java\n@HandleDocument(\n        value = \"([a-zA-Z0-9\\\\s_\\\\\\\\.\\\\-\\\\(\\\\):])+(.jpeg|.png|.pdf)$\",\n        match = MatchType.REGEX,\n        property = DocumentScope.FILE_NAME\n)\nprivate void handleDocument() {\n    // ...\n}\n```\n\n* `EQUALS_IGNORE_CASE`\n\nEQUALS_IGNORE_CASE - The match strategy is the same as `EQUALS`, with the only difference being that `EQUALS_IGNORE_CASE` \ndoes not consider the case of the letters.\n\n```java\n@HandleMessage(value = \"/START\", match = MatchType.EQUALS_IGNORE_CASE)\nprivate void startExample(Update update) {\n    // ...\n}\n```\n\n* `EXPRESSION`\n\nEXPRESSION - The match strategy allows you to use SpEL (Spring Expression Language) to write expressions, and the \nhandler method is invoked only if the incoming message matches the expression. Consider the code below. Using SpEL, \nthe handler is triggered if the incoming message starts with the letter 'a' and ends with the letter 'z'.\n\n'#value' - This adds the message received from Telegram as a variable to SpEL. You can write your desired expression by \nreferring to the `value` variable.\n\n```java\n@HandleMessage(value = \"#value.startsWith('a') \u0026\u0026 #value.endsWith('z')\", match = MatchType.EXPRESSION)\npublic void handleExpression() {\n    // ...\n}\n```\n\n* `VAR_EXPRESSION`\n\nVAR_EXPRESSION - The match strategy is also used for working with text messages. VAR_EXPRESSION is used to extract \nvariables from the incoming message that match a regex pattern. You can see the code below.\n\n```java\n@HandleMessage(value = \"/username : {name:[a-z]}\", match = MatchType.VAR_EXPRESSION)\nvoid startWithAbsHandler(Update update, @BotVariable(\"name\") String username) {\n    fluentTemplate.sendText(\"Username : \" + username);\n}\n```\n\n### 2.5 Handle any\n\nThe `@HandleAny` annotation is used to handle any update received from Telegram. `@HandleAny` is processed before other \nhandler annotations, meaning that the incoming update is first routed to methods with the `@HandleAny` annotation before \nproceeding to other handler methods.\n\n```java\n@HandleAny(type = HandleType.MESSAGE)\nprivate void handleAnyMessages() {\n    String text = \"I'm handle any updates\";\n    fluentTemplate.sendText(text);\n}\n\n@HandleMessage(value = \"/button\")\npublic void handleButtonCommand() {\n    InlineKeyboardMarkupBuilder builder = new InlineKeyboardMarkupBuilder();\n\n    builder.addButton(\"First\")\n            .callbackData(\"First\");\n    builder.addButton(\"Second\")\n            .callbackData(\"Second\");\n\n    builder.addRow();\n\n    builder.addButton(\"Third\")\n            .callbackData(\"Third\");\n    builder.addButton(\"Fourth\")\n            .callbackData(\"Fourth\");\n\n    fluentTemplate.sendText(\"This is inline button\", builder.build());\n}\n```\n\n![first_screen](documentation/images/first_screen.png)\n\n`@HandleAny` annotation accepts 2 parameters.\n\n1. `type` - This parameter is used to specify which type of updates to handle. By default, if no `type` is specified, \n   `@HandleAny` will handle `HandleType.MESSAGE`.\n\nFor example\n\n```java\n@HandleAny(type = HandleType.STICKER)\nprivate void handleAnyStickers(Update update) {\n    String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(update.getMessage().getSticker());\n    String text = \"I'm handle this sticker : \\n\" + value;\n    fluentTemplate.sendText(text);\n}\n\n@HandleAny(type = HandleType.PHOTO)\nprivate void handleAnyPhoto(Update update) {\n    String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(update.getMessage().getPhoto());\n    String text = \"handle any this photo : \\n\" + value;\n    fluentTemplate.sendText(text);\n}\n\n@HandleAny(type = HandleType.DOCUMENT)\nprivate void handleAnyDocument(Update update) {\n    String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(update.getMessage().getDocument());\n    String text = \"I'm handle this document : \\n\" + value;\n    fluentTemplate.sendText(text);\n}\n\n@HandleAny(type = HandleType.AUDIO)\nprivate void handleAnyCallbacks(Update update) {\n    String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(update.getMessage().getAudio());\n    String text = \"I'm handle this audio : \\n\" + value;\n    fluentTemplate.sendText(text);\n}\n\n@HandleAny(type = HandleType.VIDEO_NOTE)\nprivate void handleAnyVideoNote(Update update) {\n    String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(update.getMessage().getVideoNote());\n    String text = \"I'm handle this video note : \\n\" + value;\n    fluentTemplate.sendText(text);\n}\n```\n\nAt the same time, we can accept multiple types.\n\n```java\n@HandleAny(type = {HandleType.MESSAGE, HandleType.AUDIO})\nprivate void handleAnyMessagesWithUpdate(Update update) {\n    String text = \"I'm handle message or audio updates\";\n    fluentTemplate.sendText(text);\n}\n```\n\n2. `proceed` - This parameter allows us to specify whether subsequent handler methods should be executed after the method \n   with the `@HandleAny` annotation is executed. If `Proceed.PROCEED` is set, it permits the execution of subsequent methods. \n   If set otherwise, it prevents the execution of subsequent handler methods. By default, the `@HandleAny` annotation has \n   `type` set to `HandleType.MESSAGE` and `proceed` set to `Proceed.PROCEED`.\n\nIn this example, the `@HandleAny` annotation's `proceed` parameter is set to its default value, which is `Proceed.PROCEED`.\n\n```java\n@UpdateController\npublic class SimpleController {\n\n    @HandleAny\n    void handleAnyMessage(Update update) {\n        fluentTemplate.sendText(\"Handler Any Message😎\");\n    }\n\n    @HandleMessage(value = \"abs\", scope = MatchScope.START_WITH)\n    void startWithAbsHandler(Update update) {\n        String text = \"Start With 'abs' : \" + update.getMessage().getText();\n        fluentTemplate.sendText(text);\n    }\n}\n```\n\n![handle_any_1](documentation/images/handle_any_1.png)\n\nWe will change the `proceed` type of the `@HandleAny` annotation to `NOT_PROCEED` and observe the result.\n\n```java\n@UpdateController\npublic class SimpleController {\n\n    @HandleAny(proceed = Proceed.NOT_PROCEED)\n    void handleAnyMessage(Update update) {\n        fluentTemplate.sendText(\"Handler Any Message😎\");\n    }\n\n    @HandleMessage(value = \"abs\", scope = MatchScope.START_WITH)\n    void startWithAbsHandler(Update update, AbsSender sender) {\n        String text = \"Start With 'abs' : \" + update.getMessage().getText();\n        fluentTemplate.sendText(text);\n    }\n}\n```\n\n![handle_any_2](documentation/images/handle_any_2.png)\n\nFrom the result shown in the image, you can see that only the `@HandleAny` method was executed, and no subsequent handler\nmethods were executed.\n\nYou can also write a method with the `@HandleAny` annotation without any parameters.\n\n```java\n@HandleAny\npublic void handleAnyMessage() {\n    fluentTemplate.sendText(\"Handler Any Message😎\");\n}\n```\n\n### 2.6 Other handlers\n\nHozirgacha ko'rsatilgan `@HandleMessage`, `@HandleDocument` va `@HandleAny` annotatsiyalaridan boshqa annotatsiyalari ham\nbor ularga birma bir to'xtalish shart emas sababi ular ham huddi `@HandleMessage` va `@HandleDocument` bilan bir xil\nfaqat propertylarida farq qiladi.\n\n### 2.6.1 Handle callback\n\n`@HandleCallback` annotatsiyasi nomidan ma'lum callbacklarni handle qilish uchun ishlatiladi.\n\nQuyidagi kodga qarang:\n\n```java\n@HandleCallback({\"EN\", \"RU\", \"UZ\"})\nprivate void callBack() {\n    // ...\n}\n```\n\n### 2.6.2 Handle photo\n\n`@HandlePhoto` annotatsiyasi nomidan ma'lum rasmlarni handle qilish uchun ishlatiladi.\n\nQuyidagi kodga qarang:\n\n```java\n@HandlePhoto(value = \"start: \", match = MatchType.STARTS_WITH, property = PhotoScope.CAPTION)\npublic void handleStartCaptionPhoto() {\n    // ...\n}\n```\n\n### 2.6.3 Handle video\n\n`@HandleVideo` annotatsiyasi nomidan ma'lum videolarni handle qilish uchun ishlatiladi.\n\nQuyidagi kodga qarang:\n\n```java\n@HandleVideo(value = \"caption:\", match = MatchType.STARTS_WITH, property = VideoScope.CAPTION)\npublic void handleVideoCaption() {\n    // ...\n}\n```\n\n### 2.6.4 Handle audio\n\n`@HandleVideo` annotatsiyasi nomidan ma'lum audiolarni handle qilish uchun ishlatiladi.\n\n```java\n@HandleAudio(value = \"caption:\", match = MatchType.STARTS_WITH, property = VideoScope.CAPTION)\npublic void handleAudioCaption() {\n    // ...\n}\n```\n\n### 2.6.5 Repeatable\n\nBazi annotatsiyalarni ko'plik varianti ham mavjud misol uchun `@HandleCallback` annotatsiyasini ko'plik varianti mavjud\n`@HandleCallbacks` unda siz bir nechta `@HandleCallback` larni e'lon qilishingiz mumkin.\n\n```java\n@HandleCallbacks(value = {\n        @HandleCallback({\"NEXT\", \"PREV\"}),\n        @HandleCallback({\"TOP\", \"BOTTOM\"}),\n        @HandleCallback(value = {\"INDEX\"}, match = MatchType.STARTS_WITH)\n})\nprivate void multiCallback(Update update) {\n    // ...\n}\n```\n\n`@HandleAny`, `@HandleUnknown` va boshqa bir nechta annotatsiyalarni ko'plik variantlari mavjud emas.\n\n## 3. FluentTemplate\n\nFluent xabar yuborish uchun asosiy rol o'ynaydigan \"template\" ni taqdim etadi. Xabar yuborish uchun asosiy operatsiyalarni\nbelgilaydigan interface `FluentTemplate` deb ataladi. Ushbu operatsiyalar sizga xabar yuborishni ancha osonlash tiradi.\nXabar yuborish uchun siz endi object yaratishingizni va chat_id ni o'rnatishingizni hojati yoq siz uchun bu ishni FluentTemplate\nbajarib beradi.\n\nFluentTemplate o'z ichiga qamrab olgan asosiy operatsiyalar\n\n- text, audio, document, photo, video va boshqalarni yuborish uchun operatsiyalar\n- callback va inline query larga javob yuborish uchun operatsiyalar\n- xabarlarni yangilash o'chirish, o'zgarishtirish va boshqalar uchun operatsiyalar\n\nQuyidagi kodga e'tibor bering\n\n- Xabar yuborishga misol\n\n```java\nprivate void sendTextExample() {\n    fluentTemplate.sendText(\"'Hello World' message send!\");\n}\n```\n\nTepadagi kodda e'tibor bergan bo'lsangiz xabar yuborish uchun hech qanday yangi object va chat_id ni o'rnatish shart bo'lmagan\nshunchaki yubormoqchi bo'lgan xabaringizni o'zini kiritish yetarli. Ushbu xabar current update ni chat_id ga jo'natiladi.\nAgar siz boshqa chat_id jo'natmoqchi bo'lsangiz uni ham qilishingiz mumkin.\n\n```java\nprivate void sendTextExample() {\n    fluentTemplate.sendText(\"'Hello World' message send!\", 574635734L);\n}\n```\n\n- Xabarni reply keyboard bilan birga yuborishga misol\n\n```java\npublic void sendTextWithReplyKeyboardExample() {\n    ReplyKeyboardMarkupBuilder builder = new ReplyKeyboardMarkupBuilder();\n    builder.oneTimeKeyboard(true);\n\n    builder.addButton(\"First\");\n    builder.addButton(\"Second\");\n\n    builder.addRow();\n\n    builder.addButton(\"Third\");\n    builder.addButton(\"Fourth\");\n\n    fluentTemplate.sendText(\"This is reply button\", builder.build());\n}\n```\n\n- Audio yuborishga misol\n\n```java\npublic void sendAudioExample() {\n    InputStream inputStream = getClass().getResourceAsStream(\"/fluent.m4a\");\n    fluentTemplate.sendAudio(inputStream, \"fluent.m4a\");\n}\n```\n\n- Boshqa operatsiyalarni ushbu kerakli classlarga kirib ko'rishingiz mumkin.\n\n  - [SendTextOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendTextOperations.java)\n  - [SendAudioOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendAudioOperations.java)\n  - [SendAnimationOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendAnimationOperations.java)\n  - [SendChatActionOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendChatActionOperations.java)\n  - [SendContactOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendContactOperations.java)\n  - [SendDiceOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendDiceOperations.java)\n  - [SendDocumentOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendDocumentOperations.java)\n  - [SendGameOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendGameOperations.java)\n  - [SendLocationOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendLocationOperations.java)\n  - [SendMediaGroupOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendMediaGroupOperations.java)\n  - [SendPhotoOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendPhotoOperations.java)\n  - [SendStickerOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendStickerOperations.java)\n  - [SendVideoNoteOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendVideoNoteOperations.java)\n  - [SendVideoOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendVideoOperations.java)\n  - [SendVoiceOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FSendVoiceOperations.java)\n  - [ForwardMessageOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2FForwardMessageOperations.java)\n  - [DeleteMessageOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fupdatingmessages%2FDeleteMessageOperations.java)\n  - [EditMessageCaptionOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fupdatingmessages%2FEditMessageCaptionOperations.java)\n  - [EditMessageLiveLocationOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fupdatingmessages%2FEditMessageLiveLocationOperations.java)\n  - [EditMessageMediaOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fupdatingmessages%2FEditMessageMediaOperations.java)\n  - [EditMessageReplyMarkupOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fupdatingmessages%2FEditMessageReplyMarkupOperations.java)\n  - [EditMessageTextOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fupdatingmessages%2FEditMessageTextOperations.java)\n  - [AnswerCallbackQueryOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fquery%2FAnswerCallbackQueryOperations.java)\n  - [AnswerInlineQueryOperations.java](fluent-core%2Fsrc%2Fmain%2Fjava%2Forg%2Fkhasanof%2Fservice%2Ftemplate%2Foperations%2Fquery%2FAnswerInlineQueryOperations.java)\n\n## 4. Handling exceptions\n\nFluent kutubxonasi xatolar bilan ham ishlash uchun kuchli funksionalikni taqdim etadi. Bu funksionalikdan foydalanib dastur\nishlash vaqtida yuzagan kelishi mumkin bo'lgan xatolarni osonlik bilan ushlab uni qayta ishlashimiz mumkin.\n\nQuyidagi kodga qarang:\n```java\n@ExceptionController\npublic class SimpleExceptionHandler {\n\n    @HandleException({RuntimeException.class})\n    public void handleRuntimeException(RuntimeException exception) {\n        // write your logic...\n    }\n}\n```\n\n`@ExceptionController` annotatsiyasi xatolarni qayta ishlovchi class sifatida belgilash uchun qo'yishingiz kerak!.\nushbu annotatsiyani classni ustiga qo'yganingizdan so'ng. Ushbu class ichida xatolarni qayta ishlovchi methodlarni\nyozishingiz mumkin.\n\nXatolarni qayta ishlovchi method yozish qoidalari.\n\n1. Xatolarni qayta ishlovchi method yozishingiz uchun siz qilishingiz shart bo'lgan ish method ustiga `@HandleException`\n   annotatsiyasini qo'yish va annotatsiyada qaysi `Exception`larni handle qilishini ko'rsatishdir.\n2. Methodni hech qanday parametersiz ham yozishingiz ham mumkin. Xatolarni qayta ishlovchi methodlarga 2ta parameter kirib\n   kelishi mumkin birinchisi `Exception`, ikkinchisi esa `Update` bu ikklasini istalgani kutib olishingiz hamda ikkalasini ham\n   kutib olishingiz mumkin. Birinchi `Exception` keyin `Update` kirishi ham majuburiy emas qaysi biri birinchi kirishi ahamiyatsiz asosiysi\n   shu 2ta parameterlarni to'g'ir belgilashingiz.\n\n## 5. Making keyboards\n\nFluent kutubxonasi keyboardlarni yasash uchun ham API larni taqdim etadi. Ushbu API lar keyboardlarni yasashi ancha osonlashtiradi.\n\nFluent API dan foydalanmasdan keyboardlarni yaratish uchun yozilgan kod:\n\n```java\npublic InlineKeyboardMarkup inlineKeyboardMarkup() {\n    InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();\n\n    InlineKeyboardButton next = new InlineKeyboardButton(\"NEXT\");\n    next.setCallbackData(\"NEXT\");\n\n    InlineKeyboardButton prev = new InlineKeyboardButton(\"PREV\");\n    prev.setCallbackData(\"PREV\");\n\n    InlineKeyboardButton top = new InlineKeyboardButton(\"TOP\");\n    top.setCallbackData(\"TOP\");\n\n    InlineKeyboardButton bottom = new InlineKeyboardButton(\"BOTTOM\");\n    bottom.setCallbackData(\"BOTTOM\");\n\n    List\u003cInlineKeyboardButton\u003e row1 = new ArrayList\u003c\u003e();\n    row1.add(next);\n    row1.add(prev);\n\n    List\u003cInlineKeyboardButton\u003e row2 = new ArrayList\u003c\u003e();\n    row2.add(top);\n    row2.add(bottom);\n\n    inlineKeyboardMarkup.setKeyboard(Arrays.asList(row1, row2));\n    return inlineKeyboardMarkup;\n}\n```\n\nFluent API dan foydalanib keyboardlarni yaratish uchun yozilgan kod:\n```java\npublic InlineKeyboardMarkup inlineKeyboardMarkup() {\n    InlineKeyboardMarkupBuilder builder = new InlineKeyboardMarkupBuilder();\n\n    builder.addButton(\"NEXT\")\n            .callbackData(\"NEXT\");\n    \n    builder.addButton(\"PREV\")\n            .callbackData(\"PREV\");\n\n    builder.addRow();\n\n    builder.addButton(\"TOP\")\n            .callbackData(\"TOP\");\n    \n    builder.addButton(\"BOTTOM\")\n            .callbackData(\"BOTTOM\");\n\n    InlineKeyboardMarkup inlineKeyboardMarkup = builder.build();\n}\n```\n\nTepadagi ko'rsatilgan kodlar ikkalasi ham bir xil keyboardlarni yaratish uchun yozilgan kodlar.\n\nResult:\n\n![inline-keyboards](documentation/images/inline-keyboards.png)\n\n### 5.1 Inline keyboard\n\nInline keyboardlarni yasash uchun ishlashtiladigan class `InlineKeyboardMarkupBuilder`.\nUshbu classdan foydalanib siz inline keyboardlarni osonlik bilan yasashingiz mumkin.\n\nQuyidagi kodga qarang:\n\n```java\npublic InlineKeyboardMarkup inlineKeyboardMarkupExample() {\n    InlineKeyboardMarkupBuilder builder = new InlineKeyboardMarkupBuilder();\n\n    builder.addButton(\"Next\")\n            .callbackData(\"Next\");\n\n    InlineKeyboardMarkup inlineKeyboardMarkup = builder.build();\n}\n```\n\nNatijasi:\n\n![inline-keyboard-result](documentation/images/inline-keyboard-result.png)\n\n`addRow` method foydalanib osongina yangi row qo'shingiz mumkin.\n\n```java\nbuilder.addRow();\n```\n\n```java\npublic InlineKeyboardMarkup inlineKeyboardMarkupExample() {\n    InlineKeyboardMarkupBuilder builder = new InlineKeyboardMarkupBuilder();\n\n    builder.addButton(\"Next\")\n            .callbackData(\"Next\");\n\n    builder.addRow();\n    \n    builder.addButton(\"Prev\")\n          .callbackData(\"Prev\");\n\n    InlineKeyboardMarkup inlineKeyboardMarkup = builder.build();\n}\n```\n\nTepadagi kodni natijasi:\n\n![inline-keyboard-result-2](documentation/images/inline-keyboard-result-2.png)\n\n### 5.2 Reply keyboard\n\nReply keyboardlarni yasash uchun ishlatiladigan class `ReplyKeyboardMarkupBuilder`.\nUshbu classdan foydalanib siz reply keyboardlarni osonlik bilan yasashingiz mumkin huddi inline keyboardga o'xshab.\n\nQuyidagi kodga qarang:\n\n```java\npublic void handleReplyCommand() {\n    ReplyKeyboardMarkupBuilder builder = new ReplyKeyboardMarkupBuilder();\n    builder.oneTimeKeyboard(true);\n\n    builder.addButton(\"First\");\n    builder.addButton(\"Second\");\n\n    builder.addRow();\n\n    builder.addButton(\"Third\");\n    builder.addButton(\"Fourth\");\n\n    ReplyKeyboardMarkup replyKeyboardMarkup = builder.build();\n}\n```\n\nNatijasi:\n\n![reply-keyboard.png](documentation/images/reply-keyboard.png)\n\n## 6. Interceptors\n\nFluent kutubxonasi sizga Interceptorlarni qo'shish imkonini beradi. Interceptor nima ekanligini bilmaydiganlarga\nuchun istalgan action chaqiruvchidan oldin va keyin framework tomonidan chaqiriladigan funksiya yani method.\nSpring Interceptorlari httpdan kirib request kelganda va chiqib ketayotgan chiqariladi. Fluent tomonidan taqdim etiladigan\ninterceptorlar ham huddi shunday faqat telegramdan kirib kelgan requestlar uchun ishlaydi.\n\nQuyidagi kodga qarang:\n\n```java\n@Slf4j\npublic class SimpleFluentInterceptor implements FluentInterceptor {\n\n    @Override\n    public boolean preHandle(Update update) {\n        log.info(\"FluentInterceptor handle update! : {}\", update.getUpdateId());\n        return FluentInterceptor.super.preHandle(update);\n    }\n\n    @Override\n    public void postHandle(Update update) {\n        log.warn(\"FluentInterceptor handle update end!\");\n    }\n}\n```\n```java\n@Configuration\npublic class FluentInterceptorConfig {\n\n    private final FluentInterceptorRegistryContainer registryContainer;\n\n    public FluentInterceptorConfig(FluentInterceptorRegistryContainer registryContainer) {\n        this.registryContainer = registryContainer;\n    }\n\n    @PostConstruct\n    public void registerFluentInterceptor() {\n        registryContainer.addFluentInterceptor(new SimpleFluentInterceptor());\n    }\n}\n```\n\nNatijasi:\n\n![interceptor](documentation/images/fluent-interceptor.png)\n\nBirinchi `SimpleFluentInterceptor` classi bu interceptor. Fluentda interceptor yozishingiz uchun `FluentInterceptor`\ninterfacedan implementatsiya olishingiz va uni ichida kerakli logikangizni yozishingiz mumkin bo'ladi.\n\nIkkinchi `FluentInterceptorConfig` classi esa oldin yozilgan `SimpleFluentInterceptor` ni ro'yxatdan o'tkazish uchun\nyozildi boshqacha qilib aytganda yozgan interceptorimizni Fluent kutubxonasi tanib olishi uchun uni ro'yxatdan o'tkazishimiz\nkerak bo'ladi. `FluentInterceptorRegistryContainer` classini siz interceptorlarni ro'yxatdan o'tkazish uchun ishlatasiz.\n\n## 7. State\n\nFluent kutubxonasi statelardan foydalanish imkoniyatini taqdim etadi. Siz bu imkoniyatdan foydalanib ketma-ket bajarilishi\nkerak bo'lgan murakkabroq jarayonlarni osonlik bilan yozishingiz mumkin. Misol uchun botda authentication qilish jarayoni.\nUshbu jarayoni commandalar bilan qilish to'g'ri bolmaydi sababi ketma ketlikni buzib yuborish mumkin osonlik bilan state\nbilan shu jarayon qilishingiz mumkin osonlik bilan. fluentni state foydalanmoqchi bo'lsangiz state design pattern o'qib\nchiqingizni maslahat beramiz. Sababi fluent state, state design pattern bir xil yoziladi va ishlaydi.\n\n### 7.1 Dependency\n\nFluent State dan foydalanish uchun birinchi qo'shimcha dependency qo'shishingiz kerak.\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.nurislom373\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-starter-fluent-state\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```groovy\nimplementation group: 'io.github.nurislom373', name: 'spring-boot-starter-fluent-state', version: '1.2.2'\n```\n\n### 7.2 Process Type\n\nUshbu dependency qo'shganingizdan so'ng qilishingiz kerak bo'lgan keyingi ish configration filedan `process-type` ni\nto'g'irlashingiz kerak\n\n```yaml\nfluent:\n  bot:\n    token: \u003cyour bot token\u003e\n    username: \u003cyour bot username\u003e\n    process-type: update\n```\n\nEski holatida `process-type` update turib endi siz uni `both` yoki `state` qilishingiz kerak state bilan ishlashingiz uchun.\n`process-type` orqali biz handler va state larni fluentga ishlatishi kerakligi yoki aks qilishini ko'rsatish uchun ishlatamiz.\n\n### 7.3 Declaring states\n\nProcess Type ni to'g'irlab bo'lganingizdan so'ng endi State larni e'lon qilishingiz kerak bo'ladi. State larni bitta enum\nichida e'lon qilishingiz kerak.\n\nQuyidagi kodga qarang:\n\n```java\npublic enum SimpleState {\n    START, CHECK, END\n}\n```\n\n### 7.4 State configurer\n\nStatelarni e'lon qilib bo'lganingizdan so'ng endi statelarni konfiguratsiya qilishingiz kerak bo'ladi. Buning uchun\n`StateConfigurerAdapter` interfaceda implementatsiya olgan bean yozishingiz kerak. Bean bitta bo'lishi majburiy agar\nyo'q bo'lsa yoki bittadan ko'p bo'lsa xatolik yuzaga keladi.\n\nQuyidagi kodga qarang:\n\n```java\n@SpringBootApplication\npublic class FluentTestApplication implements StateConfigurerAdapter\u003cSimpleState\u003e {\n\n    public static void main(String[] args) {\n        SpringApplication.run(FluentTestApplication.class, args);\n    }\n\n    @Override\n    public void configure(StateConfigurer\u003cSimpleState\u003e state) {\n        state.initial(SimpleState.START)\n                .states(EnumSet.allOf(SimpleState.class));\n    }\n}\n```\n\n`StateConfigurerAdapter` interfaceni bitta `configure` method ushbu method qayta yozishingiz kerak bo'ladi tepadagi kodga\no'xshatib. Configure methodda parameter sifatida kirib kelgan `StateConfigurer` da statelarni ko'rsatishingiz kerak bo'ladi.\n\n- `initial`: foydalanuvchi botga request yuborishi bilan default holat ushbu `initial` state belgilanadi va saqlab qoyiladi.\n- `states`: methodida esa barcha statelarni ro'yxatdan o'tkazish uchun ishlatiladi. Statesda ko'rsatilgan statelargina\n  dasturda ishlatiladi.\n\n### 7.5 Writing state\n\nStateni konfiguratsiya qilishni tugatganingizdan so'ng statelarni yozishingiz mumkin bo'ladi. Statelarni yozish uchun\n`StateAction` interface implementatsiya olgan class bo'lishi kerak va ushbu class bean bo'lishi kerak.\n\nQuyidagi kodga qarang:\n\n```java\n@Slf4j\n@Component\npublic class StartState implements StateAction\u003cSimpleState\u003e {\n\n    private final FluentTemplate fluentTemplate;\n\n    public StartState(FluentTemplate fluentTemplate) {\n        this.fluentTemplate = fluentTemplate;\n    }\n\n    @Override\n    public void onReceive(Update update, State state) {\n        log.info(\"Hello World I'm Start State\");\n        fluentTemplate.sendText(\"Hello World I'm Start State\");\n        state.nextState();\n    }\n\n    @Override\n    public SimpleState state() {\n        return SimpleState.START;\n    }\n}\n```\n\n- `onReceive`: method ichida o'zingizga kerakli business logikangizni yozishingiz mumkin.\n- `state`: methodida esa qaysi state ekanligini ko'rsatishingiz kerak!.\n\nparameter sifatida kirib kelgan `State` esa bu kirib kelgan updateni state yani updateni yuborgan foydalanuvchini holati.\nUshbu `State` interfaceni `nextState` method bu enumda yozilgan ketma ketlik bo'yicha foydalanuvchini stateni undan\nkeyingisiga o'tkazadi. Misol uchun `SimpleState` da `START` statedan keyin `CHECK` turibdi `nextState` methodi `START`\nstateni o'zi `CHECK` o'tkazib qoyadi yani o'zidan keyingisiga. Agar keyingisiga emas oldingisiga yoki bir nechta keyingi\nstatega o'tmoqchi bo'lsangiz huddi shu methodni state qabul qiluvchi varianti ham mavjud. Ushbu variantidan foydalanib\nhohlagan foydalanuvchingizni stateni o'zgartirishingiz mumkin.\n\n### 7.6 Update Handlers proceed\n\nState lar default holatda tepada aytib o'tilganidek commanda sifatida yuborilgan requestlarni qabul qilmaydi yani `@Handle...`\nbilan boshlanadigan annotatsiya qo'yilgan handlerlaringiz ishlamaydi buning sababi `StateAction` interfaceni `updateHandlersProceed`\nmethod default holatda false qaytaradi yani update handlerlarni ishlashini cheklaydi. Sizga state ham update handler lar\nishlashi kerak bo'lganda ushbu method override qilib `true` qiymat qaytarishingiz kerak shunda update handlerlar va state\nbirga ishlab ketaveradi.\n\nQuyidagi kodga qarang:\n\n```java\n@Slf4j\n@Component\npublic class StartState implements StateAction\u003cSimpleState\u003e {\n\n    private final FluentTemplate fluentTemplate;\n\n    public StartState(FluentTemplate fluentTemplate) {\n        this.fluentTemplate = fluentTemplate;\n    }\n\n    @Override\n    public void onReceive(Update update, State state) {\n        log.info(\"Hello World I'm Start State\");\n        fluentTemplate.sendText(\"Hello World I'm Start State\");\n        state.nextState();\n    }\n\n    @Override\n    public SimpleState state() {\n        return SimpleState.START;\n    }\n\n    @Override\n    public boolean updateHandlersProceed() {\n        return true;\n    }\n}\n```\n\n### 7.7 State repository\n\nState lar saqlanadigan repositorydan foydalanib foydalanuvchilarni statelarni olishingiz ham mumkin. Ushbu repositorydan\nfoydalanib statelarni manipulatsiya qilishingiz ham mumkin. Ushbu repositoryni nomi `StateRepositoryStrategy` interfacesi.\nUshbu interfacedan implementatsiya olib o'zingiz statelarni hohlagancha boshqarishingiz ham mumkin yani statelar qayerdan\nsaqlanishini va hokazolarni.\n\n## 8. State configure postgresql\n\nStatelarni default holatda hotirada saqlanadi bu kamchiliklaridan biri dasturni o'chirganingizda barcha foydalanuvchilarni\nstatelari o'chib ketadi. Shuning uchun siz statelarni ma'lumotlar bazasidan birida saqlashingiz kerak. fluent sizga postgresql\nbilan osonlik bilan integratsiya qilish imkoni beradi. Sizni qilishingiz kerak bo'lgan narsa faqat ma'lumotlar bazasini\ncredentiallarni sozlash shundan so'ng fluent siz sozlagan baza bilan to'g'ridan to'g'ri ishlashni boshlaydi va statelarni\nushbu ma'lumotlar bazasida saqlaydi va dasturni ochirib yoqsangiz ham ma'luomotlar o'chib ketmaydi.\n\nQilishingiz kerak bo'lgan birinchi ish dependency qo'shish.\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.nurislom373\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-fluent-postgresql\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nKeyingi qilishingiz kerak bo'lgan ish konfiguratsiyalarni sozlash.\n\n```yaml\nfluent:\n  bot:\n    token: \u003cyour_token\u003e\n    username: \u003cyour_bot_username\u003e\n    process-type: both\n  datasource:\n    driver-class-name: org.postgresql.Driver\n    type: com.zaxxer.hikari.HikariDataSource\n    jdbc-url: jdbc:postgresql://localhost:5432/fluent\n    username: fluent\n    password: 123\n    properties:\n      hibernate:\n        hbm2ddl:\n          auto: update\n        show-sql: true\n```\n\n`fluent.datasource` ichida kerakli sozlamalarni yozishingiz kerak bo'ladi tepadagi misolga o'xshab.\n\n## 9. Conditions\n\nFluent kutubxonasi conditionlani yozish imkoniyatni beradi. U ma'lum bir Controller yoki Handler larning belgilangan\nshart asosida ishlashini ko'rsatish uchun ishlatiladi.\n\nMisol uchun sizda controller class bor va undagi barcha handlerlarni ma'lum bir state holatida bo'lgan ishlatishni hohlaysiz.\nShunda conditiondan foydalanishingiz mumkin.\n\nQuyidagi kodga qarang:\n\n```java\n@UpdateController\n@ConditionOnState({\"CHECK\"})\npublic class FluentController {\n    \n    // ...\n    \n}\n```\n\nTepadagi kodda state `CHECK` bo'lgandagina faqat ushbu controller classi ichidagi handlerlar ishlaydi.\n\nEndi Fluent tomonidan taqdim etilgan condition annotatsiyalarini birma bir ko'rib chiqamiz.\n\n### 9.1 Expression\n\n`@ConditionOnExpression` annotatsiya yordamida siz expression yozishingiz mumkin. Siz yozgan expression to'g'ri bo'lsagina\nHandler ishlaydi.\n\nQuyidagi kodga qarang:\n\n```java\n@ConditionOnExpression(\"1 == 1\")\n@ConditionOnExpression(\"2 == 2\")\n@HandleMessage(value = \"/start\", match = MatchType.STARTS_WITH)\npublic void fluent(Update update) {\n    // ...\n}\n```\n\nTepadagi kodda 2ta condition yozilgan 2lasi ham bajarilsagina ushbu handler ishlaydi.\n\n`@ConditionOnExpression` annotatsiyasida siz boshqa bir bean ni method chaqirishingiz mumkin.\n\n```java\n@HandleMessage(\"/template\")\n@ConditionOnExpression(\"#{conditionBean.exist()}\")\npublic void templateExample(Update update) {\n    // ...\n}\n```\n\nConditionBean\n\n```java\n@Service(\"conditionBean\")\npublic class ConditionBean {\n\n    public boolean exist() {\n        return true; \n    }\n}\n```\n\n### 9.2 State\n\n`@ConditionOnState` annotatsiya yordamida ma'lum bir yoki bir nechta state da bo'lsagina foydalanuvchi ushbu handler ishlaydi.\n\nQuyidagi kodga qarang:\n\n```java\n@ConditionOnState({\"CHECK\"})\n@HandleMessage(value = \"message:\", match = MatchType.STARTS_WITH)\npublic void startWithExampleHandler() {\n    // ...\n}\n```\n\n### 9.3 Custom\n\nFluent tomondan taqdim etilgan condition annotatsiyalari sizga yetarli bo'lmasa o'zingizga kerakli annotatsiyasini yozishingiz mumkin.\n\nQuyidagi kodga qarang:\n\nBirinchi qilishingiz kerak bo'lgan ish `FluentCondition` interfacedan implementatsiya olgan class yaratishingiz kerak.\n`matches` method ichida esa o'zingiz kerakli condition yozishingiz mumkin.\n\n- `Attributes`: parameterida sizga kerakli parameterlar kirib keladi misol uchun `update`\n- `AnnotatedTypeMetadata`: parameterida qaysi method yoki classni ustiga qoyilgani haqida ma'lumotlar\n\n```java\npublic class CustomFluentCondition implements FluentCondition {\n\n    @Override\n    public boolean matches(Attributes attributes, AnnotatedTypeMetadata metadata) {\n        Update update = (Update) attributes.getAttribute(AttributesConstants.UPDATE_ATTRIBUTE);\n        return update.hasMessage();\n    }\n}\n```\n\nKeyingi qilishingiz kerak bo'lgan ish annotatsiya yozish va unda ushbu tepada yozilgan classni belgilab qoyish.\n\n```java\n@ProcessCondition\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.METHOD})\n@Condition(value = CustomFluentCondition.class)\npublic @interface CustomCondition {\n}\n```\n\n```java\n@CustomCondition\n@HandleAny(type = {HandleType.MESSAGE, HandleType.AUDIO})\nprivate void handleAnyUpdate(Update update) {\n    // ...\n}\n```\n\n## 10. Inline Query\n\nInline Query lar ko'p ishlatilmagani uchun uni alohida modul sifatida chiqarilgan yani faqat kerak bo'lganda kerakli\ndependency qo'shish orqali ishlatishingiz mumkin.\n\n### 10.1 Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.nurislom373\u003c/groupId\u003e\n    \u003cartifactId\u003efluent-inline-query\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### 10.2 Gradle\n\n```groovy\nimplementation group: 'io.github.nurislom373', name: 'fluent-inline-query', version: '1.2.2'\n```\n\n### 10.3 Enabling inline mode\n\nBy defualt, botingiz uchun inline rejimi o'chirib qo'yilgan bo'ladi. Siz @BotFather bilan bog'lanishingiz va botingiz inline\nquerylarni olishni boshlashi uchun inline rejimini yoqishingiz kerak.\n\n### 10.4 Handling\n\nTepada ko'rsatilgan dependency ni qo'shganingizdan so'ng. `@HandleInlineQuery` va `@HandleChosenInlineQuery` annotatsiyalaridan\nfoydalanib inline querylar bilan ishlashingiz mumkin.\n\n```java\n@HandleInlineQuery\npublic void handleInlineQuery(Update update, String query) {\n    // ...\n}\n\n@HandleChosenInlineQuery\npublic void handleChosenInlineQuery(Update update, ChosenInlineQueryParam param) {\n    log.info(\"chosen param : {}\", param);\n}\n```\n\n`Update` parameterini yonidagi parameter kutishingiz majburiy emas!.\n\n## 11. Customization\n\nfluent kutubxonasini o'zingizga moslashtirib olishingiz misol uchun kutubxona tomondan taqdim etilgan annotatsiyalar\nsizga yetarli bo'lamayabdi o'zngizga kerakli annotatsiyani yozib olishingiz mumkin hech qanday muammosiz. Yana bir misol\nstate hozircha faqat postgresql bilan integratsiyasi mavjud bu ham sizga yetarli nosql ma'lumotlar bazasi bilan ishlamoqchisiz\nbu ham muammo emas kerakli interfaceni implementatsiya qilib o'zingizga moslashingiz mumkin. Qisqa qilib aytganda fluent\nkutubxonasini o'zingiz qiynalmasdan kengaytirishingiz mumkin kutubxonadagi kodlar SOLID prinspiga toliq\nholda amal qilib yozilgan :).\n\n# Contributors\n\n\u003ctable\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Nurislom373\"\u003e\u003cimg src=\"https://github.com/Nurislom373/khasanof-common-util/blob/main/etc/images/khasanof.png\" width=\"100px;\" alt=\"Nurislom Xasanov\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNurislom Xasanov\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n            \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Nurislom373\"\u003e\u003cimg src=\"https://github.com/Nurislom373/fluent/blob/main/etc/images/abdulloh.jpg\" width=\"100px;\" alt=\"Shukrullaev Abdulloh\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eShukrullaev Abdulloh\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n            \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Nurislom373\"\u003e\u003cimg src=\"https://github.com/Nurislom373/fluent/blob/main/etc/images/shoxista.jpg\" width=\"100px;\" alt=\"Rahimova Shoxista\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eRahimova Shoxista\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n            \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Nurislom373\"\u003e\u003cimg src=\"https://github.com/Nurislom373/fluent/blob/main/etc/images/jlkesh.jpg\" width=\"100px;\" alt=\"Elmurodov Javohir\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eElmurodov Javohir\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#\" title=\"Code\"\u003e🌍💻\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\n## License\n\n[MIT License](https://github.com/Nurislom373/Fluent-Doc/blob/main/LICENSE)\n\n© 2023 Nurislom\n\nYou have permission to use this software for free. For more details, check the [full license](https://github.com/Nurislom373/Fluent-Doc/blob/main/LICENSE).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnurislom373%2Ffluent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnurislom373%2Ffluent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnurislom373%2Ffluent/lists"}