pedrobaeza: And what about messages like
``` 
Great contribution, thanks!

/ocabot merge
```
?
pedrobaeza: Well, more this one (the previous one was covered):
``` 
Great contribution, thanks!

/ocabot merge

Please forward port it to 12.0.
```

sbidoul: @pedrobaeza should be ok.
diff --git a/HISTORY.rst b/HISTORY.rst
index 0f9ae33..08d2ad2 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,6 +1,10 @@
 next
 ~~~~
 
+**Features**
+
+- Improved command parser (#53)
+
 **Bug fixes**
 
 - Do not attempt to build wheels for uninstallable addons.
diff --git a/src/oca_github_bot/commands.py b/src/oca_github_bot/commands.py
index e377d69..7aba1e7 100644
--- a/src/oca_github_bot/commands.py
+++ b/src/oca_github_bot/commands.py
@@ -6,7 +6,7 @@ import re
 from .tasks import merge_bot
 
 BOT_COMMAND_RE = re.compile(
-    r"/ocabot +(?P<command>\w+)( +(?P<options>.*?))? *$", re.MULTILINE
+    r"/ocabot[ \t]+(?P<command>\w+)(?P<options>[ \t\w]*)(\W|\r?$)", re.MULTILINE
 )
 
 
@@ -23,6 +23,7 @@ class InvalidOptionsError(Exception):
 class BotCommand:
     def __init__(self, name, options):
         self.name = name
+        self.options = options
         self.parse_options(options)
 
     @classmethod
@@ -46,8 +47,8 @@ class BotCommandMerge(BotCommand):
     def parse_options(self, options):
         if not options:
             return
-        if options in ("major", "minor", "patch"):
-            self.bumpversion = options
+        if len(options) == 1 and options[0] in ("major", "minor", "patch"):
+            self.bumpversion = options[0]
         else:
             raise InvalidOptionsError(self.name, options)
 
@@ -60,4 +61,6 @@ class BotCommandMerge(BotCommand):
 def parse_commands(text):
     """ Parse a text and return an iterator of BotCommand objects. """
     for mo in BOT_COMMAND_RE.finditer(text):
-        yield BotCommand.create(mo.group("command"), mo.group("options"))
+        yield BotCommand.create(
+            mo.group("command"), mo.group("options").strip().split()
+        )

merge bot: parse command inside a greater message
For now, the merge bot only reacts to the merge command putting that comment in an isolated message (example of greater message: https://github.com/OCA/account-invoicing/pull/552#issuecomment-507203903).
diff --git a/tests/test_commands.py b/tests/test_commands.py
index 1195b53..2091988 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -22,11 +22,38 @@ def test_parse_command_multi():
                 /ocabot merge major
                 /ocabot   merge   patch
                 /ocabot merge patch
+                /ocabot merge, please
+                /ocabot merge  minor, please
+                /ocabot merge minor, please
+                /ocabot merge.
+                /ocabot merge patch. blah
+                /ocabot merge minor # ignored
                 ...
             """
         )
     )
-    assert len(cmds) == 3
+    assert [(cmd.name, cmd.options) for cmd in cmds] == [
+        ("merge", ["major"]),
+        ("merge", ["patch"]),
+        ("merge", ["patch"]),
+        ("merge", []),
+        ("merge", ["minor"]),
+        ("merge", ["minor"]),
+        ("merge", []),
+        ("merge", ["patch"]),
+        ("merge", ["minor"]),
+    ]
+
+
+def test_parse_command_2():
+    cmds = list(
+        parse_commands(
+            "Great contribution, thanks!\r\n\r\n"
+            "/ocabot merge\r\n\r\n"
+            "Please forward port it to 12.0."
+        )
+    )
+    assert [(cmd.name, cmd.options) for cmd in cmds] == [("merge", [])]
 
 
 def test_parse_command_merge():

pip install -e .[dev]
parse_log_pytest
pytest
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohappyeyeballs==2.6.1
aiohttp==3.11.14
aiosignal==1.3.2
amqp==5.3.1
appdirs==1.4.4
async-timeout==5.0.1
attrs==25.3.0
backports.tarfile==1.2.0
billiard==4.2.1
celery==5.4.0
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
click-didyoumean==0.3.1
click-plugins==1.1.1
click-repl==0.3.0
cryptography==44.0.2
docutils==0.21.2
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
flower==2.0.1
frozenlist==1.5.0
gidgethub==5.3.0
github3.py==4.0.1
humanize==4.12.2
id==1.5.0
idna==3.10
importlib_metadata==8.6.1
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
keyring==25.6.0
kombu==5.5.2
markdown-it-py==3.0.0
mdurl==0.1.2
more-itertools==10.6.0
multidict==6.2.0
nh3==0.2.21
-e git+https://github.com/OCA/oca-github-bot.git@0ab1c02e2ef25e2b3231a326e04c31b40710ce6f#egg=oca_github_bot
OdooRPC==0.10.1
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
prometheus_client==0.21.1
prompt_toolkit==3.0.50
propcache==0.3.1
pycparser==2.22
Pygments==2.19.1
PyJWT==2.10.1
pytest @ file:///croot/pytest_1738938843180/work
python-dateutil==2.9.0.post0
pytz==2025.2
raven==6.10.0
readme_renderer==44.0
redis==5.2.1
requests==2.32.3
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
six==1.17.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
tornado==6.4.2
twine==6.1.0
typing_extensions==4.12.2
tzdata==2025.2
uritemplate==4.1.1
urllib3==2.3.0
vine==5.1.0
wcwidth==0.2.13
yarl==1.18.3
zipp==3.21.0

name: oca-github-bot
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohappyeyeballs==2.6.1
      - aiohttp==3.11.14
      - aiosignal==1.3.2
      - amqp==5.3.1
      - appdirs==1.4.4
      - async-timeout==5.0.1
      - attrs==25.3.0
      - backports-tarfile==1.2.0
      - billiard==4.2.1
      - celery==5.4.0
      - certifi==2025.1.31
      - cffi==1.17.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - click-didyoumean==0.3.1
      - click-plugins==1.1.1
      - click-repl==0.3.0
      - cryptography==44.0.2
      - docutils==0.21.2
      - flower==2.0.1
      - frozenlist==1.5.0
      - gidgethub==5.3.0
      - github3-py==4.0.1
      - humanize==4.12.2
      - id==1.5.0
      - idna==3.10
      - importlib-metadata==8.6.1
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - keyring==25.6.0
      - kombu==5.5.2
      - markdown-it-py==3.0.0
      - mdurl==0.1.2
      - more-itertools==10.6.0
      - multidict==6.2.0
      - nh3==0.2.21
      - odoorpc==0.10.1
      - prometheus-client==0.21.1
      - prompt-toolkit==3.0.50
      - propcache==0.3.1
      - pycparser==2.22
      - pygments==2.19.1
      - pyjwt==2.10.1
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - raven==6.10.0
      - readme-renderer==44.0
      - redis==5.2.1
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - six==1.17.0
      - tornado==6.4.2
      - twine==6.1.0
      - typing-extensions==4.12.2
      - tzdata==2025.2
      - uritemplate==4.1.1
      - urllib3==2.3.0
      - vine==5.1.0
      - wcwidth==0.2.13
      - yarl==1.18.3
      - zipp==3.21.0
prefix: /opt/conda/envs/oca-github-bot

tests/test_commands.py::test_parse_command_multi tests/test_commands.py::test_parse_command_2
tests/test_commands.py::test_parse_command_not_a_command tests/test_commands.py::test_parse_command_merge
HISTORY.rst src/oca_github_bot/commands.py
HISTORY.rst src/oca_github_bot/commands.py