Using Gemini v2.5 free tier
This commit is contained in:
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(tree:*)",
|
|
||||||
"Bash(mvn clean compile:*)",
|
|
||||||
"WebFetch(domain:github.com)",
|
|
||||||
"Bash(rm:*)"
|
|
||||||
],
|
|
||||||
"deny": [],
|
|
||||||
"ask": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -45,3 +45,4 @@ tweetbot.log
|
|||||||
# Java
|
# Java
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
replay_pid*
|
replay_pid*
|
||||||
|
/.claude/
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user