Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Discord OAuth Flow #1129

Merged
merged 39 commits into from
Jan 10, 2025
Merged

feat: Add Discord OAuth Flow #1129

merged 39 commits into from
Jan 10, 2025

Conversation

AveryYay
Copy link
Collaborator

@AveryYay AveryYay commented Oct 29, 2024

Description

This PR introduces discord OAuth flow: Implements a Discord OAuth flow which allows users to authenticate with Discord, managing the entire OAuth process from generating the authorization URL, handling the authorization code exchange, to retrieving basic user information and an installation store to manage multi server installation.

Motivation and Context

Enables secure user authentication via Discord OAuth, enhancing the bot’s integration with Discord’s authentication system. And supports multi server management.

  • I have raised an issue to propose this change (required for new features and bug fixes)

Types of changes

What types of changes does your code introduce? Put an x in all the boxes that apply:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds core functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation (update in the documentation)
  • Example (update in the folder of example)

Implemented Tasks

  • Implemented OAuth flow for user authentication
  • Created a custom Discord bot with thread creation and history response capabilities
  • Added queuing for efficient message processing in the bot

Checklist

Go over all the following points, and put an x in all the boxes that apply.
If you are unsure about any of these, don't hesitate to ask. We are here to help!

  • I have read the CONTRIBUTION guide. (required)
  • My change requires a change to the documentation.
  • I have updated the tests accordingly. (required for a bug fix or a new feature)
  • I have updated the documentation accordingly.

…threads and responding with the chat history features
@koch3092 koch3092 self-requested a review October 29, 2024 07:23
@koch3092
Copy link
Collaborator

Thanks @AveryYay , I will review the code and assist you in completing the PR integration

Copy link
Collaborator

@koch3092 koch3092 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AveryYay , good code! left some comments below.

And in terms of functionality, I hope you refer to camel.bots.slack.slack_app.py and integrate the oauth process into camel.bots.discord.discord_app.py (you can create camel.bots.discord to store module content)

examples/bots/discord_OAuth_flow.py Outdated Show resolved Hide resolved
examples/bots/discord_OAuth_flow.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@koch3092 koch3092 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @AveryYay , left some comments below.


user_data = await self.oauth_client.get_user_info(access_token)
logger.info(f"User data: {user_data}")
await self.shutdown_oauth_server()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await self.shutdown_oauth_server()

Server is a long-term service, so it's better to let users manually shut it down

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example can be combined with the usage of chat_agent to integrate existing historical records into the memory of chat_agent.

@Wendong-Fan Wendong-Fan changed the title Add Discord OAuth Flow and Bot Public Thread with History Response Feature feat: Add Discord OAuth Flow and Bot Public Thread with History Response Feature Nov 6, 2024
Copy link
Member

@Wendong-Fan Wendong-Fan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AveryYay ! Overall LGTM, left some comments below, It would be better if we could also add docstring for DiscordOAuth
BTW, you can run pre-commit run --all-files before the push to ensure the code pass pre-commit check

token: Optional[str] = None,
client_id: Optional[str] = None,
client_secret: Optional[str] = None,
redirect_uri: str = "http://localhost:8000/callback",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use REDIRECT_URI instead?

Comment on lines 58 to 59
response = await client.post(TOKEN_URL, data=data, headers=headers)
response_data = response.json()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before fetch the data, check the status code first like below?

if response.status_code != 200:
    logger.error("Failed to exchange code for token)
    return None

Comment on lines 65 to 66
user_response = await client.get(USER_URL, headers=headers)
return user_response.json()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Comment on lines 275 to 276
self.server.should_exit = True
logger.info("Shutting down the OAuth server.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add await here to make sure the server is fully stopped?

Copy link
Member

@Wendong-Fan Wendong-Fan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AveryYay , left some comments below

@@ -53,6 +62,9 @@ def __init__(
token (Optional[str]): The Discord bot token for authentication.
If not provided, the token will be retrieved from the
environment variable `DISCORD_TOKEN`.
client_id (Optional[str]): The client ID for Discord OAuth.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as other variables

Suggested change
client_id (Optional[str]): The client ID for Discord OAuth.
client_id (Optional[str]): The client ID for Discord OAuth. (default: :obj:`None`)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
client_id (Optional[str]): The client ID for Discord OAuth.
client_id (str, optional): The client ID for Discord OAuth. (default: :obj:`None`)

Comment on lines 95 to 99
if not all([client_id, client_secret]):
raise ValueError(
"DISCORD_CLIENT_ID and DISCORD_CLIENT_SECRET must be set "
"for OAuth."
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from camel.utils import api_keys_required use @api_keys_required

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't actually api keys, do I still use @api_keys_required?

Comment on lines 127 to 131
r"""Generate an OAuth URL to invite the bot to a Discord server.

This method returns a link that allows users to invite the bot to
their Discord server with specific permissions.
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

including args and returns in docstring

- Added DiscordAsyncInstallationStore to manage installations (using list right now, need to switch to database or file instead)
- Added DiscordInstallation entity class
- Implemented relevant functions in DiscordApp
@AveryYay AveryYay marked this pull request as draft November 28, 2024 21:18
…rdInstallation abd DiscordAsyncInstallationStore to __init__.py
@AveryYay AveryYay marked this pull request as ready for review November 29, 2024 08:08
@Wendong-Fan Wendong-Fan marked this pull request as ready for review December 29, 2024 16:05
@liuxukun2000
Copy link
Collaborator

Hi @Wendong-Fan and @AveryYay, the current implementation of DiscordBaseInstallationStore looks great! For future scalability, though, I’d like to suggest considering the use of an ORM as an alternative. For example, you can check out Tortoise ORM. It simplifies the code and provides the flexibility to support nearly any type of database without requiring code modifications.

What are your thoughts on this approach? 😊

@Asher-hss
Copy link
Collaborator

你好@Wendong-Fan@AveryYay,的当前实现DiscordBaseInstallationStore看起来很棒!不过,为了将来的可扩展性,我建议考虑使用 ORM 作为替代方案。例如,您可以查看Tortoise ORM。它简化了代码,并提供了灵活性,无需修改代码即可支持几乎任何类型的数据库。

你对这种方法有什么看法?😊

@koch3092

Copy link
Collaborator

@koch3092 koch3092 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AveryYay , left some comments below

" here: `https://discord.com/developers/applications`."
)

intents = discord.Intents.default()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add an intents parameter so that users can set their own bot permissions

Comment on lines 169 to 174
r"""Retrieve user information using the access token.
Args:
access_token (str): The access token received from Discord.
Returns:
dict: The user information retrieved from Discord.
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
r"""Retrieve user information using the access token.
Args:
access_token (str): The access token received from Discord.
Returns:
dict: The user information retrieved from Discord.
"""
r"""Retrieve user information using the access token.
Args:
access_token (str): The access token received from Discord.
Returns:
dict: The user information retrieved from Discord.
"""

Comment on lines 288 to 290

Returns:
None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Returns:
None

Comment on lines 316 to 318

Returns:
None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Returns:
None

@koch3092
Copy link
Collaborator

koch3092 commented Jan 3, 2025

Hi @Wendong-Fan and @AveryYay, the current implementation of DiscordBaseInstallationStore looks great! For future scalability, though, I’d like to suggest considering the use of an ORM as an alternative. For example, you can check out Tortoise ORM. It simplifies the code and provides the flexibility to support nearly any type of database without requiring code modifications.

What are your thoughts on this approach? 😊

We are currently using SQLite as a database for lightweight storage. If ORM is introduced, it will make the entire functionality too cumbersome. If we specifically implement certain businesses, we can use ORM, but just doing OAuth and SQL would be more efficient. I have discussed this issue with @AveryYay . Avery's current implementation is based on the Slack Bot solution, which also provides the SQLite solution.

# Conflicts:
#	poetry.lock
Copy link
Member

@Wendong-Fan Wendong-Fan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AveryYay , left some comments below

Comment on lines 14 to 19
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please check and clean the license info

Comment on lines 14 to 19
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
# Licensed under the Apache License, Version 2.0 (the “License”);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean

Comment on lines 14 to 26
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
# Licensed under the Apache License, Version 2.0 (the “License”);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an “AS IS” BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file need to be deleted

Comment on lines 31 to 44
class DiscordInstallation:
def __init__(
self,
guild_id: str,
access_token: str,
refresh_token: str,
installed_at: datetime,
token_expires_at: Optional[datetime] = None,
):
self.guild_id = guild_id
self.access_token = access_token
self.refresh_token = refresh_token
self.installed_at = installed_at
self.token_expires_at = token_expires_at
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add docstring

Comment on lines 31 to 32
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use our own logger

from camel.logger import get_logger

logger = get_logger(__name__)

@koch3092 koch3092 self-requested a review January 8, 2025 07:09
Copy link
Collaborator

@koch3092 koch3092 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AveryYay LGTM

Copy link
Member

@Wendong-Fan Wendong-Fan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please resolve the conflict with master branch

# Conflicts:
#	poetry.lock
#	pyproject.toml
Copy link
Collaborator

@liuxukun2000 liuxukun2000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but there are a few small points that could be improved.

return None
headers = {"Authorization": f"Bearer {access_token}"}
async with httpx.AsyncClient() as client:
user_response = await client.get(USER_URL, headers=headers)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling should be implemented here.

"client_secret, or redirect_uri."
)
return None
assert self.installation_store is not None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to raise an error here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a check before this that already checks self.installation_store is not None, this assertion here is just for it to pass pre commit.

"client_secret, or redirect_uri."
)
return None
assert self.installation_store is not None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to raise an error here.

"client_secret, or redirect_uri."
)
return None
assert self.installation_store is not None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to raise an error here.

@AveryYay AveryYay merged commit a4820b7 into camel-ai:master Jan 10, 2025
1 of 6 checks passed
@Wendong-Fan
Copy link
Member

hey @AveryYay @liuxukun2000 @koch3092 , I think there's some error in cicd was not fixed but this PR was approved and merged. current error message in master branch:

=========================== short test summary info ============================
FAILED examples/test/bots/test_discord_app.py::TestDiscordApp::test_init_with_token_from_env - ValueError: `DISCORD_TOKEN` not defined. Get it here: `[https://discord.com/developers/applications`.](https://discord.com/developers/applications%60.)
FAILED examples/test/bots/test_discord_app.py::TestDiscordApp::test_run_bot - ValueError: `DISCORD_TOKEN` not defined. Get it here: `[https://discord.com/developers/applications`.](https://discord.com/developers/applications%60.)
============ 2 failed, 39 passed, 12 warnings in 166.87s (0:02:46) =============

I will do a hot fix, please pay more attention to the cicd check next time~

@AveryYay
Copy link
Collaborator Author

hey @AveryYay @liuxukun2000 @koch3092 , I think there's some error in cicd was not fixed but this PR was approved and merged. current error message in master branch:


=========================== short test summary info ============================

FAILED examples/test/bots/test_discord_app.py::TestDiscordApp::test_init_with_token_from_env - ValueError: `DISCORD_TOKEN` not defined. Get it here: `[https://discord.com/developers/applications`.](https://discord.com/developers/applications%60.)

FAILED examples/test/bots/test_discord_app.py::TestDiscordApp::test_run_bot - ValueError: `DISCORD_TOKEN` not defined. Get it here: `[https://discord.com/developers/applications`.](https://discord.com/developers/applications%60.)

============ 2 failed, 39 passed, 12 warnings in 166.87s (0:02:46) =============

I will do a hot fix, please pay more attention to the cicd check next time~

Sorry about this and thank you for the fix! I thought it was one of the certification issue I encountered since I forked the repo😭

@Wendong-Fan
Copy link
Member

hey @AveryYay @liuxukun2000 @koch3092 , I think there's some error in cicd was not fixed but this PR was approved and merged. current error message in master branch:


=========================== short test summary info ============================

FAILED examples/test/bots/test_discord_app.py::TestDiscordApp::test_init_with_token_from_env - ValueError: `DISCORD_TOKEN` not defined. Get it here: `[https://discord.com/developers/applications`.](https://discord.com/developers/applications%60.)

FAILED examples/test/bots/test_discord_app.py::TestDiscordApp::test_run_bot - ValueError: `DISCORD_TOKEN` not defined. Get it here: `[https://discord.com/developers/applications`.](https://discord.com/developers/applications%60.)

============ 2 failed, 39 passed, 12 warnings in 166.87s (0:02:46) =============

I will do a hot fix, please pay more attention to the cicd check next time~

Sorry about this and thank you for the fix! I thought it was one of the certification issue I encountered since I forked the repo😭

No worries~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants