camel-ai 0.1.9__py3-none-any.whl → 0.2.3__py3-none-any.whl

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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (102) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +334 -113
  3. camel/agents/knowledge_graph_agent.py +4 -6
  4. camel/bots/__init__.py +34 -0
  5. camel/bots/discord_app.py +138 -0
  6. camel/bots/slack/__init__.py +30 -0
  7. camel/bots/slack/models.py +158 -0
  8. camel/bots/slack/slack_app.py +255 -0
  9. camel/bots/telegram_bot.py +82 -0
  10. camel/configs/__init__.py +1 -2
  11. camel/configs/anthropic_config.py +2 -5
  12. camel/configs/base_config.py +6 -6
  13. camel/configs/gemini_config.py +1 -1
  14. camel/configs/groq_config.py +2 -3
  15. camel/configs/ollama_config.py +1 -2
  16. camel/configs/openai_config.py +2 -23
  17. camel/configs/samba_config.py +2 -2
  18. camel/configs/togetherai_config.py +1 -1
  19. camel/configs/vllm_config.py +1 -1
  20. camel/configs/zhipuai_config.py +2 -3
  21. camel/embeddings/openai_embedding.py +2 -2
  22. camel/loaders/__init__.py +2 -0
  23. camel/loaders/chunkr_reader.py +163 -0
  24. camel/loaders/firecrawl_reader.py +13 -45
  25. camel/loaders/unstructured_io.py +65 -29
  26. camel/messages/__init__.py +1 -0
  27. camel/messages/func_message.py +2 -2
  28. camel/models/__init__.py +2 -4
  29. camel/models/anthropic_model.py +32 -26
  30. camel/models/azure_openai_model.py +39 -36
  31. camel/models/base_model.py +31 -20
  32. camel/models/gemini_model.py +37 -29
  33. camel/models/groq_model.py +29 -23
  34. camel/models/litellm_model.py +44 -61
  35. camel/models/mistral_model.py +33 -30
  36. camel/models/model_factory.py +66 -76
  37. camel/models/nemotron_model.py +33 -23
  38. camel/models/ollama_model.py +42 -47
  39. camel/models/{openai_compatibility_model.py → openai_compatible_model.py} +36 -41
  40. camel/models/openai_model.py +60 -25
  41. camel/models/reka_model.py +30 -28
  42. camel/models/samba_model.py +82 -177
  43. camel/models/stub_model.py +2 -2
  44. camel/models/togetherai_model.py +37 -43
  45. camel/models/vllm_model.py +43 -50
  46. camel/models/zhipuai_model.py +33 -27
  47. camel/retrievers/auto_retriever.py +28 -10
  48. camel/retrievers/vector_retriever.py +72 -44
  49. camel/societies/babyagi_playing.py +6 -3
  50. camel/societies/role_playing.py +17 -3
  51. camel/storages/__init__.py +2 -0
  52. camel/storages/graph_storages/__init__.py +2 -0
  53. camel/storages/graph_storages/graph_element.py +3 -5
  54. camel/storages/graph_storages/nebula_graph.py +547 -0
  55. camel/storages/key_value_storages/json.py +6 -1
  56. camel/tasks/task.py +11 -4
  57. camel/tasks/task_prompt.py +4 -0
  58. camel/toolkits/__init__.py +28 -24
  59. camel/toolkits/arxiv_toolkit.py +155 -0
  60. camel/toolkits/ask_news_toolkit.py +653 -0
  61. camel/toolkits/base.py +2 -3
  62. camel/toolkits/code_execution.py +6 -7
  63. camel/toolkits/dalle_toolkit.py +6 -6
  64. camel/toolkits/{openai_function.py → function_tool.py} +34 -11
  65. camel/toolkits/github_toolkit.py +9 -10
  66. camel/toolkits/google_maps_toolkit.py +7 -14
  67. camel/toolkits/google_scholar_toolkit.py +146 -0
  68. camel/toolkits/linkedin_toolkit.py +7 -10
  69. camel/toolkits/math_toolkit.py +8 -8
  70. camel/toolkits/open_api_toolkit.py +5 -8
  71. camel/toolkits/reddit_toolkit.py +7 -10
  72. camel/toolkits/retrieval_toolkit.py +5 -9
  73. camel/toolkits/search_toolkit.py +9 -9
  74. camel/toolkits/slack_toolkit.py +11 -14
  75. camel/toolkits/twitter_toolkit.py +377 -454
  76. camel/toolkits/weather_toolkit.py +6 -6
  77. camel/toolkits/whatsapp_toolkit.py +177 -0
  78. camel/types/__init__.py +6 -1
  79. camel/types/enums.py +43 -85
  80. camel/types/openai_types.py +3 -0
  81. camel/types/unified_model_type.py +104 -0
  82. camel/utils/__init__.py +0 -2
  83. camel/utils/async_func.py +7 -7
  84. camel/utils/commons.py +40 -4
  85. camel/utils/token_counting.py +38 -214
  86. camel/workforce/__init__.py +6 -6
  87. camel/workforce/base.py +9 -5
  88. camel/workforce/prompts.py +179 -0
  89. camel/workforce/role_playing_worker.py +181 -0
  90. camel/workforce/{single_agent_node.py → single_agent_worker.py} +49 -23
  91. camel/workforce/task_channel.py +7 -8
  92. camel/workforce/utils.py +20 -50
  93. camel/workforce/{worker_node.py → worker.py} +15 -12
  94. camel/workforce/workforce.py +456 -19
  95. camel_ai-0.2.3.dist-info/LICENSE +201 -0
  96. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/METADATA +40 -65
  97. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/RECORD +98 -86
  98. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/WHEEL +1 -1
  99. camel/models/open_source_model.py +0 -170
  100. camel/workforce/manager_node.py +0 -299
  101. camel/workforce/role_playing_node.py +0 -168
  102. camel/workforce/workforce_prompt.py +0 -125
@@ -11,12 +11,10 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
- from typing import Optional, Union
14
+ from typing import TYPE_CHECKING, Optional, Union
15
15
 
16
- try:
16
+ if TYPE_CHECKING:
17
17
  from unstructured.documents.elements import Element
18
- except ImportError:
19
- Element = None
20
18
 
21
19
  from camel.agents import ChatAgent
22
20
  from camel.messages import BaseMessage
@@ -144,13 +142,13 @@ class KnowledgeGraphAgent(ChatAgent):
144
142
 
145
143
  def run(
146
144
  self,
147
- element: Union[str, Element],
145
+ element: "Element",
148
146
  parse_graph_elements: bool = False,
149
147
  ) -> Union[str, GraphElement]:
150
148
  r"""Run the agent to extract node and relationship information.
151
149
 
152
150
  Args:
153
- element (Union[str, Element]): The input element or string.
151
+ element (Element): The input element.
154
152
  parse_graph_elements (bool, optional): Whether to parse into
155
153
  `GraphElement`. Defaults to `False`.
156
154
 
camel/bots/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ from .discord_app import DiscordApp
15
+ from .slack.models import (
16
+ SlackAppMentionEventBody,
17
+ SlackAppMentionEventProfile,
18
+ SlackAuthProfile,
19
+ SlackEventBody,
20
+ SlackEventProfile,
21
+ )
22
+ from .slack.slack_app import SlackApp
23
+ from .telegram_bot import TelegramBot
24
+
25
+ __all__ = [
26
+ 'DiscordApp',
27
+ 'SlackApp',
28
+ 'SlackAppMentionEventBody',
29
+ 'SlackAppMentionEventProfile',
30
+ 'SlackAuthProfile',
31
+ 'SlackEventBody',
32
+ 'SlackEventProfile',
33
+ 'TelegramBot',
34
+ ]
@@ -0,0 +1,138 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ import logging
15
+ import os
16
+ from typing import TYPE_CHECKING, List, Optional
17
+
18
+ from camel.utils import dependencies_required
19
+
20
+ if TYPE_CHECKING:
21
+ from discord import Message
22
+
23
+ logging.basicConfig(level=logging.INFO)
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class DiscordApp:
28
+ r"""A class representing a Discord app that uses the `discord.py` library
29
+ to interact with Discord servers.
30
+
31
+ This bot can respond to messages in specific channels and only reacts to
32
+ messages that mention the bot.
33
+
34
+ Attributes:
35
+ channel_ids (Optional[List[int]]): A list of allowed channel IDs. If
36
+ provided, the bot will only respond to messages in these channels.
37
+ token (Optional[str]): The Discord bot token used for authentication.
38
+ """
39
+
40
+ @dependencies_required('discord')
41
+ def __init__(
42
+ self,
43
+ channel_ids: Optional[List[int]] = None,
44
+ token: Optional[str] = None,
45
+ ) -> None:
46
+ r"""Initialize the DiscordApp instance by setting up the Discord client
47
+ and event handlers.
48
+
49
+ Args:
50
+ channel_ids (Optional[List[int]]): A list of allowed channel IDs.
51
+ The bot will only respond to messages in these channels if
52
+ provided.
53
+ token (Optional[str]): The Discord bot token for authentication.
54
+ If not provided, the token will be retrieved from the
55
+ environment variable `DISCORD_TOKEN`.
56
+
57
+ Raises:
58
+ ValueError: If the `DISCORD_TOKEN` is not found in environment
59
+ variables.
60
+ """
61
+ self.token = token or os.getenv('DISCORD_TOKEN')
62
+ self.channel_ids = channel_ids
63
+
64
+ if not self.token:
65
+ raise ValueError(
66
+ "`DISCORD_TOKEN` not found in environment variables. Get it"
67
+ " here: `https://discord.com/developers/applications`."
68
+ )
69
+
70
+ import discord
71
+
72
+ intents = discord.Intents.default()
73
+ intents.message_content = True
74
+ self._client = discord.Client(intents=intents)
75
+
76
+ # Register event handlers
77
+ self._client.event(self.on_ready)
78
+ self._client.event(self.on_message)
79
+
80
+ async def start(self):
81
+ r"""Asynchronously start the Discord bot using its token.
82
+
83
+ This method starts the bot and logs into Discord asynchronously using
84
+ the provided token. It should be awaited when used in an async
85
+ environment.
86
+ """
87
+ await self._client.start(self.token)
88
+
89
+ def run(self) -> None:
90
+ r"""Start the Discord bot using its token.
91
+
92
+ This method starts the bot and logs into Discord synchronously using
93
+ the provided token. It blocks execution and keeps the bot running.
94
+ """
95
+ self._client.run(self.token) # type: ignore[arg-type]
96
+
97
+ async def on_ready(self) -> None:
98
+ r"""Event handler that is called when the bot has successfully
99
+ connected to the Discord server.
100
+
101
+ When the bot is ready and logged into Discord, it prints a message
102
+ displaying the bot's username.
103
+ """
104
+ logger.info(f'We have logged in as {self._client.user}')
105
+
106
+ async def on_message(self, message: 'Message') -> None:
107
+ r"""Event handler for processing incoming messages.
108
+
109
+ This method is called whenever a new message is received by the bot. It
110
+ will ignore messages sent by the bot itself, only respond to messages
111
+ in allowed channels (if specified), and only to messages that mention
112
+ the bot.
113
+
114
+ Args:
115
+ message (discord.Message): The message object received from
116
+ Discord.
117
+ """
118
+ # If the message author is the bot itself,
119
+ # do not respond to this message
120
+ if message.author == self._client.user:
121
+ return
122
+
123
+ # If allowed channel IDs are provided,
124
+ # only respond to messages in those channels
125
+ if self.channel_ids and message.channel.id not in self.channel_ids:
126
+ return
127
+
128
+ # Only respond to messages that mention the bot
129
+ if not self._client.user or not self._client.user.mentioned_in(
130
+ message
131
+ ):
132
+ return
133
+
134
+ logger.info(f"Received message: {message.content}")
135
+
136
+ @property
137
+ def client(self):
138
+ return self._client
@@ -0,0 +1,30 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ from .models import (
15
+ SlackAppMentionEventBody,
16
+ SlackAppMentionEventProfile,
17
+ SlackAuthProfile,
18
+ SlackEventBody,
19
+ SlackEventProfile,
20
+ )
21
+ from .slack_app import SlackApp
22
+
23
+ __all__ = [
24
+ 'SlackApp',
25
+ 'SlackAppMentionEventBody',
26
+ 'SlackAppMentionEventProfile',
27
+ 'SlackAuthProfile',
28
+ 'SlackEventBody',
29
+ 'SlackEventProfile',
30
+ ]
@@ -0,0 +1,158 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ from typing import Optional
15
+
16
+ from pydantic import BaseModel
17
+
18
+
19
+ class SlackAuthProfile(BaseModel):
20
+ r"""Represents the authorization profile within a Slack event.
21
+
22
+ Events will contain a single, compact authorizations field that shows one
23
+ installation of your app that the event is visible to.
24
+ In other words, lists of authorizations will be truncated to one element.
25
+
26
+ If there's more than one installing party that your app is keeping track
27
+ of, it's best not to rely on the single party listed in authorizations to
28
+ be any particular one.
29
+
30
+ To get a full list of who can see events, call the apps.event.
31
+ authorizations.list method after obtaining an app-level token. Read more on
32
+ the changes here; they have taken effect for existing apps as of
33
+ February 24, 2021.
34
+
35
+ References:
36
+
37
+ - https://api.slack.com/apis/events-api#authorizations
38
+ - https://api.slack.com/changelog/2020-09-15-events-api-truncate-authed-users#no_context
39
+ """
40
+
41
+ enterprise_id: Optional[str] = None
42
+ """The ID of the enterprise associated with the authorization."""
43
+
44
+ team_id: str
45
+ """The ID of the team associated with the authorization."""
46
+
47
+ user_id: str
48
+ """The ID of the user associated with the authorization."""
49
+
50
+ is_bot: bool
51
+ """Whether the authorized user is a bot."""
52
+
53
+ is_enterprise_install: bool
54
+ """Whether the authorization is for an enterprise installation."""
55
+
56
+
57
+ class SlackEventProfile(BaseModel):
58
+ r"""Represents the detailed profile of a Slack event, including user,
59
+ message, and context data.
60
+ """
61
+
62
+ user: str
63
+ """The ID of the user associated with the event."""
64
+
65
+ type: str
66
+ """The type of the event (e.g., 'message')."""
67
+
68
+ ts: str
69
+ """A timestamp representing when the event was triggered."""
70
+
71
+ thread_ts: Optional[str] = None
72
+ """The timestamp of the parent message in a thread."""
73
+
74
+ client_msg_id: str
75
+ """A unique ID generated by the client for the message (if available)."""
76
+
77
+ text: str
78
+ """The message content text."""
79
+
80
+ team: str
81
+ """The ID of the team that the event is associated with."""
82
+
83
+ blocks: list
84
+ """The list of message blocks, providing structured information."""
85
+
86
+ channel: str
87
+ """The ID of the Slack channel where the event happened."""
88
+
89
+ event_ts: str
90
+ """The event-specific timestamp when it occurred."""
91
+
92
+ channel_type: Optional[str]
93
+ """The type of Slack channel (e.g., 'channel', 'im')."""
94
+
95
+
96
+ class SlackEventBody(BaseModel):
97
+ r"""Represents the entire body of a Slack event, including the event
98
+ profile, authorization, and context.
99
+ """
100
+
101
+ token: str
102
+ """The token to verify the source of the event."""
103
+
104
+ team_id: str
105
+ """The ID of the team where the event is happening."""
106
+
107
+ context_team_id: Optional[str]
108
+ """The team ID for the shared channel context, if applicable."""
109
+
110
+ context_enterprise_id: Optional[str] = None
111
+ """The enterprise ID for the shared channel context, if applicable."""
112
+
113
+ api_app_id: str
114
+ """The unique identifier for the Slack app that received the event."""
115
+
116
+ event: SlackEventProfile
117
+ """A detailed profile of the event"""
118
+
119
+ type: str
120
+ """The overall type of event received (e.g., 'event_callback')."""
121
+
122
+ event_id: str
123
+ """A unique identifier assigned to this event by Slack."""
124
+
125
+ event_time: int
126
+ """The timestamp (in seconds) representing when the event was triggered."""
127
+
128
+ authorizations: Optional[list[SlackAuthProfile]] = None
129
+ """An optional list of authorizations that describe which installation can
130
+ see the event."""
131
+
132
+ is_ext_shared_channel: bool
133
+ """Indicates if the event is part of a shared channel between different
134
+ organizations."""
135
+
136
+ event_context: str
137
+ """A unique string representing the context of the event."""
138
+
139
+
140
+ class SlackAppMentionEventProfile(SlackEventProfile):
141
+ r"""Represents the detailed profile of a Slack event where the app was
142
+ mentioned in a message.
143
+ """
144
+
145
+ channel_type: Optional[str] = None
146
+ """The type of Slack channel. it's None for app mentions."""
147
+
148
+
149
+ class SlackAppMentionEventBody(SlackEventBody):
150
+ r"""Represents the entire body of a Slack event where the app was mentioned
151
+ in a message.
152
+ """
153
+
154
+ context_team_id: Optional[str] = None
155
+ """A detailed profile of the event. it's None for app mentions."""
156
+
157
+ event: SlackAppMentionEventProfile
158
+ """A detailed profile of the event"""
@@ -0,0 +1,255 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ import logging
15
+ import os
16
+ from typing import TYPE_CHECKING, Any, Dict, Optional
17
+
18
+ from slack_sdk.oauth.installation_store.async_installation_store import (
19
+ AsyncInstallationStore,
20
+ )
21
+ from starlette import requests, responses
22
+
23
+ from camel.bots.slack.models import (
24
+ SlackAppMentionEventBody,
25
+ SlackAppMentionEventProfile,
26
+ SlackEventBody,
27
+ SlackEventProfile,
28
+ )
29
+ from camel.utils import dependencies_required
30
+
31
+ if TYPE_CHECKING:
32
+ from slack_bolt.context.async_context import AsyncBoltContext
33
+ from slack_bolt.context.say.async_say import AsyncSay
34
+ from slack_sdk.web.async_client import AsyncWebClient
35
+
36
+ logging.basicConfig(level=logging.INFO)
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class SlackApp:
41
+ r"""Represents a Slack app that is powered by a Slack Bolt `AsyncApp`.
42
+
43
+ This class is responsible for initializing and managing the Slack
44
+ application by setting up event handlers, running the app server, and
45
+ handling events such as messages and mentions from Slack.
46
+
47
+ Args:
48
+ token (Optional[str]): Slack API token for authentication.
49
+ scopes (Optional[str]): Slack app scopes for permissions.
50
+ signing_secret (Optional[str]): Signing secret for verifying Slack
51
+ requests.
52
+ client_id (Optional[str]): Slack app client ID.
53
+ client_secret (Optional[str]): Slack app client secret.
54
+ redirect_uri_path (str): The URI path for OAuth redirect, defaults to
55
+ "/slack/oauth_redirect".
56
+ installation_store (Optional[AsyncInstallationStore]): The installation
57
+ store for handling OAuth installations.
58
+ """
59
+
60
+ @dependencies_required('slack_bolt')
61
+ def __init__(
62
+ self,
63
+ token: Optional[str] = None,
64
+ scopes: Optional[str] = None,
65
+ signing_secret: Optional[str] = None,
66
+ client_id: Optional[str] = None,
67
+ client_secret: Optional[str] = None,
68
+ redirect_uri_path: str = "/slack/oauth_redirect",
69
+ installation_store: Optional[AsyncInstallationStore] = None,
70
+ ) -> None:
71
+ r"""Initializes the SlackApp instance by setting up the Slack Bolt app
72
+ and configuring event handlers and OAuth settings.
73
+
74
+ Args:
75
+ token (Optional[str]): The Slack API token.
76
+ scopes (Optional[str]): The scopes for Slack app permissions.
77
+ signing_secret (Optional[str]): The signing secret for verifying
78
+ requests.
79
+ client_id (Optional[str]): The Slack app client ID.
80
+ client_secret (Optional[str]): The Slack app client secret.
81
+ redirect_uri_path (str): The URI path for handling OAuth redirects
82
+ (default is "/slack/oauth_redirect").
83
+ installation_store (Optional[AsyncInstallationStore]): An optional
84
+ installation store for OAuth installations.
85
+ """
86
+ from slack_bolt.adapter.starlette.async_handler import (
87
+ AsyncSlackRequestHandler,
88
+ )
89
+ from slack_bolt.app.async_app import AsyncApp
90
+ from slack_bolt.oauth.async_oauth_settings import AsyncOAuthSettings
91
+
92
+ self.token: Optional[str] = token or os.getenv("SLACK_TOKEN")
93
+ self.scopes: Optional[str] = scopes or os.getenv("SLACK_SCOPES")
94
+ self.signing_secret: Optional[str] = signing_secret or os.getenv(
95
+ "SLACK_SIGNING_SECRET"
96
+ )
97
+ self.client_id: Optional[str] = client_id or os.getenv(
98
+ "SLACK_CLIENT_ID"
99
+ )
100
+ self.client_secret: Optional[str] = client_secret or os.getenv(
101
+ "SLACK_CLIENT_SECRET"
102
+ )
103
+
104
+ if not all([self.token, self.scopes, self.signing_secret]):
105
+ raise ValueError(
106
+ "`SLACK_TOKEN`, `SLACK_SCOPES`, and `SLACK_SIGNING_SECRET` "
107
+ "environment variables must be set. Get it here: "
108
+ "`https://api.slack.com/apps`."
109
+ )
110
+
111
+ # Setup OAuth settings if client ID and secret are provided
112
+ if self.client_id and self.client_secret:
113
+ self._app = AsyncApp(
114
+ oauth_settings=AsyncOAuthSettings(
115
+ client_id=self.client_id,
116
+ client_secret=self.client_secret,
117
+ scopes=self.scopes,
118
+ redirect_uri_path=redirect_uri_path,
119
+ ),
120
+ logger=logger,
121
+ signing_secret=self.signing_secret,
122
+ installation_store=installation_store,
123
+ token=self.token,
124
+ )
125
+ else:
126
+ # Initialize Slack Bolt AsyncApp with settings
127
+ self._app = AsyncApp(
128
+ logger=logger,
129
+ signing_secret=self.signing_secret,
130
+ installation_store=installation_store,
131
+ token=self.token,
132
+ )
133
+
134
+ self._handler = AsyncSlackRequestHandler(self._app)
135
+ self.setup_handlers()
136
+
137
+ def setup_handlers(self) -> None:
138
+ r"""Sets up the event handlers for Slack events, such as `app_mention`
139
+ and `message`.
140
+
141
+ This method registers the `app_mention` and `on_message` event handlers
142
+ with the Slack Bolt app to respond to Slack events.
143
+ """
144
+ self._app.event("app_mention")(self.app_mention)
145
+ self._app.event("message")(self.on_message)
146
+
147
+ def run(
148
+ self,
149
+ port: int = 3000,
150
+ path: str = "/slack/events",
151
+ host: Optional[str] = None,
152
+ ) -> None:
153
+ r"""Starts the Slack Bolt app server to listen for incoming Slack
154
+ events.
155
+
156
+ Args:
157
+ port (int): The port on which the server should run (default is
158
+ 3000).
159
+ path (str): The endpoint path for receiving Slack events (default
160
+ is "/slack/events").
161
+ host (Optional[str]): The hostname to bind the server (default is
162
+ None).
163
+ """
164
+ self._app.start(port=port, path=path, host=host)
165
+
166
+ async def handle_request(
167
+ self, request: requests.Request
168
+ ) -> responses.Response:
169
+ r"""Handles incoming requests from Slack through the request handler.
170
+
171
+ Args:
172
+ request (Request): A Starlette request object representing the
173
+ incoming request.
174
+
175
+ Returns:
176
+ The response generated by the Slack Bolt handler.
177
+ """
178
+ return await self._handler.handle(request)
179
+
180
+ async def app_mention(
181
+ self,
182
+ context: "AsyncBoltContext",
183
+ client: "AsyncWebClient",
184
+ event: Dict[str, Any],
185
+ body: Dict[str, Any],
186
+ say: "AsyncSay",
187
+ ) -> None:
188
+ r"""Event handler for `app_mention` events.
189
+
190
+ This method is triggered when someone mentions the app in Slack.
191
+
192
+ Args:
193
+ context (AsyncBoltContext): The Slack Bolt context for the event.
194
+ client (AsyncWebClient): The Slack Web API client.
195
+ event (Dict[str, Any]): The event data for the app mention.
196
+ body (Dict[str, Any]): The full request body from Slack.
197
+ say (AsyncSay): A function to send a response back to the channel.
198
+ """
199
+ event_profile = SlackAppMentionEventProfile(**event)
200
+ event_body = SlackAppMentionEventBody(**body)
201
+
202
+ logger.info(f"app_mention, context: {context}")
203
+ logger.info(f"app_mention, client: {client}")
204
+ logger.info(f"app_mention, event_profile: {event_profile}")
205
+ logger.info(f"app_mention, event_body: {event_body}")
206
+ logger.info(f"app_mention, say: {say}")
207
+
208
+ async def on_message(
209
+ self,
210
+ context: "AsyncBoltContext",
211
+ client: "AsyncWebClient",
212
+ event: Dict[str, Any],
213
+ body: Dict[str, Any],
214
+ say: "AsyncSay",
215
+ ) -> None:
216
+ r"""Event handler for `message` events.
217
+
218
+ This method is triggered when the app receives a message in Slack.
219
+
220
+ Args:
221
+ context (AsyncBoltContext): The Slack Bolt context for the event.
222
+ client (AsyncWebClient): The Slack Web API client.
223
+ event (Dict[str, Any]): The event data for the message.
224
+ body (Dict[str, Any]): The full request body from Slack.
225
+ say (AsyncSay): A function to send a response back to the channel.
226
+ """
227
+ await context.ack()
228
+
229
+ event_profile = SlackEventProfile(**event)
230
+ event_body = SlackEventBody(**body)
231
+
232
+ logger.info(f"on_message, context: {context}")
233
+ logger.info(f"on_message, client: {client}")
234
+ logger.info(f"on_message, event_profile: {event_profile}")
235
+ logger.info(f"on_message, event_body: {event_body}")
236
+ logger.info(f"on_message, say: {say}")
237
+
238
+ logger.info(f"Received message: {event_profile.text}")
239
+
240
+ def mention_me(
241
+ self, context: "AsyncBoltContext", body: SlackEventBody
242
+ ) -> bool:
243
+ r"""Check if the bot is mentioned in the message.
244
+
245
+ Args:
246
+ context (AsyncBoltContext): The Slack Bolt context for the event.
247
+ body (SlackEventBody): The body of the Slack event.
248
+
249
+ Returns:
250
+ bool: True if the bot is mentioned in the message, False otherwise.
251
+ """
252
+ message = body.event.text
253
+ bot_user_id = context.bot_user_id
254
+ mention = f"<@{bot_user_id}>"
255
+ return mention in message