|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import logging |
|
import os |
|
from typing import TYPE_CHECKING, List, Optional |
|
|
|
from camel.utils import dependencies_required |
|
|
|
if TYPE_CHECKING: |
|
from discord import Message |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class DiscordApp: |
|
r"""A class representing a Discord app that uses the `discord.py` library |
|
to interact with Discord servers. |
|
|
|
This bot can respond to messages in specific channels and only reacts to |
|
messages that mention the bot. |
|
|
|
Attributes: |
|
channel_ids (Optional[List[int]]): A list of allowed channel IDs. If |
|
provided, the bot will only respond to messages in these channels. |
|
token (Optional[str]): The Discord bot token used for authentication. |
|
""" |
|
|
|
@dependencies_required('discord') |
|
def __init__( |
|
self, |
|
channel_ids: Optional[List[int]] = None, |
|
token: Optional[str] = None, |
|
) -> None: |
|
r"""Initialize the DiscordApp instance by setting up the Discord client |
|
and event handlers. |
|
|
|
Args: |
|
channel_ids (Optional[List[int]]): A list of allowed channel IDs. |
|
The bot will only respond to messages in these channels if |
|
provided. |
|
token (Optional[str]): The Discord bot token for authentication. |
|
If not provided, the token will be retrieved from the |
|
environment variable `DISCORD_TOKEN`. |
|
|
|
Raises: |
|
ValueError: If the `DISCORD_TOKEN` is not found in environment |
|
variables. |
|
""" |
|
self.token = token or os.getenv('DISCORD_TOKEN') |
|
self.channel_ids = channel_ids |
|
|
|
if not self.token: |
|
raise ValueError( |
|
"`DISCORD_TOKEN` not found in environment variables. Get it" |
|
" here: `https://discord.com/developers/applications`." |
|
) |
|
|
|
import discord |
|
|
|
intents = discord.Intents.default() |
|
intents.message_content = True |
|
self._client = discord.Client(intents=intents) |
|
|
|
|
|
self._client.event(self.on_ready) |
|
self._client.event(self.on_message) |
|
|
|
async def start(self): |
|
r"""Asynchronously start the Discord bot using its token. |
|
|
|
This method starts the bot and logs into Discord asynchronously using |
|
the provided token. It should be awaited when used in an async |
|
environment. |
|
""" |
|
await self._client.start(self.token) |
|
|
|
def run(self) -> None: |
|
r"""Start the Discord bot using its token. |
|
|
|
This method starts the bot and logs into Discord synchronously using |
|
the provided token. It blocks execution and keeps the bot running. |
|
""" |
|
self._client.run(self.token) |
|
|
|
async def on_ready(self) -> None: |
|
r"""Event handler that is called when the bot has successfully |
|
connected to the Discord server. |
|
|
|
When the bot is ready and logged into Discord, it prints a message |
|
displaying the bot's username. |
|
""" |
|
logger.info(f'We have logged in as {self._client.user}') |
|
|
|
async def on_message(self, message: 'Message') -> None: |
|
r"""Event handler for processing incoming messages. |
|
|
|
This method is called whenever a new message is received by the bot. It |
|
will ignore messages sent by the bot itself, only respond to messages |
|
in allowed channels (if specified), and only to messages that mention |
|
the bot. |
|
|
|
Args: |
|
message (discord.Message): The message object received from |
|
Discord. |
|
""" |
|
|
|
|
|
if message.author == self._client.user: |
|
return |
|
|
|
|
|
|
|
if self.channel_ids and message.channel.id not in self.channel_ids: |
|
return |
|
|
|
|
|
if not self._client.user or not self._client.user.mentioned_in( |
|
message |
|
): |
|
return |
|
|
|
logger.info(f"Received message: {message.content}") |
|
|
|
@property |
|
def client(self): |
|
return self._client |
|
|