Replace ChatGPT with Google Gemini
This commit is contained in:
@@ -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:");
|
||||
|
||||
@@ -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");
|
||||
|
||||
132
src/main/java/com/voidcode/tweetbot/service/GeminiService.java
Normal file
132
src/main/java/com/voidcode/tweetbot/service/GeminiService.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user