Using Gemini v2.5 free tier

This commit is contained in:
2025-10-30 20:50:14 +09:00
parent 9b8e79877f
commit 48497b3c73
4 changed files with 30 additions and 30 deletions

View File

@@ -1,12 +0,0 @@
{
"permissions": {
"allow": [
"Bash(tree:*)",
"Bash(mvn clean compile:*)",
"WebFetch(domain:github.com)",
"Bash(rm:*)"
],
"deny": [],
"ask": []
}
}

1
.gitignore vendored
View File

@@ -45,3 +45,4 @@ tweetbot.log
# Java # Java
hs_err_pid* hs_err_pid*
replay_pid* replay_pid*
/.claude/

View File

@@ -4,7 +4,7 @@ An automated Twitter bot that generates and posts tweets using Google Gemini's f
## Features ## 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:** - **Three operation modes:**
- Interactive mode with manual approval - Interactive mode with manual approval
- Auto-post mode for one-time tweets - 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 4. Select or create a Google Cloud project
5. Copy the API key and save it securely 5. Copy the API key and save it securely
**Note:** The free tier of Google Gemini includes: **Note:** The free tier of Gemini 2.5 Flash includes:
- 15 requests per minute - 10 requests per minute
- 1 million tokens per minute - 250,000 tokens per minute
- 1,500 requests per day - 250 requests per day
- More than enough for a tweet bot! - More than enough for a tweet bot!
### 2. Get Twitter API Credentials ### 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" ### "Failed to generate tweet"
1. Verify your Gemini API key is valid 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 3. Review the error message in logs
4. Ensure your Google Cloud project is properly configured 4. Ensure your Google Cloud project is properly configured

View File

@@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit;
public class GeminiService { public class GeminiService {
private static final Logger logger = LoggerFactory.getLogger(GeminiService.class); private static final Logger logger = LoggerFactory.getLogger(GeminiService.class);
private static final int MAX_TWEET_LENGTH = 280; 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 static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
private final String apiKey; private final String apiKey;
@@ -27,7 +27,7 @@ public class GeminiService {
.writeTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS)
.build(); .build();
this.objectMapper = new ObjectMapper(); 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 { try {
logger.info("Generating tweet with prompt: {}", prompt); logger.info("Generating tweet with prompt: {}", prompt);
// Build the request body // Build the request body with simplified prompt
String systemInstruction = "You are a creative social media content creator. Generate engaging tweets that are concise, " + String fullPrompt = "You are a creative social media content creator. Generate an engaging tweet (under 280 characters) about: " + prompt;
"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.";
String jsonBody = String.format( String jsonBody = String.format(
"{\"contents\":[{\"parts\":[{\"text\":\"%s\\n\\n%s\"}]}],\"generationConfig\":{\"temperature\":0.8,\"maxOutputTokens\":100}}", "{\"contents\":[{\"parts\":[{\"text\":\"%s\"}]}],\"generationConfig\":{\"temperature\":0.8,\"maxOutputTokens\":100000}}",
escapeJson(systemInstruction), escapeJson(fullPrompt)
escapeJson(userPrompt)
); );
logger.debug("Request body: {}", jsonBody);
// Build the request // Build the request
Request request = new Request.Builder() Request request = new Request.Builder()
.url(GEMINI_API_URL + "?key=" + apiKey) .url(GEMINI_API_URL + "?key=" + apiKey)
@@ -84,7 +81,21 @@ public class GeminiService {
JsonNode parts = content.get("parts"); JsonNode parts = content.get("parts");
if (parts == null || parts.isEmpty()) { 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(); String generatedText = parts.get(0).get("text").asText().trim();