Repository: aiogram/aiogram
Pull request #1344 (2023-10-20 13:22:57)

Add new FSM strategy CHAT_TOPIC strategy.py
# Description

The new FSM strategy, which sets the state for the entire topic in the chat, also works in private messages and regular groups without topics.

aiogram did not have an FSM strategy that would set state only for a specific topic in the chat. This strategy is quite reasonable and it has many applications. 
The work of state in private messages remains unchanged
The work of state in groups / super groups without topics, state will be established for all groups
The work of state in groups /super groups with topics, State will be established only for one topic, other topics will not have a state until it is installed, state can be set separately for each topic, and state_data will be separate in all topics where state is installed.


## Type of change

- New feature (non-breaking change which adds functionality)
- This change requires a documentation update

# How Has This Been Tested?

- Run tests/test_fsm/test_strategy.py
- You can run bot with FSMStrategy.CHAT_TOPIC, and make sure that the strategy is working correctly:

   - Check the work of state in private messages
   - Check the work of state in groups/super groups without topics, State should be set for the whole group, since the topic value will be None
   - Check the work of state in groups/super groups with topics, State should be installed only for one topic, other topics will not have a state until it is installed, state can be set separately for each topic, and state_data for should be separate in all topics where state is installed.


Test Configuration:
* Operating System: windows 11
* Python version: 3.11

# Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have made corresponding changes to the documentation
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes

github-actions[bot]: # :heavy_check_mark: Changelog found.

Thank you for adding a description of the changes
OnaZeroN: @JrooTJunior I didn't really understand the error that came out during the tests
JrooTJunior: > @JrooTJunior I didn't really understand the error that came out during the tests

Is coming from the code-style check.
PEP-8 blank lines: https://peps.python.org/pep-0008/#blank-lines
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/aiogram/aiogram/pull/1344?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) Report
> Merging [#1344](https://app.codecov.io/gh/aiogram/aiogram/pull/1344?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) (726f92c) into [dev-3.x](https://app.codecov.io/gh/aiogram/aiogram/commit/3b21262d34c62e74d5a0fb4eb8457b2fff9330eb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) (3b21262) will **not change** coverage.
> The diff coverage is `100.00%`.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/aiogram/aiogram/pull/1344/graphs/tree.svg?width=650&height=150&src=pr&token=cFqEr3p9MX&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram)](https://app.codecov.io/gh/aiogram/aiogram/pull/1344?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram)

```diff
@@            Coverage Diff            @@
##           dev-3.x     #1344   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          369       369           
  Lines         9435      9438    +3     
=========================================
+ Hits          9435      9438    +3     
```

| [Flag](https://app.codecov.io/gh/aiogram/aiogram/pull/1344/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/aiogram/aiogram/pull/1344/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) | `100.00% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files](https://app.codecov.io/gh/aiogram/aiogram/pull/1344?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram) | Coverage Δ | |
|---|---|---|
| [aiogram/fsm/strategy.py](https://app.codecov.io/gh/aiogram/aiogram/pull/1344?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aiogram#diff-YWlvZ3JhbS9mc20vc3RyYXRlZ3kucHk=) | `100.00% <100.00%> (ø)` | |

</details>

Patch:
diff --git a/CHANGES/1343.feature.rst b/CHANGES/1343.feature.rst
new file mode 100644
index 00000000..6ae25cd3
--- /dev/null
+++ b/CHANGES/1343.feature.rst
@@ -0,0 +1,1 @@
+The new FSM strategy CHAT_TOPIC, which sets the state for the entire topic in the chat, also works in private messages and regular groups without topics.
diff --git a/aiogram/fsm/strategy.py b/aiogram/fsm/strategy.py
index 2695d60e..f68aa045 100644
--- a/aiogram/fsm/strategy.py
+++ b/aiogram/fsm/strategy.py
@@ -7,6 +7,7 @@ class FSMStrategy(Enum):
     CHAT = auto()
     GLOBAL_USER = auto()
     USER_IN_TOPIC = auto()
+    CHAT_TOPIC = auto()
 
 
 def apply_strategy(
@@ -21,4 +22,7 @@ def apply_strategy(
         return user_id, user_id, None
     if strategy == FSMStrategy.USER_IN_TOPIC:
         return chat_id, user_id, thread_id
+    if strategy == FSMStrategy.CHAT_TOPIC:
+        return chat_id, chat_id, thread_id
+
     return chat_id, user_id, None


Test patch:
diff --git a/tests/test_fsm/test_strategy.py b/tests/test_fsm/test_strategy.py
index 782d1eeb..1f6f2e93 100644
--- a/tests/test_fsm/test_strategy.py
+++ b/tests/test_fsm/test_strategy.py
@@ -27,6 +27,9 @@ class TestStrategy:
             [FSMStrategy.USER_IN_TOPIC, CHAT, CHAT],
             [FSMStrategy.USER_IN_TOPIC, PRIVATE, PRIVATE],
             [FSMStrategy.USER_IN_TOPIC, THREAD, THREAD],
+            [FSMStrategy.CHAT_TOPIC, CHAT, (CHAT_ID, CHAT_ID, None)],
+            [FSMStrategy.CHAT_TOPIC, PRIVATE, PRIVATE],
+            [FSMStrategy.CHAT_TOPIC, THREAD, (CHAT_ID, CHAT_ID, THREAD_ID)],
         ],
     )
     def test_strategy(self, strategy, case, expected):