From 662850c35ccc4349fa99465b3e53add1b7598b56 Mon Sep 17 00:00:00 2001 From: Julian Arndt Date: Tue, 18 Jun 2024 21:21:27 +0200 Subject: [PATCH] Add WhatsApp reminder --- build.gradle | 4 +- jenkinsfiles/WhatsAppReminder/Jenkinsfile | 83 ++++++++ src/main/java/de/jeyp91/App.java | 45 +++-- .../TippligaGoogleEventManager.java | 2 +- .../tippligaforum/TippligaSQLConnector.java | 182 ++++++++++++++---- .../de/jeyp91/whatsapp/OpenAIConnector.java | 81 ++++++++ .../de/jeyp91/whatsapp/WhatsAppNotifier.java | 65 +++++++ .../de/jeyp91/whatsapp/WhatsAppReminder.java | 16 ++ 8 files changed, 422 insertions(+), 56 deletions(-) create mode 100644 jenkinsfiles/WhatsAppReminder/Jenkinsfile create mode 100644 src/main/java/de/jeyp91/whatsapp/OpenAIConnector.java create mode 100644 src/main/java/de/jeyp91/whatsapp/WhatsAppNotifier.java create mode 100644 src/main/java/de/jeyp91/whatsapp/WhatsAppReminder.java diff --git a/build.gradle b/build.gradle index 7f99f5e..704480e 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ apply plugin: 'java' apply plugin: 'application' mainClassName = 'App' -sourceCompatibility = 15 -targetCompatibility = 15 +sourceCompatibility = 16 +targetCompatibility = 16 version = '1.0' compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' diff --git a/jenkinsfiles/WhatsAppReminder/Jenkinsfile b/jenkinsfiles/WhatsAppReminder/Jenkinsfile new file mode 100644 index 0000000..234494e --- /dev/null +++ b/jenkinsfiles/WhatsAppReminder/Jenkinsfile @@ -0,0 +1,83 @@ +pipeline { + agent any + + stages { + stage('Restore tlw-database-tool') { + steps { + copyArtifacts filter: '**/tlw-database-tool-1.0.jar', fingerprintArtifacts: true, projectName: 'build tlw-database-tool', selector: lastSuccessful(), target: '.' + } + } + stage('Execute') { + steps { + script { + String jdkPath = tool name: 'OpenJDK19', type: 'jdk' + withCredentials([ + usernamePassword(credentialsId: 'aws', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_KEY'), + usernamePassword(credentialsId: 'forum_database', usernameVariable: 'TLW_DATABASE_USERNAME', passwordVariable: 'TLW_DATABASE_PASSWORD'), + usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD') + secretText(credentialsId: 'WhatsApp_Token', secretVariable: 'TLW_WHATSAPP_API_KEY') + secretText(credentialsId: 'OPENAI_TOKEN', secretVariable: 'OPENAI_TOKEN') + ]) { + withEnv([ + "JAVA_HOME=${jdkPath}/jdk-19.0.2" + ]) { + try { + sh "ls build/libs" + sh "java -jar build/libs/tlw-database-tool-1.0.jar --mode WhatsAppNotifier --season ${season} --league 1 --configFile Tippliga 2>&1 >> log.txt" + } catch (Exception e) { + telegramSendManual("TLW-Database-Tool crashed!") + } + String outputString = readFile 'log.txt' + if(outputString != "") { + ArrayList outputList = outputString.replace("\n\n", "\n").split('\n').toList() + outputList = clean(outputList) + outputList.each { + telegramSendManual(it) + } + } + } + } + } + } + } + } + post { + always { + deleteDir() + } + failure { + telegramSendManual("Build failed!\n${env.BUILD_URL}console") + } + } +} + +private ArrayList clean(ArrayList orig) { + ArrayList cleaned = new ArrayList() + orig.each{line -> + if(line.indexOf(" [main] INFO de.jeyp91 - ") >= 0) { + line = line.substring(20, line.size()) + } + + if(line.indexOf(" [main] ERROR de.jeyp91") >= 0) { + line = line.substring(20, line.size()) + } + cleaned.add(line) + } + return cleaned.unique() +} + +def telegramSendManual(String text) { + sleep 1 + if(text) { + def encodedMessage = URLEncoder.encode(text, "UTF-8") + println encodedMessage + httpRequest( + httpMode: 'GET', + contentType: 'APPLICATION_JSON', + responseHandle: 'NONE', + url: "https://api.telegram.org/bot1298223079:AAEplcQpfzFG59qNYAYuSbJKtB9HMXCCE_U/sendMessage?text=$encodedMessage&chat_id=459231986&disable_web_page_preview=true", + wrapAsMultipart: false, + consoleLogResponseBody: true + ) + } +} diff --git a/src/main/java/de/jeyp91/App.java b/src/main/java/de/jeyp91/App.java index 4726b7a..9868bd3 100644 --- a/src/main/java/de/jeyp91/App.java +++ b/src/main/java/de/jeyp91/App.java @@ -5,6 +5,7 @@ import de.jeyp91.tippligaforum.MatchesListForumUpdater; import de.jeyp91.tippliga.*; import de.jeyp91.tippligaforum.TippligaConfigProvider; import de.jeyp91.tippligaforum.TippligaSQLConnector; +import de.jeyp91.whatsapp.WhatsAppNotifier; import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.inf.ArgumentParser; import net.sourceforge.argparse4j.inf.ArgumentParserException; @@ -43,46 +44,50 @@ public class App { initOptions(args); String sql = ""; String beautifulInfo = ""; - switch(mode) { - case "MatchdaysUpdater": + switch (mode) { + case "MatchdaysUpdater" -> { TLWMatchdaysUpdater matchdaysUpdater = new TLWMatchdaysUpdater(season, league, configFile); sql = matchdaysUpdater.getUpdateSql(); beautifulInfo = matchdaysUpdater.getBeautifulInfo(); matchdaysUpdater.updateGoogleCalendar(); - break; - case "MatchesCreatorFootball": + } + case "MatchesCreatorFootball" -> { TLWMatchesCreatorFootball creator = new TLWMatchesCreatorFootball(season, league, configFile); sql = creator.getSQLInsertString(); - break; - case "MatchesUpdaterFootball": + } + case "MatchesUpdaterFootball" -> { TLWMatchesUpdaterFootball tlwMatchesUpdaterFootball = new TLWMatchesUpdaterFootball(season, league, configFile); sql = tlwMatchesUpdaterFootball.getUpdateSQL(); beautifulInfo = tlwMatchesUpdaterFootball.getBeautifulInfo(); - break; - case "MatchesResultsUpdater": + } + case "MatchesResultsUpdater" -> { TLWMatchesResultsUpdater tlwMatchesResultsUpdater = new TLWMatchesResultsUpdater(season, league, configFile); beautifulInfo = tlwMatchesResultsUpdater.getBeautifulInfo(); - break; - case "TeamsUpdater": + } + case "TeamsUpdater" -> { TLWTeamsUpdater teamsUpdater = new TLWTeamsUpdater(season, league, configFile); sql = teamsUpdater.getInsertSQL(); - break; - case "APIFootballUpdater": + } + case "APIFootballUpdater" -> { APIFootballUpdater apiFootballUpdater = new APIFootballUpdater(); apiFootballUpdater.updateAllFixtures(season); apiFootballUpdater.updateAllRounds(season); - break; - case "MatchesListGistUpdater": + } + case "MatchesListGistUpdater" -> { MatchesListForumUpdater matchesListForumUpdater = new MatchesListForumUpdater(season); matchesListForumUpdater.updateAllLeagues(season); - break; - case "PostChecksum": + } + case "PostChecksum" -> { TippligaConfigProvider configProvider = new TippligaConfigProvider(season); String checksum = configProvider.getChecksumOfConfigPost(configFile); System.out.println(checksum); - break; - default: - break; + } + case "WhatsAppNotifier" -> { + WhatsAppNotifier notifier = new WhatsAppNotifier(); + notifier.sendNotifications(); + } + default -> { + } } if(!StatusHolder.getError() && !sql.equals("")) { TippligaSQLConnector con = TippligaSQLConnector.getInstance(); @@ -99,7 +104,7 @@ public class App { parser.addArgument("-m", "--mode") .dest("mode") - .choices("MatchdaysUpdater", "MatchesCreatorFootball", "MatchesUpdaterFootball", "MatchesResultsUpdater", "TeamsUpdater", "APIFootballUpdater", "MatchesListGistUpdater", "PostChecksum") + .choices("MatchdaysUpdater", "MatchesCreatorFootball", "MatchesUpdaterFootball", "MatchesResultsUpdater", "TeamsUpdater", "APIFootballUpdater", "MatchesListGistUpdater", "PostChecksum", "WhatsAppNotifier") .help("") .required(true) .type(String.class); diff --git a/src/main/java/de/jeyp91/googlecalendar/TippligaGoogleEventManager.java b/src/main/java/de/jeyp91/googlecalendar/TippligaGoogleEventManager.java index f255523..af90b04 100644 --- a/src/main/java/de/jeyp91/googlecalendar/TippligaGoogleEventManager.java +++ b/src/main/java/de/jeyp91/googlecalendar/TippligaGoogleEventManager.java @@ -157,7 +157,7 @@ public class TippligaGoogleEventManager { .setDateTime(deliveryDateTime) .setTimeZone("Europe/Berlin"); - // Set reminder to 12 hours before + // Set reminder to 12 remainingHours before Event.Reminders reminders = new Event.Reminders(); reminders.setUseDefault(true); diff --git a/src/main/java/de/jeyp91/tippligaforum/TippligaSQLConnector.java b/src/main/java/de/jeyp91/tippligaforum/TippligaSQLConnector.java index 06ada02..6b81a02 100644 --- a/src/main/java/de/jeyp91/tippligaforum/TippligaSQLConnector.java +++ b/src/main/java/de/jeyp91/tippligaforum/TippligaSQLConnector.java @@ -5,6 +5,7 @@ import de.jeyp91.tippliga.TLWLeague; import de.jeyp91.tippliga.TLWMatch; import de.jeyp91.tippliga.TLWMatchday; import de.jeyp91.tippliga.TLWTeam; +import de.jeyp91.whatsapp.WhatsAppReminder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -64,7 +65,7 @@ public class TippligaSQLConnector { } public static TippligaSQLConnector getInstance() { - if(tlwSqlCon == null) { + if (tlwSqlCon == null) { tlwSqlCon = new TippligaSQLConnector(); } return tlwSqlCon; @@ -157,37 +158,37 @@ public class TippligaSQLConnector { public ArrayList getUpdatedMatchdaysBasedOnMatches(String season, String league) { String queryString = """ - SELECT - d1.season, - d1.league, - d1.matchday, - d1.status, - d1.delivery_date, - min(STR_TO_DATE(d2.match_datetime, '%Y-%m-%d %H:%i:%s')) as delivery_date_2, - min(STR_TO_DATE(d3.match_datetime, '%Y-%m-%d %H:%i:%s')) as delivery_date_3, - d1.matchday_name, - d1.matches - FROM ( SELECT - md.season, - md.league, - md.matchday, - md.status, - min(STR_TO_DATE(ma.match_datetime, '%Y-%m-%d %H:%i:%s')) AS delivery_date, - md.matchday_name, - md.matches - FROM phpbb_footb_matchdays md - LEFT JOIN phpbb_footb_matches ma ON (md.season = ma.season AND md.league = ma.league AND md.matchday = ma.matchday) - WHERE md.season =""" + " " + season + " " + """ - AND md.league =""" + " " + league + " " + """ - GROUP BY md.season, md.league, md.matchday - ) AS d1 - LEFT JOIN phpbb_footb_matches d2 ON (d1.season = d2.season AND d1.league = d2.league AND d1.matchday = d2.matchday - AND DATEDIFF(DATE(STR_TO_DATE(d2.match_datetime, '%Y-%m-%d %H:%i:%s')), DATE(d1.delivery_date)) > 0) - LEFT JOIN phpbb_footb_matches d3 ON (d1.season = d3.season AND d1.league = d3.league AND d1.matchday = d3.matchday - AND DATEDIFF(DATE(STR_TO_DATE(d3.match_datetime, '%Y-%m-%d %H:%i:%s')), DATE(d1.delivery_date)) > 6) - GROUP BY d1.season, d1.league, d1.matchday; - """; + d1.season, + d1.league, + d1.matchday, + d1.status, + d1.delivery_date, + min(STR_TO_DATE(d2.match_datetime, '%Y-%m-%d %H:%i:%s')) as delivery_date_2, + min(STR_TO_DATE(d3.match_datetime, '%Y-%m-%d %H:%i:%s')) as delivery_date_3, + d1.matchday_name, + d1.matches + FROM ( + SELECT + md.season, + md.league, + md.matchday, + md.status, + min(STR_TO_DATE(ma.match_datetime, '%Y-%m-%d %H:%i:%s')) AS delivery_date, + md.matchday_name, + md.matches + FROM phpbb_footb_matchdays md + LEFT JOIN phpbb_footb_matches ma ON (md.season = ma.season AND md.league = ma.league AND md.matchday = ma.matchday) + WHERE md.season =""" + " " + season + " " + """ + AND md.league =""" + " " + league + " " + """ + GROUP BY md.season, md.league, md.matchday + ) AS d1 + LEFT JOIN phpbb_footb_matches d2 ON (d1.season = d2.season AND d1.league = d2.league AND d1.matchday = d2.matchday + AND DATEDIFF(DATE(STR_TO_DATE(d2.match_datetime, '%Y-%m-%d %H:%i:%s')), DATE(d1.delivery_date)) > 0) + LEFT JOIN phpbb_footb_matches d3 ON (d1.season = d3.season AND d1.league = d3.league AND d1.matchday = d3.matchday + AND DATEDIFF(DATE(STR_TO_DATE(d3.match_datetime, '%Y-%m-%d %H:%i:%s')), DATE(d1.delivery_date)) > 6) + GROUP BY d1.season, d1.league, d1.matchday; + """; ArrayList matchdays = new ArrayList<>(); ResultSet rset = executeQuery(queryString); @@ -243,7 +244,7 @@ public class TippligaSQLConnector { return post; } - public ResultSet executeQuery (String queryString){ + public ResultSet executeQuery(String queryString) { Statement stmt; ResultSet rset = null; try { @@ -255,7 +256,7 @@ public class TippligaSQLConnector { return rset; } - public void executeUpdate (String queryString){ + public void executeUpdate(String queryString) { Statement stmt; try { stmt = con.createStatement(); @@ -321,4 +322,119 @@ public class TippligaSQLConnector { post = post.replace("'", "'"); return post; } + + public ArrayList getNextWhatsAppReminders(int hours) { + final String remindersToSendQuery = """ + WITH delivery_date_next_day AS ( + SELECT + m.season, + m.league, + l.league_name, + l.league_type, + m.matchday, + m.matchday_name, + delivery_date, + STR_TO_DATE(delivery_date, '%Y-%m-%d %H:%i:%s') as parsed_delivery_date + FROM phpbb_footb_matchdays m + LEFT JOIN phpbb_footb_leagues l ON (m.season = l.season AND m.league = l.league) + HAVING parsed_delivery_date > NOW() AND parsed_delivery_date < NOW() + INTERVAL {{remainingHours}} HOUR + ) + SELECT + d.season, + d.league, + d.league_name, + d.matchday, + IF(d.matchday_name = '', CONCAT(d.matchday, '. Spieltag'), d.matchday_name) AS matchday_name, + d.delivery_date, + CASE + WHEN DATE (d.parsed_delivery_date) = CURDATE() THEN 'heute' + WHEN DATE (d.parsed_delivery_date) = CURDATE() + INTERVAL 1 DAY THEN 'morgen' + ELSE DATE (d.parsed_delivery_date) + END AS today_or_tomorrow, + TIME_FORMAT(TIME(d.parsed_delivery_date), '%H:%i') AS time, + '{{remainingHours}}' AS remaining_hours, + b.user_id, + u.username, + pf.pf_handynummer, + count(*) AS missing_bets + FROM delivery_date_next_day d + LEFT JOIN phpbb_footb_matches m + ON (d.season = m.season + AND d.league = m.league + AND d.matchday = m.matchday) + LEFT JOIN phpbb_footb_bets b + ON (m.season = b.season + AND m.league = b.league + AND m.match_no = b.match_no) + LEFT JOIN phpbb_footb_matches mb + ON (d.season = mb.season + AND d.league = mb.league - 50 + AND d.matchday = mb.matchday + AND (b.user_id + 2000 = mb.team_id_home OR b.user_id + 2000 = mb.team_id_guest)) + LEFT JOIN phpbb_footb_whatsapp_reminders r + ON (m.season = r.season + AND m.league = r.league + AND m.matchday = r.matchday + AND r.delivery_date = d.delivery_date + AND b.user_id = r.user_id + AND r.remaining_hours = {{remainingHours}}) + LEFT JOIN phpbb_users u + ON (b.user_id = u.user_id) + LEFT JOIN phpbb_profile_fields_data pf + ON (u.user_id = pf.user_id) + WHERE ((d.league_type = 1 AND mb.match_no IS NOT NULL) OR d.league_type = 2) + AND (b.goals_home = '' OR b.goals_guest = '') + AND r.timestamp IS NULL + AND m.status = 0 + AND pf.pf_handynummer != '' + GROUP BY d.league_name, d.matchday, u.username, pf.pf_handynummer; + """.replace("{{remainingHours}}", "" + hours); + final int SEASON = 1; + final int LEAGUE = 2; + final int LEAGUE_NAME = 3; + final int MATCHDAY = 4; + final int MATCHDAY_NAME = 5; + final int DELIVERY_DATE = 6; + final int TODAY_OR_TOMORROW = 7; + final int TIME = 8; + final int HOURS = 9; + final int USER_ID = 10; + final int USERNAME = 11; + final int PHONE_NUMBER = 12; + final int MISSING_BETS = 13; + try { + ResultSet rset = executeQuery(remindersToSendQuery); + ArrayList reminders = new ArrayList<>(); + while (rset.next()) { + reminders.add( + new WhatsAppReminder( + Integer.parseInt(rset.getString(SEASON)), + Integer.parseInt(rset.getString(LEAGUE)), + rset.getString(LEAGUE_NAME), + Integer.parseInt(rset.getString(MATCHDAY)), + rset.getString(MATCHDAY_NAME), + rset.getString(DELIVERY_DATE), + rset.getString(TODAY_OR_TOMORROW), + rset.getString(TIME), + Integer.parseInt(rset.getString(HOURS)), + Integer.parseInt(rset.getString(USER_ID)), + rset.getString(USERNAME), + rset.getString(PHONE_NUMBER), + Integer.parseInt(rset.getString(MISSING_BETS)) + ) + ); + } + return reminders; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void markWhatsAppReminderAsSent(WhatsAppReminder reminder) { + String query = """ + INSERT INTO phpbb_footb_whatsapp_reminders + (season, league, matchday, delivery_date, user_id, remaining_hours) + VALUES + (""" + reminder.season() + ", " + reminder.league() + ", " + reminder.matchday() + ", '" + reminder.deliveryDate() + "', " + reminder.userId() + ", " + reminder.remainingHours() + ");"; + executeUpdate(query); + } } diff --git a/src/main/java/de/jeyp91/whatsapp/OpenAIConnector.java b/src/main/java/de/jeyp91/whatsapp/OpenAIConnector.java new file mode 100644 index 0000000..94d3ccf --- /dev/null +++ b/src/main/java/de/jeyp91/whatsapp/OpenAIConnector.java @@ -0,0 +1,81 @@ +package de.jeyp91.whatsapp; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.HashMap; + +import static de.jeyp91.apifootball.APIFootballConnector.stringToJSONObject; + +public class OpenAIConnector { + private final HttpClient client; + private final String OPENAI_TOKEN = System.getenv("OPENAI_TOKEN"); + private final String OPENAPI_URL = "https://api.openai.com/v1/chat/completions"; + private final String OPENAPI_MODEL = "gpt-3.5-turbo-0125"; + private final HashMap generatedMessages = new HashMap<>(); + + public OpenAIConnector() { + client = HttpClient.newBuilder().build(); + } + + public String getReminderMessage(WhatsAppReminder reminder) { + String key = reminder.leagueName() + reminder.matchdayName() + reminder.todayTomorrow() + reminder.time(); + String message; + if (generatedMessages.containsKey(key)) { + message = generatedMessages.get(key); + } else { + message = executeReminderPrompt(reminder.leagueName(), reminder.matchdayName(), reminder.todayTomorrow(), reminder.time(), reminder.remainingHours()); + generatedMessages.put(key, message); + } + message = message.replace("Hey", "Hey " + reminder.username()); + String end = "\nDu kannst deine Tipps wie immer unter https://tippliga-wuerzburg.de/app.php/football/bet abgeben.\nViele Grüße\nDie Tippliga Admins"; + message += end; + return message; + } + + private String executeReminderPrompt(String leagueName, String matchdayName, String deliveryDay, String time, int remainingHours) { + String systemPrompt = "Generiere eine Nachricht, die dem Zweck dient, jemanden daran zu erinnern, seine noch fehlenden Tipps für den kommenden Spieltag abzuschicken. Die Nachricht muss so klingen, als ob ein Freund sie in natürlicher Sprache abschickt. Sie muss auf Deutsch sein. Die Nachricht muss den richtigen Singular und Plural für die Anzahl der fehlenden Wetten verwenden. Halte dich genau an die Vorlage und ersetze die Teile gekennzeichnet durch \"{}\" durch passende Passagen. Die Antwort auf diesen Prompt soll ausschließlich die Nachricht sein.\n\n " + +"Dies ist die Vorlage:\n" + +"Hey,\n" + + (remainingHours == 24 ? "{ein netter einleitender Satz der \"league_name\", \"matchday_name\" enthält}.\n" : "{ein einleitender Satz der \"league_name\", \"matchday_name\" enthält und klar macht, dass die Zeit abläuft und nur noch weniger als eine Stunde Zeit für die Abgabe der Tipps bleibt}.\n") + +"Bitte gib die fehlenden Tipps bis {delivery_day} um {time} ab."; + JSONObject userInput = new JSONObject(); + userInput.put("league_name", leagueName); + userInput.put("matchday_name", matchdayName); + userInput.put("delivery_day", deliveryDay); + userInput.put("time", time); + String user = userInput.toJSONString(); + JSONObject jsonBody = new JSONObject(); + jsonBody.put("model", OPENAPI_MODEL); + JSONArray messages = new JSONArray(); + JSONObject system = new JSONObject(); + system.put("role", "system"); + system.put("content", systemPrompt); + messages.add(system); + JSONObject userMessage = new JSONObject(); + userMessage.put("role", "user"); + userMessage.put("content", user); + messages.add(userMessage); + jsonBody.put("messages", messages); + HttpRequest openAIRequest = HttpRequest.newBuilder() + .uri(URI.create(OPENAPI_URL)) + .headers("Content-Type", "application/json") + .headers("Authorization", "Bearer " + OPENAI_TOKEN) + .POST(HttpRequest.BodyPublishers.ofString(jsonBody.toJSONString())) + .build(); + JSONObject response = new JSONObject(); + try { + response = stringToJSONObject(client.send(openAIRequest, HttpResponse.BodyHandlers.ofString()).body()); + } catch (Exception e) { + e.printStackTrace(); + } + JSONArray choices = (JSONArray) response.get("choices"); + JSONObject choice = (JSONObject) choices.get(0); + JSONObject message = (JSONObject) choice.get("message"); + return message.get("content").toString(); + } +} diff --git a/src/main/java/de/jeyp91/whatsapp/WhatsAppNotifier.java b/src/main/java/de/jeyp91/whatsapp/WhatsAppNotifier.java new file mode 100644 index 0000000..34c3b4a --- /dev/null +++ b/src/main/java/de/jeyp91/whatsapp/WhatsAppNotifier.java @@ -0,0 +1,65 @@ +package de.jeyp91.whatsapp; + +import de.jeyp91.tippligaforum.TippligaSQLConnector; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.json.simple.JSONObject; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.ArrayList; + +public class WhatsAppNotifier { + private final String host = System.getenv("TLW_WHATSAPP_HOST"); + private final String port = System.getenv("TLW_WHATSAPP_PORT"); + private final String apiKey = System.getenv("TLW_WHATSAPP_API_KEY"); + private static final Logger logger = LogManager.getLogger(WhatsAppNotifier.class); + private final HttpClient client; + private final OpenAIConnector openAIConnector = new OpenAIConnector(); + + public WhatsAppNotifier() { + client = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build(); + } + + public void sendNotifications() { + ArrayList reminders = TippligaSQLConnector.getInstance().getNextWhatsAppReminders(24); + reminders.addAll(TippligaSQLConnector.getInstance().getNextWhatsAppReminders(1)); + reminders.forEach(reminder -> { + boolean success = sendMessage(reminder); + if (success) { + markReminderAsSent(reminder); + } + }); + } + + public boolean sendMessage(WhatsAppReminder reminder) { + String message = openAIConnector.getReminderMessage(reminder); + JSONObject body = new JSONObject(); + body.put("number", reminder.phoneNumber().substring(2)); + body.put("message", message); + HttpRequest req = HttpRequest.newBuilder() + .uri(URI.create(this.host + (this.port != null ? ":" + this.port : "") + "/send")) + .headers("Content-Type", "application/json") + .headers("x-api-key", apiKey) + .POST(HttpRequest.BodyPublishers.ofString(body.toJSONString())) + .build(); + boolean success = false; + try { + client.send(req, HttpResponse.BodyHandlers.ofString()).body(); + success = true; + } catch (Exception e) { + logger.error("Failed to send WhatsApp message: " + e.getMessage()); + } + return success; + } + + public void markReminderAsSent(WhatsAppReminder reminder) { + TippligaSQLConnector.getInstance().markWhatsAppReminderAsSent(reminder); + } +} + diff --git a/src/main/java/de/jeyp91/whatsapp/WhatsAppReminder.java b/src/main/java/de/jeyp91/whatsapp/WhatsAppReminder.java new file mode 100644 index 0000000..0076d3a --- /dev/null +++ b/src/main/java/de/jeyp91/whatsapp/WhatsAppReminder.java @@ -0,0 +1,16 @@ +package de.jeyp91.whatsapp; + +public record WhatsAppReminder(int season, + int league, + String leagueName, + int matchday, + String matchdayName, + String deliveryDate, + String todayTomorrow, + String time, + int remainingHours, + int userId, + String username, + String phoneNumber, + int missingBets) { +}