From 48497b3c73184ac479e9b8d9d26d6f02d35f0196 Mon Sep 17 00:00:00 2001 From: Botu SUN Date: Thu, 30 Oct 2025 20:50:14 +0900 Subject: [PATCH] Using Gemini v2.5 free tier --- .claude/settings.local.json | 12 ------- .gitignore | 1 + README.md | 12 +++---- .../tweetbot/service/GeminiService.java | 35 ++++++++++++------- 4 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 24244a5..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(tree:*)", - "Bash(mvn clean compile:*)", - "WebFetch(domain:github.com)", - "Bash(rm:*)" - ], - "deny": [], - "ask": [] - } -} diff --git a/.gitignore b/.gitignore index a86511d..51a5cc2 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ tweetbot.log # Java hs_err_pid* replay_pid* +/.claude/ diff --git a/README.md b/README.md index a43f595..50bdd00 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ An automated Twitter bot that generates and posts tweets using Google Gemini's f ## Features -- Generate tweet content using Google Gemini (gemini-2.0-flash-exp) - **Free Tier** +- Generate tweet content using Google Gemini (gemini-2.5-flash) - **Free Tier** - **Three operation modes:** - Interactive mode with manual approval - Auto-post mode for one-time tweets @@ -35,10 +35,10 @@ An automated Twitter bot that generates and posts tweets using Google Gemini's f 4. Select or create a Google Cloud project 5. Copy the API key and save it securely -**Note:** The free tier of Google Gemini includes: -- 15 requests per minute -- 1 million tokens per minute -- 1,500 requests per day +**Note:** The free tier of Gemini 2.5 Flash includes: +- 10 requests per minute +- 250,000 tokens per minute +- 250 requests per day - More than enough for a tweet bot! ### 2. Get Twitter API Credentials @@ -382,7 +382,7 @@ This error means your Twitter API access level doesn't support v1.1 tweet postin ### "Failed to generate tweet" 1. Verify your Gemini API key is valid -2. Check that you're within the free tier rate limits (15 RPM, 1500 RPD) +2. Check that you're within the free tier rate limits (10 RPM, 250 RPD) 3. Review the error message in logs 4. Ensure your Google Cloud project is properly configured diff --git a/src/main/java/com/voidcode/tweetbot/service/GeminiService.java b/src/main/java/com/voidcode/tweetbot/service/GeminiService.java index f8abf9e..2210485 100644 --- a/src/main/java/com/voidcode/tweetbot/service/GeminiService.java +++ b/src/main/java/com/voidcode/tweetbot/service/GeminiService.java @@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit; public class GeminiService { private static final Logger logger = LoggerFactory.getLogger(GeminiService.class); private static final int MAX_TWEET_LENGTH = 280; - private static final String GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent"; + private static final String GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent"; private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); private final String apiKey; @@ -27,7 +27,7 @@ public class GeminiService { .writeTimeout(60, TimeUnit.SECONDS) .build(); this.objectMapper = new ObjectMapper(); - logger.info("Gemini service initialized with model: gemini-2.0-flash-exp"); + logger.info("Gemini service initialized with model: gemini-2.5-flash"); } /** @@ -39,19 +39,16 @@ public class GeminiService { try { logger.info("Generating tweet with prompt: {}", prompt); - // Build the request body - String systemInstruction = "You are a creative social media content creator. Generate engaging tweets that are concise, " + - "interesting, and within Twitter's character limit. Do not include hashtags unless specifically " + - "requested. Keep it under 280 characters."; - - String userPrompt = prompt + " Keep it under " + MAX_TWEET_LENGTH + " characters."; + // Build the request body with simplified prompt + String fullPrompt = "You are a creative social media content creator. Generate an engaging tweet (under 280 characters) about: " + prompt; String jsonBody = String.format( - "{\"contents\":[{\"parts\":[{\"text\":\"%s\\n\\n%s\"}]}],\"generationConfig\":{\"temperature\":0.8,\"maxOutputTokens\":100}}", - escapeJson(systemInstruction), - escapeJson(userPrompt) + "{\"contents\":[{\"parts\":[{\"text\":\"%s\"}]}],\"generationConfig\":{\"temperature\":0.8,\"maxOutputTokens\":100000}}", + escapeJson(fullPrompt) ); + logger.debug("Request body: {}", jsonBody); + // Build the request Request request = new Request.Builder() .url(GEMINI_API_URL + "?key=" + apiKey) @@ -84,7 +81,21 @@ public class GeminiService { JsonNode parts = content.get("parts"); if (parts == null || parts.isEmpty()) { - throw new RuntimeException("No parts in Gemini response"); + // Check for finish reason to understand why content was blocked + JsonNode finishReason = candidates.get(0).get("finishReason"); + JsonNode safetyRatings = candidates.get(0).get("safetyRatings"); + + String errorMsg = "No parts in Gemini response."; + if (finishReason != null) { + errorMsg += " Finish reason: " + finishReason.asText(); + } + if (safetyRatings != null) { + errorMsg += " Safety ratings: " + safetyRatings.toString(); + } + errorMsg += " Full candidate: " + candidates.get(0).toString(); + + logger.error(errorMsg); + throw new RuntimeException(errorMsg); } String generatedText = parts.get(0).get("text").asText().trim();