csp-bot 2.0.3__tar.gz → 2.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {csp_bot-2.0.3 → csp_bot-2.0.4}/PKG-INFO +1 -1
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/__init__.py +1 -1
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/agent.py +5 -1
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/base.py +4 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/framework.py +11 -1
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/help.py +8 -2
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/legacy.py +4 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_agent_command.py +30 -0
- csp_bot-2.0.4/csp_bot/tests/test_help_command.py +115 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/pyproject.toml +2 -2
- csp_bot-2.0.3/csp_bot/tests/test_help_command.py +0 -63
- {csp_bot-2.0.3 → csp_bot-2.0.4}/.gitignore +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/LICENSE +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/README.md +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/backends/__init__.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/backends/discord.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/backends/slack.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/backends/symphony.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/backends/telegram.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/bot.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/bot_config.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/cli.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/__init__.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/claude_agent.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/context.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/echo.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/executor.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/mcp.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/schedule.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/commands/status.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/__init__.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/backend/discord.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/backend/slack.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/backend/symphony.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/backend/symphony_rsa.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/backend/telegram.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/commands.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/conf.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/all.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/bot.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/discord.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/mixed.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/slack.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/symphony.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/gateway/telegram.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/hydra/job_logging/custom.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/config/modules.yaml +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/gateway/__init__.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/gateway/gateway.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/persistence.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/structs.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/__init__.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/conftest.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_agent_mcp.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_backends.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_bot.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_bot_integration.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_claude_agent.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_command_framework.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_config.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_config_composition.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_echo.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_persistence.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_schedule.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_slack_entity_regex.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_status.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_structs.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/tests/test_utils.py +0 -0
- {csp_bot-2.0.3 → csp_bot-2.0.4}/csp_bot/utils.py +0 -0
|
@@ -381,6 +381,9 @@ class AgentCommand(ReplyCommand):
|
|
|
381
381
|
model_name = kwargs.pop("model_name", None)
|
|
382
382
|
if model_name is not None:
|
|
383
383
|
self.model_name = model_name
|
|
384
|
+
root_prompt = kwargs.pop("root_prompt", None)
|
|
385
|
+
if root_prompt is not None:
|
|
386
|
+
self.root_prompt = root_prompt
|
|
384
387
|
self.mcp_servers = [MCPServerConfig.model_validate(config) for config in kwargs.pop("mcp_servers", [])]
|
|
385
388
|
|
|
386
389
|
@classmethod
|
|
@@ -886,7 +889,8 @@ class AgentCommandModel(BaseCommandModel):
|
|
|
886
889
|
command: type[AgentCommand]
|
|
887
890
|
model_name: str = "claude-sonnet-4-6"
|
|
888
891
|
mcp_servers: list[MCPServerConfig] = Field(default_factory=list)
|
|
892
|
+
root_prompt: str | None = None
|
|
889
893
|
|
|
890
894
|
def create_command(self) -> AgentCommand:
|
|
891
895
|
"""Create the configured agent command."""
|
|
892
|
-
return self.command(model_name=self.model_name, mcp_servers=self.mcp_servers)
|
|
896
|
+
return self.command(model_name=self.model_name, mcp_servers=self.mcp_servers, root_prompt=self.root_prompt)
|
|
@@ -46,6 +46,10 @@ class BaseCommand(ABC):
|
|
|
46
46
|
"""Return the help text for the command."""
|
|
47
47
|
...
|
|
48
48
|
|
|
49
|
+
def usage(self) -> str:
|
|
50
|
+
"""Return optional command syntax, without a label."""
|
|
51
|
+
return ""
|
|
52
|
+
|
|
49
53
|
@abstractmethod
|
|
50
54
|
def num_recipients(self) -> int:
|
|
51
55
|
"""Return how many users this command can tag.
|
|
@@ -31,7 +31,7 @@ _COMMAND_REGISTRY: dict[str, CommandEntry] = {}
|
|
|
31
31
|
class CommandEntry:
|
|
32
32
|
"""Internal registry entry for a command."""
|
|
33
33
|
|
|
34
|
-
__slots__ = ("backends", "handler", "help", "is_class", "name")
|
|
34
|
+
__slots__ = ("backends", "handler", "help", "is_class", "name", "usage")
|
|
35
35
|
|
|
36
36
|
def __init__(
|
|
37
37
|
self,
|
|
@@ -40,18 +40,22 @@ class CommandEntry:
|
|
|
40
40
|
handler: Any,
|
|
41
41
|
backends: list[str] | None = None,
|
|
42
42
|
is_class: bool = False,
|
|
43
|
+
usage: str = "",
|
|
43
44
|
):
|
|
44
45
|
self.name = name
|
|
45
46
|
self.help = help
|
|
46
47
|
self.handler = handler
|
|
47
48
|
self.backends = backends or []
|
|
48
49
|
self.is_class = is_class
|
|
50
|
+
self.usage = usage
|
|
49
51
|
|
|
50
52
|
|
|
51
53
|
def command(
|
|
52
54
|
name: str,
|
|
53
55
|
help: str = "",
|
|
54
56
|
backends: list[str] | None = None,
|
|
57
|
+
*,
|
|
58
|
+
usage: str = "",
|
|
55
59
|
) -> Callable:
|
|
56
60
|
"""Decorator to register a function as a bot command.
|
|
57
61
|
|
|
@@ -67,6 +71,7 @@ def command(
|
|
|
67
71
|
name: The command name (e.g. "echo" for /echo).
|
|
68
72
|
help: Help text shown by the /help command.
|
|
69
73
|
backends: List of backends this command supports. Empty = all.
|
|
74
|
+
usage: Optional command syntax, without a label.
|
|
70
75
|
|
|
71
76
|
Returns:
|
|
72
77
|
The original function, registered in the global command registry.
|
|
@@ -85,12 +90,14 @@ def command(
|
|
|
85
90
|
handler=fn,
|
|
86
91
|
backends=backends,
|
|
87
92
|
is_class=False,
|
|
93
|
+
usage=usage,
|
|
88
94
|
)
|
|
89
95
|
_COMMAND_REGISTRY[name] = entry
|
|
90
96
|
# Stash metadata on the function for introspection
|
|
91
97
|
fn._command_name = name
|
|
92
98
|
fn._command_help = help
|
|
93
99
|
fn._command_backends = backends or []
|
|
100
|
+
fn._command_usage = usage
|
|
94
101
|
return fn
|
|
95
102
|
|
|
96
103
|
return decorator
|
|
@@ -133,6 +140,9 @@ class Command(BaseModel):
|
|
|
133
140
|
help: str = ""
|
|
134
141
|
"""Help text shown by the /help command."""
|
|
135
142
|
|
|
143
|
+
usage: str = ""
|
|
144
|
+
"""Optional command syntax, without a label."""
|
|
145
|
+
|
|
136
146
|
backends: list[str] = Field(default_factory=list)
|
|
137
147
|
"""Backends this command supports. Empty = all."""
|
|
138
148
|
|
|
@@ -29,8 +29,14 @@ def _command_name(runner: Any) -> str:
|
|
|
29
29
|
|
|
30
30
|
def _command_help(runner: Any) -> str:
|
|
31
31
|
if isinstance(runner, BaseCommand):
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
description = runner.help()
|
|
33
|
+
usage = runner.usage()
|
|
34
|
+
else:
|
|
35
|
+
description = getattr(runner, "help", "") or ""
|
|
36
|
+
usage = getattr(runner, "usage", "") or ""
|
|
37
|
+
if not usage:
|
|
38
|
+
return description
|
|
39
|
+
return f"{description} Syntax: {usage}" if description else f"Syntax: {usage}"
|
|
34
40
|
|
|
35
41
|
|
|
36
42
|
def _render_table_help(rows: list, backend: str) -> str:
|
|
@@ -180,6 +180,36 @@ class TestGetModel:
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
class TestAgentCommandModel:
|
|
183
|
+
def test_creates_command_with_configured_root_prompt(self, bot_command):
|
|
184
|
+
model = AgentCommandModel(command=ConcreteAgentCommand, root_prompt="Use the project handbook for project questions.")
|
|
185
|
+
|
|
186
|
+
command = model.create_command()
|
|
187
|
+
|
|
188
|
+
assert model.root_prompt is not None
|
|
189
|
+
assert command.root_prompt == model.root_prompt
|
|
190
|
+
assert command.build_root_prompt(bot_command) == model.root_prompt
|
|
191
|
+
assert command._prompt_prefix(bot_command).startswith(model.root_prompt)
|
|
192
|
+
|
|
193
|
+
@pytest.mark.parametrize(
|
|
194
|
+
"configured,expected",
|
|
195
|
+
[(None, "Default project context."), ("", ""), ("Custom project context.", "Custom project context.")],
|
|
196
|
+
)
|
|
197
|
+
def test_root_prompt_preserves_defaults_or_uses_explicit_override(self, configured, expected):
|
|
198
|
+
class DefaultPromptCommand(ConcreteAgentCommand):
|
|
199
|
+
root_prompt = "Default project context."
|
|
200
|
+
|
|
201
|
+
model = AgentCommandModel(command=DefaultPromptCommand, root_prompt=configured)
|
|
202
|
+
|
|
203
|
+
assert model.create_command().root_prompt == expected
|
|
204
|
+
assert DefaultPromptCommand.root_prompt == "Default project context."
|
|
205
|
+
|
|
206
|
+
def test_root_prompt_is_instance_specific(self):
|
|
207
|
+
command = ConcreteAgentCommand(root_prompt="Custom project context.")
|
|
208
|
+
|
|
209
|
+
assert command.root_prompt == "Custom project context."
|
|
210
|
+
assert ConcreteAgentCommand().root_prompt == ""
|
|
211
|
+
assert ConcreteAgentCommand.root_prompt == ""
|
|
212
|
+
|
|
183
213
|
def test_creates_command_with_configured_model_name(self):
|
|
184
214
|
model = AgentCommandModel(command=ConcreteAgentCommand, model_name="github-copilot:test-model")
|
|
185
215
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Tests for help command rendering."""
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from chatom import Message, User
|
|
7
|
+
|
|
8
|
+
from csp_bot.commands.echo import EchoCommand
|
|
9
|
+
from csp_bot.commands.framework import Command, clear_registry, command, get_registered_commands
|
|
10
|
+
from csp_bot.commands.help import HelpCommand
|
|
11
|
+
from csp_bot.commands.legacy import LegacyCommandAdapter
|
|
12
|
+
from csp_bot.structs import BotCommand, CommandVariant
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _command(backend: str) -> BotCommand:
|
|
16
|
+
return BotCommand(
|
|
17
|
+
command="help",
|
|
18
|
+
args=(),
|
|
19
|
+
source=User(id="user1"),
|
|
20
|
+
targets=(),
|
|
21
|
+
channel_id="channel1",
|
|
22
|
+
channel_name="general",
|
|
23
|
+
backend=backend,
|
|
24
|
+
variant=CommandVariant.REPLY,
|
|
25
|
+
message=Message(id="msg1", content="!help"),
|
|
26
|
+
delay=None,
|
|
27
|
+
schedule="",
|
|
28
|
+
times_run=0,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _commands():
|
|
33
|
+
return {
|
|
34
|
+
"ask": SimpleNamespace(name="Ask", help="Ask <anything> & get an answer", backends=[]),
|
|
35
|
+
"help": SimpleNamespace(name="Help", help="Get help with bot commands", backends=[]),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_help_discord_uses_list_not_markdown_table() -> None:
|
|
40
|
+
result = HelpCommand().execute(_command("discord"), _commands())
|
|
41
|
+
|
|
42
|
+
assert result.content.startswith("**Bot Commands Help**")
|
|
43
|
+
assert "- `/ask` **Ask**: Ask <anything> & get an answer" in result.content
|
|
44
|
+
assert "| Command" not in result.content
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_help_slack_uses_list_not_code_block_table() -> None:
|
|
48
|
+
result = HelpCommand().execute(_command("slack"), _commands())
|
|
49
|
+
|
|
50
|
+
assert result.content.startswith("*Bot Commands Help*")
|
|
51
|
+
assert "- `/ask` *Ask*: Ask <anything> & get an answer" in result.content
|
|
52
|
+
assert "```" not in result.content
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_help_telegram_uses_supported_html_list() -> None:
|
|
56
|
+
result = HelpCommand().execute(_command("telegram"), _commands())
|
|
57
|
+
|
|
58
|
+
assert result.content.startswith("<b>Bot Commands Help</b>")
|
|
59
|
+
assert "- <code>/ask</code> <b>Ask</b>: Ask <anything> & get an answer" in result.content
|
|
60
|
+
assert "<table>" not in result.content
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_help_symphony_keeps_table_rendering() -> None:
|
|
64
|
+
result = HelpCommand().execute(_command("symphony"), _commands())
|
|
65
|
+
|
|
66
|
+
assert "<h3>Bot Commands Help</h3>" in result.content
|
|
67
|
+
assert "<table>" in result.content
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.mark.parametrize("backend", ["discord", "slack", "telegram", "symphony"])
|
|
71
|
+
@pytest.mark.parametrize("runner_kind", ["base", "adapter", "metadata", "decorated", "stateful"])
|
|
72
|
+
def test_help_renders_optional_usage_after_description(backend, runner_kind):
|
|
73
|
+
class UsageCommand(EchoCommand):
|
|
74
|
+
def help(self) -> str:
|
|
75
|
+
return "Echo a message."
|
|
76
|
+
|
|
77
|
+
def usage(self) -> str:
|
|
78
|
+
return "/echo message"
|
|
79
|
+
|
|
80
|
+
if runner_kind == "base":
|
|
81
|
+
runner = UsageCommand()
|
|
82
|
+
elif runner_kind == "adapter":
|
|
83
|
+
runner = LegacyCommandAdapter(UsageCommand())
|
|
84
|
+
elif runner_kind == "metadata":
|
|
85
|
+
runner = SimpleNamespace(name="Echo", help="Echo a message.", usage="/echo message", backends=[])
|
|
86
|
+
elif runner_kind == "stateful":
|
|
87
|
+
runner = Command(name="Echo", help="Echo a message.", usage="/echo message")
|
|
88
|
+
else:
|
|
89
|
+
clear_registry()
|
|
90
|
+
try:
|
|
91
|
+
|
|
92
|
+
@command(name="echo", help="Echo a message.", usage="/echo message")
|
|
93
|
+
def echo(context):
|
|
94
|
+
return context.args_text
|
|
95
|
+
|
|
96
|
+
runner = get_registered_commands()["echo"]
|
|
97
|
+
finally:
|
|
98
|
+
clear_registry()
|
|
99
|
+
|
|
100
|
+
result = HelpCommand().execute(_command(backend), {"echo": runner})
|
|
101
|
+
assert "Echo a message. Syntax: /echo message" in result.content
|
|
102
|
+
assert result.content.count("Syntax:") == 1
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_help_only_base_command_keeps_existing_output():
|
|
106
|
+
runner = EchoCommand()
|
|
107
|
+
assert runner.usage() == ""
|
|
108
|
+
result = HelpCommand().execute(_command("discord"), {"echo": runner})
|
|
109
|
+
assert result.content.endswith(f"- `/echo` **{runner.name()}**: {runner.help()}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_help_renders_usage_without_description():
|
|
113
|
+
runner = Command(name="Echo", usage="/echo message")
|
|
114
|
+
result = HelpCommand().execute(_command("discord"), {"echo": runner})
|
|
115
|
+
assert result.content.endswith("- `/echo` **Echo**: Syntax: /echo message")
|
|
@@ -12,7 +12,7 @@ authors = [
|
|
|
12
12
|
description = "Reactive chat bot framework"
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
license = { text = "Apache-2.0" }
|
|
15
|
-
version = "2.0.
|
|
15
|
+
version = "2.0.4"
|
|
16
16
|
requires-python = ">=3.11"
|
|
17
17
|
keywords = [
|
|
18
18
|
"csp",
|
|
@@ -100,7 +100,7 @@ Repository = "https://github.com/Point72/csp-bot"
|
|
|
100
100
|
Homepage = "https://github.com/Point72/csp-bot"
|
|
101
101
|
|
|
102
102
|
[tool.bumpversion]
|
|
103
|
-
current_version = "2.0.
|
|
103
|
+
current_version = "2.0.4"
|
|
104
104
|
commit = true
|
|
105
105
|
tag = true
|
|
106
106
|
commit_args = "-s"
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""Tests for help command rendering."""
|
|
2
|
-
|
|
3
|
-
from types import SimpleNamespace
|
|
4
|
-
|
|
5
|
-
from chatom import Message, User
|
|
6
|
-
|
|
7
|
-
from csp_bot.commands.help import HelpCommand
|
|
8
|
-
from csp_bot.structs import BotCommand, CommandVariant
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _command(backend: str) -> BotCommand:
|
|
12
|
-
return BotCommand(
|
|
13
|
-
command="help",
|
|
14
|
-
args=(),
|
|
15
|
-
source=User(id="user1"),
|
|
16
|
-
targets=(),
|
|
17
|
-
channel_id="channel1",
|
|
18
|
-
channel_name="general",
|
|
19
|
-
backend=backend,
|
|
20
|
-
variant=CommandVariant.REPLY,
|
|
21
|
-
message=Message(id="msg1", content="!help"),
|
|
22
|
-
delay=None,
|
|
23
|
-
schedule="",
|
|
24
|
-
times_run=0,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def _commands():
|
|
29
|
-
return {
|
|
30
|
-
"ask": SimpleNamespace(name="Ask", help="Ask <anything> & get an answer", backends=[]),
|
|
31
|
-
"help": SimpleNamespace(name="Help", help="Get help with bot commands", backends=[]),
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def test_help_discord_uses_list_not_markdown_table() -> None:
|
|
36
|
-
result = HelpCommand().execute(_command("discord"), _commands())
|
|
37
|
-
|
|
38
|
-
assert result.content.startswith("**Bot Commands Help**")
|
|
39
|
-
assert "- `/ask` **Ask**: Ask <anything> & get an answer" in result.content
|
|
40
|
-
assert "| Command" not in result.content
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_help_slack_uses_list_not_code_block_table() -> None:
|
|
44
|
-
result = HelpCommand().execute(_command("slack"), _commands())
|
|
45
|
-
|
|
46
|
-
assert result.content.startswith("*Bot Commands Help*")
|
|
47
|
-
assert "- `/ask` *Ask*: Ask <anything> & get an answer" in result.content
|
|
48
|
-
assert "```" not in result.content
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def test_help_telegram_uses_supported_html_list() -> None:
|
|
52
|
-
result = HelpCommand().execute(_command("telegram"), _commands())
|
|
53
|
-
|
|
54
|
-
assert result.content.startswith("<b>Bot Commands Help</b>")
|
|
55
|
-
assert "- <code>/ask</code> <b>Ask</b>: Ask <anything> & get an answer" in result.content
|
|
56
|
-
assert "<table>" not in result.content
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_help_symphony_keeps_table_rendering() -> None:
|
|
60
|
-
result = HelpCommand().execute(_command("symphony"), _commands())
|
|
61
|
-
|
|
62
|
-
assert "<h3>Bot Commands Help</h3>" in result.content
|
|
63
|
-
assert "<table>" in result.content
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|