-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Twitter tool #525
Merged
Merged
Twitter tool #525
Changes from 23 commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
77b266a
Added oauth button for twitter
ececcc0
Added api
d7b7150
Get tokens
f8cb05e
Oauth Flow
1d56496
Helper file for twitter tokens
a91a77a
Toolkit for twitter
c789df4
Tool to send tweets with multimediaenabled
b05d440
Minor changes
d587131
Added unit tests
541236a
Minor fix
bc9c7f7
Minor fix
8a6cc91
Error fixes in unit tests
6e6252b
Create testing.txt
Tarraann 8cee52c
Update test_send_tweets.py
Tarraann b02e7b5
Create testing.txt
Tarraann 55c975a
error fixing
Tarraann 01effb2
Update test_send_tweets.py
Tarraann 6b633ca
pr comments resolved
3a7bc1c
bug fixed
e6e2e7d
Modification in unit tests
d44ec07
Modification in unit tests
f372cc5
Changes in session for db
71116b1
Saving Oauth Tokens in Database instead of Local file
e50383e
Adding of organisation id
ccb1bf1
Db changes made
32eaf20
minor changes made
26a9e12
Changes in tests
bec1921
merge dev
Fluder-Paradyne 30ca409
Update twitter_toolkit.py
c7f951b
Fixed unit tests
bddc843
Added Controller for twitter apis
9a331b3
Added unit tests for twitter tokens:
b3202e3
Merge branch 'dev' into twitter-tool
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,11 @@ | |
from sqlalchemy.orm import sessionmaker | ||
|
||
import superagi | ||
import urllib.parse | ||
import json | ||
import http.client as http_client | ||
from superagi.helper.twitter_tokens import TwitterTokens | ||
from datetime import datetime, timedelta | ||
from superagi.agent.agent_prompt_builder import AgentPromptBuilder | ||
from superagi.config.config import get_config | ||
from superagi.controllers.agent import router as agent_router | ||
|
@@ -40,7 +45,7 @@ | |
from superagi.models.agent_workflow_step import AgentWorkflowStep | ||
from superagi.models.organisation import Organisation | ||
from superagi.models.tool_config import ToolConfig | ||
from superagi.models.toolkit import Toolkit | ||
from superagi.models.oauth_tokens import OauthTokens | ||
from superagi.models.types.login_request import LoginRequest | ||
from superagi.models.user import User | ||
|
||
|
@@ -320,6 +325,16 @@ async def google_auth_calendar(code: str = Query(...), Authorize: AuthJWT = Depe | |
frontend_url = superagi.config.config.get_config("FRONTEND_URL", "http://localhost:3000") | ||
return RedirectResponse(frontend_url) | ||
|
||
@app.get('/oauth-twitter') | ||
async def twitter_oauth(oauth_token: str = Query(...),oauth_verifier: str = Query(...), Authorize: AuthJWT = Depends()): | ||
token_uri = f'https://api.twitter.com/oauth/access_token?oauth_verifier={oauth_verifier}&oauth_token={oauth_token}' | ||
conn = http_client.HTTPSConnection("api.twitter.com") | ||
conn.request("POST", token_uri, "") | ||
res = conn.getresponse() | ||
response_data = res.read().decode('utf-8') | ||
frontend_url = superagi.config.config.get_config("FRONTEND_URL", "http://localhost:3000") | ||
redirect_url_success = f"{frontend_url}/twitter_creds/?{response_data}" | ||
return RedirectResponse(url=redirect_url_success) | ||
|
||
@app.get('/github-login') | ||
def github_login(): | ||
|
@@ -411,6 +426,44 @@ def get_google_calendar_tool_configs(toolkit_id: int): | |
"client_id": google_calendar_config.value | ||
} | ||
|
||
@app.get("/twitter/get_twitter_creds/toolkit_id/{toolkit_id}") | ||
def get_twitter_tool_configs(toolkit_id: int): | ||
twitter_config_key = db.session.query(ToolConfig).filter(ToolConfig.toolkit_id == toolkit_id,ToolConfig.key == "TWITTER_API_KEY").first() | ||
twitter_config_secret = db.session.query(ToolConfig).filter(ToolConfig.toolkit_id == toolkit_id,ToolConfig.key == "TWITTER_API_SECRET").first() | ||
api_data = { | ||
"api_key": twitter_config_key.value, | ||
"api_secret": twitter_config_secret.value | ||
} | ||
response = TwitterTokens().get_request_token(api_data) | ||
return response | ||
|
||
@app.post("/twitter/send_twitter_creds/{twitter_creds}") | ||
def send_twitter_tool_configs(twitter_creds: str, Authorize: AuthJWT = Depends()): | ||
Session = sessionmaker(bind=engine) | ||
session = Session() | ||
try: | ||
Authorize.jwt_required() | ||
current_user_email = Authorize.get_jwt_subject() | ||
current_user = session.query(User).filter(User.email == current_user_email).first() | ||
except: | ||
current_user = session.query(User).filter(User.email == "[email protected]").first() | ||
user_id = current_user.id | ||
credentials = json.loads(twitter_creds) | ||
credentials["user_id"] = user_id | ||
api_key = session.query(ToolConfig).filter(ToolConfig.key == "TWITTER_API_KEY", ToolConfig.toolkit_id == credentials["toolkit_id"]).first() | ||
api_key_secret = session.query(ToolConfig).filter(ToolConfig.key == "TWITTER_API_SECRET", ToolConfig.toolkit_id == credentials["toolkit_id"]).first() | ||
final_creds = { | ||
"api_key": api_key.value, | ||
"api_key_secret": api_key_secret.value, | ||
"oauth_token": credentials["oauth_token"], | ||
"oauth_token_secret": credentials["oauth_token_secret"] | ||
} | ||
tokens = OauthTokens.add_or_update(session,credentials["toolkit_id"], current_user.id, "TWITTER_OAUTH_TOKENS", str(final_creds)) | ||
if tokens: | ||
success = True | ||
else: | ||
success = False | ||
return success | ||
|
||
@app.get("/validate-open-ai-key/{open_ai_key}") | ||
async def root(open_ai_key: str, Authorize: AuthJWT = Depends()): | ||
|
47 changes: 47 additions & 0 deletions
47
migrations/versions/cc1dcc508611_table_for_oauth_tokens.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
"""Table for oauth tokens | ||
|
||
Revision ID: cc1dcc508611 | ||
Revises: 7a3e336c0fba | ||
Create Date: 2023-06-29 07:57:22.861763 | ||
|
||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = 'cc1dcc508611' | ||
down_revision = '7a3e336c0fba' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
TransformerOptimus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
op.create_table('oauth_tokens', | ||
sa.Column('created_at', sa.DateTime(), nullable=True), | ||
sa.Column('updated_at', sa.DateTime(), nullable=True), | ||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), | ||
sa.Column('user_id', sa.Integer(), nullable=True), | ||
sa.Column('toolkit_id', sa.Integer(), nullable=True), | ||
sa.Column('key', sa.String(), nullable=True), | ||
sa.Column('value', sa.Text(), nullable=True), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.drop_index('ix_agent_execution_permissions_agent_execution_id', table_name='agent_execution_permissions') | ||
op.drop_index('ix_atc_agnt_template_id_key', table_name='agent_template_configs') | ||
op.drop_index('ix_agt_agnt_name', table_name='agent_templates') | ||
op.drop_index('ix_agt_agnt_organisation_id', table_name='agent_templates') | ||
op.drop_index('ix_agt_agnt_workflow_id', table_name='agent_templates') | ||
# ### end Alembic commands ### | ||
|
||
|
||
def downgrade() -> None: | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
op.create_index('ix_agt_agnt_workflow_id', 'agent_templates', ['agent_workflow_id'], unique=False) | ||
op.create_index('ix_agt_agnt_organisation_id', 'agent_templates', ['organisation_id'], unique=False) | ||
op.create_index('ix_agt_agnt_name', 'agent_templates', ['name'], unique=False) | ||
op.create_index('ix_atc_agnt_template_id_key', 'agent_template_configs', ['agent_template_id', 'key'], unique=False) | ||
op.create_index('ix_agent_execution_permissions_agent_execution_id', 'agent_execution_permissions', ['agent_execution_id'], unique=False) | ||
op.drop_table('oauth_tokens') | ||
# ### end Alembic commands ### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import os | ||
import json | ||
import base64 | ||
import requests | ||
from requests_oauthlib import OAuth1 | ||
from requests_oauthlib import OAuth1Session | ||
from superagi.helper.resource_helper import ResourceHelper | ||
|
||
class TwitterHelper: | ||
|
||
def get_media_ids(self, media_files, creds, agent_id): | ||
media_ids = [] | ||
oauth = OAuth1(creds.api_key, | ||
client_secret=creds.api_key_secret, | ||
resource_owner_key=creds.oauth_token, | ||
resource_owner_secret=creds.oauth_token_secret) | ||
for file in media_files: | ||
file_path = self.get_file_path(file, agent_id) | ||
image_data = open(file_path, 'rb').read() | ||
b64_image = base64.b64encode(image_data) | ||
upload_endpoint = 'https://upload.twitter.com/1.1/media/upload.json' | ||
headers = {'Authorization': 'application/octet-stream'} | ||
response = requests.post(upload_endpoint, headers=headers, | ||
data={'media_data': b64_image}, | ||
auth=oauth) | ||
ids = json.loads(response.text)['media_id'] | ||
media_ids.append(str(ids)) | ||
return media_ids | ||
|
||
def get_file_path(self, file_name, agent_id): | ||
final_path = ResourceHelper().get_agent_resource_path(file_name, agent_id) | ||
TransformerOptimus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return final_path | ||
|
||
def send_tweets(self, params, creds): | ||
tweet_endpoint = "https://api.twitter.com/2/tweets" | ||
oauth = OAuth1Session(creds.api_key, | ||
client_secret=creds.api_key_secret, | ||
resource_owner_key=creds.oauth_token, | ||
resource_owner_secret=creds.oauth_token_secret) | ||
|
||
response = oauth.post(tweet_endpoint,json=params) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import os | ||
import pickle | ||
import json | ||
import hmac | ||
import time | ||
import random | ||
import base64 | ||
import hashlib | ||
import urllib.parse | ||
import http.client as http_client | ||
from superagi.config.config import get_config | ||
from sqlalchemy.orm import Session | ||
from superagi.models.tool_config import ToolConfig | ||
from superagi.resource_manager.manager import ResourceManager | ||
|
||
class Creds: | ||
|
||
def __init__(self,api_key, api_key_secret, oauth_token, oauth_token_secret): | ||
self.api_key = api_key | ||
self.api_key_secret = api_key_secret | ||
self.oauth_token = oauth_token | ||
self.oauth_token_secret = oauth_token_secret | ||
|
||
class TwitterTokens: | ||
TransformerOptimus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
session: Session | ||
|
||
def get_request_token(self,api_data): | ||
api_key = api_data["api_key"] | ||
api_secret_key = api_data["api_secret"] | ||
http_method = 'POST' | ||
base_url = 'https://api.twitter.com/oauth/request_token' | ||
|
||
params = { | ||
'oauth_callback': 'http://localhost:3000/api/oauth-twitter', | ||
'oauth_consumer_key': api_key, | ||
'oauth_nonce': self.gen_nonce(), | ||
'oauth_signature_method': 'HMAC-SHA1', | ||
'oauth_timestamp': int(time.time()), | ||
'oauth_version': '1.0' | ||
} | ||
|
||
params_sorted = sorted(params.items()) | ||
params_qs = '&'.join([f'{k}={self.percent_encode(str(v))}' for k, v in params_sorted]) | ||
|
||
base_string = f'{http_method}&{self.percent_encode(base_url)}&{self.percent_encode(params_qs)}' | ||
|
||
signing_key = f'{self.percent_encode(api_secret_key)}&' | ||
signature = hmac.new(signing_key.encode(), base_string.encode(), hashlib.sha1) | ||
params['oauth_signature'] = base64.b64encode(signature.digest()).decode() | ||
|
||
auth_header = 'OAuth ' + ', '.join([f'{k}="{self.percent_encode(str(v))}"' for k, v in params.items()]) | ||
|
||
headers = { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
'Authorization': auth_header | ||
} | ||
conn = http_client.HTTPSConnection("api.twitter.com") | ||
conn.request("POST", "/oauth/request_token", "", headers) | ||
res = conn.getresponse() | ||
response_data = res.read().decode('utf-8') | ||
conn.close() | ||
request_token_resp = dict(urllib.parse.parse_qsl(response_data)) | ||
return request_token_resp | ||
|
||
def percent_encode(self, val): | ||
return urllib.parse.quote(val, safe='') | ||
|
||
def gen_nonce(self): | ||
nonce = ''.join([str(random.randint(0, 9)) for i in range(32)]) | ||
return nonce | ||
|
||
def get_twitter_creds(self, toolkit_id): | ||
file_name = "twitter_credentials.pickle" | ||
root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') | ||
file_path = file_name | ||
if root_dir is not None: | ||
root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir | ||
root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" | ||
file_path = root_dir + file_name | ||
else: | ||
file_path = os.getcwd() + "/" + file_name | ||
if os.path.exists(file_path): | ||
with open(file_path,'rb') as file: | ||
creds = pickle.load(file) | ||
if isinstance(creds, str): | ||
creds = json.loads(creds) | ||
twitter_creds = self.session.query(ToolConfig).filter(ToolConfig.toolkit_id == toolkit_id).all() | ||
api_key = "" | ||
api_key_secret = "" | ||
for credentials in twitter_creds: | ||
credentials = credentials.__dict__ | ||
if credentials["key"] == "TWITTER_API_KEY": | ||
api_key = credentials["value"] | ||
if credentials["key"] == "TWITTER_API_SECRET": | ||
api_key_secret = credentials["value"] | ||
final_creds = Creds(api_key, api_key_secret, creds["oauth_token"], creds["oauth_token_secret"]) | ||
return final_creds |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you move this to twitter_auth.rb in controller.