Replace ChatGPT with Google Gemini

This commit is contained in:
2025-10-29 21:57:07 +09:00
parent c39b41a21f
commit 8f38bc82a0
9 changed files with 182 additions and 145 deletions

View File

@@ -1,7 +1,7 @@
package com.voidcode.tweetbot;
import com.voidcode.tweetbot.config.Config;
import com.voidcode.tweetbot.service.OpenAIService;
import com.voidcode.tweetbot.service.GeminiService;
import com.voidcode.tweetbot.service.TwitterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit;
public class TweetBot {
private static final Logger logger = LoggerFactory.getLogger(TweetBot.class);
private static final int SCHEDULE_INTERVAL_MINUTES = 30;
private static OpenAIService openAIService;
private static GeminiService geminiService;
private static TwitterService twitterService;
private static String tweetPrompt;
private static String imagePath;
@@ -48,7 +48,7 @@ public class TweetBot {
// Load configuration
logger.info("Loading configuration...");
String openAIApiKey = Config.getOpenAIApiKey();
String geminiApiKey = Config.getGeminiApiKey();
String twitterApiKey = Config.getTwitterApiKey();
String twitterApiSecret = Config.getTwitterApiSecret();
String twitterAccessToken = Config.getTwitterAccessToken();
@@ -66,7 +66,7 @@ public class TweetBot {
// Initialize services
logger.info("Initializing services...");
openAIService = new OpenAIService(openAIApiKey);
geminiService = new GeminiService(geminiApiKey);
twitterService = new TwitterService(
twitterApiKey,
twitterApiSecret,
@@ -114,8 +114,8 @@ public class TweetBot {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logger.info("Shutdown signal received, stopping TweetBot...");
isRunning = false;
if (openAIService != null) {
openAIService.shutdown();
if (geminiService != null) {
geminiService.shutdown();
}
logger.info("TweetBot stopped gracefully");
}));
@@ -157,7 +157,7 @@ public class TweetBot {
try {
// Generate tweet content
logger.info("Generating tweet content...");
String tweetContent = openAIService.generateTweet(tweetPrompt);
String tweetContent = geminiService.generateTweet(tweetPrompt);
// Display generated content
System.out.println("\n" + "=".repeat(60));
@@ -196,8 +196,8 @@ public class TweetBot {
} finally {
// Cleanup
if (openAIService != null) {
openAIService.shutdown();
if (geminiService != null) {
geminiService.shutdown();
}
logger.info("TweetBot finished");
}
@@ -209,7 +209,7 @@ public class TweetBot {
logger.info("[{}] Starting scheduled tweet posting...", timestamp);
// Generate tweet content
String tweetContent = openAIService.generateTweet(tweetPrompt);
String tweetContent = geminiService.generateTweet(tweetPrompt);
logger.info("Generated tweet: {}", tweetContent);
// Post tweet
@@ -233,7 +233,7 @@ public class TweetBot {
}
private static void printUsage() {
System.out.println("TweetBot - Automated Twitter posting with ChatGPT");
System.out.println("TweetBot - Automated Twitter posting with Google Gemini");
System.out.println("\nUsage:");
System.out.println(" java -jar tweetbot.jar [OPTIONS] [CUSTOM_PROMPT]");
System.out.println("\nOptions:");

View File

@@ -8,12 +8,12 @@ public class Config {
.ignoreIfMissing()
.load();
// OpenAI Configuration
public static String getOpenAIApiKey() {
return getEnvOrThrow("OPENAI_API_KEY");
// Google Gemini Configuration
public static String getGeminiApiKey() {
return getEnvOrThrow("GEMINI_API_KEY");
}
// Twitter Configuration
// Twitter Configuration (OAuth 1.0a)
public static String getTwitterApiKey() {
return getEnvOrThrow("TWITTER_API_KEY");
}
@@ -30,10 +30,6 @@ public class Config {
return getEnvOrThrow("TWITTER_ACCESS_TOKEN_SECRET");
}
public static String getTwitterBearerToken() {
return getEnvOrThrow("TWITTER_BEARER_TOKEN");
}
// Tweet Generation Configuration
public static String getTweetPrompt() {
return dotenv.get("TWEET_PROMPT", "Write a short, engaging tweet about technology");

View File

@@ -0,0 +1,132 @@
package com.voidcode.tweetbot.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
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 MediaType JSON = MediaType.get("application/json; charset=utf-8");
private final String apiKey;
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper;
public GeminiService(String apiKey) {
this.apiKey = apiKey;
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
this.objectMapper = new ObjectMapper();
logger.info("Gemini service initialized with model: gemini-2.0-flash-exp");
}
/**
* Generate tweet content using Google Gemini
* @param prompt The prompt to generate the tweet
* @return Generated tweet text
*/
public String generateTweet(String prompt) {
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.";
String jsonBody = String.format(
"{\"contents\":[{\"parts\":[{\"text\":\"%s\\n\\n%s\"}]}],\"generationConfig\":{\"temperature\":0.8,\"maxOutputTokens\":100}}",
escapeJson(systemInstruction),
escapeJson(userPrompt)
);
// Build the request
Request request = new Request.Builder()
.url(GEMINI_API_URL + "?key=" + apiKey)
.post(RequestBody.create(jsonBody, JSON))
.build();
// Execute the request
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
String errorBody = response.body() != null ? response.body().string() : "No error body";
logger.error("Gemini API request failed: {} - {}", response.code(), errorBody);
throw new IOException("Gemini API request failed: " + response.code() + " - " + errorBody);
}
String responseBody = response.body().string();
logger.debug("Gemini API response: {}", responseBody);
// Parse the response
JsonNode rootNode = objectMapper.readTree(responseBody);
JsonNode candidates = rootNode.get("candidates");
if (candidates == null || candidates.isEmpty()) {
throw new RuntimeException("No candidates in Gemini response");
}
JsonNode content = candidates.get(0).get("content");
if (content == null) {
throw new RuntimeException("No content in Gemini response");
}
JsonNode parts = content.get("parts");
if (parts == null || parts.isEmpty()) {
throw new RuntimeException("No parts in Gemini response");
}
String generatedText = parts.get(0).get("text").asText().trim();
// Remove quotes if the AI wrapped the tweet in quotes
generatedText = generatedText.replaceAll("^[\"']|[\"']$", "");
// Ensure it's within Twitter's character limit
if (generatedText.length() > MAX_TWEET_LENGTH) {
logger.warn("Generated tweet exceeds {} characters, truncating", MAX_TWEET_LENGTH);
generatedText = generatedText.substring(0, MAX_TWEET_LENGTH - 3) + "...";
}
logger.info("Generated tweet: {}", generatedText);
return generatedText;
}
} catch (IOException e) {
logger.error("Error generating tweet", e);
throw new RuntimeException("Failed to generate tweet: " + e.getMessage(), e);
}
}
/**
* Escape special characters for JSON
*/
private String escapeJson(String text) {
return text.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
/**
* Shutdown the Gemini service
*/
public void shutdown() {
if (httpClient != null) {
httpClient.dispatcher().executorService().shutdown();
httpClient.connectionPool().evictAll();
logger.info("Gemini service shut down");
}
}
}

View File

@@ -1,91 +0,0 @@
package com.voidcode.tweetbot.service;
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatCompletionResult;
import com.theokanning.openai.completion.chat.ChatMessage;
import com.theokanning.openai.completion.chat.ChatMessageRole;
import com.theokanning.openai.service.OpenAiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
public class OpenAIService {
private static final Logger logger = LoggerFactory.getLogger(OpenAIService.class);
private final OpenAiService openAiService;
private static final int MAX_TWEET_LENGTH = 280;
public OpenAIService(String apiKey) {
this.openAiService = new OpenAiService(apiKey, Duration.ofSeconds(60));
logger.info("OpenAI service initialized");
}
/**
* Generate tweet content using ChatGPT
* @param prompt The prompt to generate the tweet
* @return Generated tweet text
*/
public String generateTweet(String prompt) {
try {
logger.info("Generating tweet with prompt: {}", prompt);
// Create the system message to set context
ChatMessage systemMessage = new ChatMessage(
ChatMessageRole.SYSTEM.value(),
"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."
);
// Create the user message with the prompt
ChatMessage userMessage = new ChatMessage(
ChatMessageRole.USER.value(),
prompt + " Keep it under " + MAX_TWEET_LENGTH + " characters."
);
List<ChatMessage> messages = new ArrayList<>();
messages.add(systemMessage);
messages.add(userMessage);
// Create the chat completion request
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model("gpt-3.5-turbo")
.messages(messages)
.temperature(0.8)
.maxTokens(100)
.build();
// Get the response
ChatCompletionResult result = openAiService.createChatCompletion(request);
String generatedText = result.getChoices().get(0).getMessage().getContent().trim();
// Remove quotes if the AI wrapped the tweet in quotes
generatedText = generatedText.replaceAll("^[\"']|[\"']$", "");
// Ensure it's within Twitter's character limit
if (generatedText.length() > MAX_TWEET_LENGTH) {
logger.warn("Generated tweet exceeds {} characters, truncating", MAX_TWEET_LENGTH);
generatedText = generatedText.substring(0, MAX_TWEET_LENGTH - 3) + "...";
}
logger.info("Generated tweet: {}", generatedText);
return generatedText;
} catch (Exception e) {
logger.error("Error generating tweet", e);
throw new RuntimeException("Failed to generate tweet: " + e.getMessage(), e);
}
}
/**
* Shutdown the OpenAI service
*/
public void shutdown() {
if (openAiService != null) {
openAiService.shutdownExecutor();
logger.info("OpenAI service shut down");
}
}
}