In my quest to grow a community for "My Gemini Journey," I embarked on a new coding project with my AI co-pilot, Gemini. The goal was to build a Python-powered follower bot that would automatically welcome new followers on X with a direct message, creating a more personal connection with my audience.
This article documents the highs and lows of that journey—the initial success, the unexpected technical hurdles, and the valuable lessons learned from a complex human-AI collaboration.
Our Roles: A Human-AI Partnership
Our collaboration on this project was a case study in modern brand-building. My primary mission is to be the visionary and project manager, defining the mission and guiding the development process. I am the human element, asking the right questions and bringing a personal touch to the final product.
As my AI co-pilot, Gemini’s mission is to be the code mentor. Gemini provided details about the first code, explained the complexities of the X API, and consistently adapted the scripts to facilitate my knowledge, and troubleshooting the errors we faced.
Grok’s was the one who provided the first code based on my idea, and the one who ensured every intervention to provide a new perspective on a persistent problem. Grok is my consultant and problem-solver, offering an alternative solution when we hit a roadblock.
This three-way partnership proved that complex problems often require diverse perspectives. While not all suggestions worked, the process itself was invaluable.
The Hurdles We Faced
While the logic was sound, the execution presented a series of unexpected challenges. Each error was a lesson in the complexity of building in a real-world, mobile-first environment.
- The TypeError Loop: Our first major roadblock was a series of TypeError messages. The tweepy library's Client object was expecting a bearer_token as an argument, but my initial scripts were sending an access_token or refresh_token as different keyword arguments. This highlighted the subtle version differences in libraries and the importance of using the correct syntax.
- "Something Went Wrong": The Redirect URL Nightmare: The most persistent and frustrating issue was a generic "something went wrong" message after the authorization step. We tried several solutions to get the temporary code from the redirected URL
- Using https://x.com: Our first attempt failed because the URL's structure was slightly different from what the script expected.
- Using a Webhook Service (webhook.site): The idea from Grok was to use a third-party service to capture the URL. This was a clever workaround for a mobile environment, but it ultimately failed because the Pydroid 3 terminal was corrupted, adding an unreadable ^@ character to the pasted URL and rendering it useless.
Inside the Code: The Script's Anatomy
Here is the exact Python script that we developed together, along with a detailed explanation of each part. This script represents the final, most reliable version we created to handle the authentication process.
import tweepy
import time
from urllib.parse import urlparse, parse_qs
# --- (1) App Credentials and Variables ---
CLIENT_ID = " YOUR_CLIENT_ID"
CLIENT_SECRET ="YOUR_CLIENT_SECRET_KEY"
REDIRECT_URI = "[https://x.com](https://x.com)"
ACCESS_TOKEN = ""
REFRESH_TOKEN = ""
# ---
# --- (2) The Main Authentication Function ---
def get_tokens_and_client():
if ACCESS_TOKEN and REFRESH_TOKEN:
print("Using existing tokens to authenticate...")
try:
client = tweepy.Client(
bearer_token=ACCESS_TOKEN,
refresh_token=REFRESH_TOKEN
)
client.get_me()
print("Authentication successful!")
return client
except tweepy.HTTPException as e:
print(f"Error with existing tokens: {e}")
print("Refreshing tokens...")
try:
oauth2_user_handler = tweepy.OAuth2UserHandler(
client_id=CLIENT_ID,
redirect_uri=REDIRECT_URI,
scope=["users.read", "tweet.read", "follows.read", "dm.read", "dm.write", "offline.access"],
client_secret=CLIENT_SECRET
)
token_info = oauth2_user_handler.refresh_token(REFRESH_TOKEN)
new_access_token = token_info.get("access_token")
new_refresh_token = token_info.get("refresh_token")
print("Tokens refreshed successfully! Please update them in the script.")
print(f"New Access Token: {new_access_token}")
print(f"New Refresh Token: {new_refresh_token}")
client = tweepy.Client(
bearer_token=new_access_token,
refresh_token=new_refresh_token
)
return client
except Exception as e:
print(f"Failed to refresh tokens: {e}")
return None
else:
print("Tokens not found. Starting a new authentication flow...")
oauth2_user_handler = tweepy.OAuth2UserHandler(
client_id=CLIENT_ID,
redirect_uri=REDIRECT_URI,
scope=["users.read", "tweet.read", "follows.read", "dm.read", "dm.write", "offline.access"],
client_secret=CLIENT_SECRET
)
print("Please go to the following URL to authorize the app:")
auth_url = oauth2_user_handler.get_authorization_url()
print(auth_url)
authorization_response_url = input("\nPaste the full authorization response URL here: ")
try:
parsed_url = urlparse(authorization_response_url)
query_params = parse_qs(parsed_url.query)
code = query_params.get('code', [None])[0]
if not code:
raise ValueError("Code not found in URL. Did you paste the correct URL?")
except Exception as e:
print(f"Error parsing URL: {e}")
return None
try:
token_info = oauth2_user_handler.fetch_token(f"{REDIRECT_URI}?code={code}")
access_token = token_info.get("access_token")
refresh_token = token_info.get("refresh_token")
print("Authentication successful! Here are your tokens:")
print(f"Access Token: {access_token}")
print(f"Refresh Token: {refresh_token}")
print("\nIMPORTANT: Update the script with these tokens for future runs.")
client = tweepy.Client(
bearer_token=access_token,
refresh_token=refresh_token
)
return client
except tweepy.HTTPException as e:
print(f"Error fetching token: {e}")
return None
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None
# ---
# --- (3) Placeholder for Bot Logic ---
def send_welcome_dm(client):
print("Bot is authenticated and ready to send DMs!")
# Example:
# my_id = client.get_me().data.id
# followers = client.get_users_followers(id=my_id).data
# ... your logic here
# ---
# --- (4) Main Execution Block ---
if __name__ == "__main__":
authenticated_client = get_tokens_and_client()
if authenticated_client:
send_welcome_dm(authenticated_client)
else:
print("Failed to authenticate. Exiting.")
# ---
Explanation of the Code
(1) App Credentials and Variables
This section holds the information needed to identify your application to the X API.
CLIENT_ID and CLIENT_SECRET: These are your application's unique keys, generated in the X Developer Portal. They tell X exactly who is trying to authenticate.
REDIRECT_URI: The URL that X will send the temporary code to after a user authorizes your bot. In this case, we used https://x.com, which served as a simple URL to capture the code from.
ACCESS_TOKEN and REFRESH_TOKEN: These variables are where you will manually paste the tokens you get after a successful first run. The bot will then use them for all future runs.
(2) The Main Authentication Function (get_tokens_and_client)
This function is the most complex part of the script and handles the entire authentication flow.
Token Check (if ACCESS_TOKEN and REFRESH_TOKEN): The script first checks if you've already saved your tokens. If you have, it attempts to use them to authenticate.
Token Refresh (try...except block): If the old tokens have expired, it uses the refresh_token to get a new set of access_token and refresh_token from X. This is what allows your bot to run without a human re-authorizing it every time.
Initial Authentication Flow (else block): If the tokens are not found (the very first time you run it), it generates a long authorization URL. This is the URL you open in your browser to give the bot permission to access your account.
User Input (input()): The script pauses here, waiting for you to paste the full URL from your browser's address bar. It then parses the URL to get the code it needs.
Token Exchange (fetch_token): This is the final step where the script sends the temporary code to the X API. In return, X provides the precious, long-lasting access_token and refresh_token.
(3) Placeholder for Bot Logic (send_welcome_dm)
This is the function where your bot's core functionality would live. It's a simple placeholder that shows the bot has successfully authenticated and is ready to perform its tasks, such as sending welcome DMs or fetching followers.
(4) Main Execution Block (if __name__ == "__main__":)
This is standard Python syntax that ensures the get_tokens_and_client function is called and the script starts running when you execute it. It's the entry point to our program.
A Project at 95% Completion...
We have decided to place the Welcoming Bot project on standby, not because of a technical failure in the code, but because of a final, complex logistical challenge. This is not a failure, but a necessary pause to document our journey and a testament to the value of persistence and the willingness to pivot.
On a deeper level, this project is 95% complete. The code itself is finished and fully debugged. The remaining 5% is simply the matter of getting a clean copy of the final authentication token, a step that was blocked by the unique limitations of the Pydroid 3 terminal on a mobile phone. We have a clear path forward to solve this final hurdle and are confident we can do so when we return to it.
The experience has taught me invaluable lessons about debugging complex systems and the reality of working with imperfect tools. It has also highlighted the incredible power of my human-AI partnership with Gemini and Grok.
The "Code & Connect" series will continue. We will resume working on this project together in the future with many upcoming ones. In the meantime, we will continue our journey together to discover what's coming next. Stay tuned!
P.S: If this concept resonates with you, and you're ready for the entire story, we've compiled a full roadmap of our journey, including our ethics, strategy, and finance articles, on Start Here: The Full Gemini Journey Blueprint
Ready to Build Your First AI Workflow?
Start Creating with **MY GEMINI JOURNEY WORKFLOW KIT**
Comments
Post a Comment