From 43144e884dc64ded2d010084f15eb68149d979f1 Mon Sep 17 00:00:00 2001 From: Fabrice Rochette Date: Thu, 4 Jun 2026 18:09:14 -0500 Subject: [PATCH] =?UTF-8?q?Add=20=C2=A4A=C2=A4=20action-result=20extension?= =?UTF-8?q?=20to=20the=20ustk=20response=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Responses may now carry an optional action result after a new USTK_ACTION_SEPARATOR ("¤A¤"): "¤A¤" where the token is (O0/O1/O2 = OK, C1/C2 = cancelled). This is what the micro applet appends after the REPLY_APPENDIX it echoes. - ResponseHelper.USTK_ACTION_SEPARATOR + buildMicroResponseString(...) build the full double-separator response terminated with ¤A¤ for applets that cannot wrap the value themselves. - parseUstkString peels the ¤A¤ token into UstkParams (lastActionId + result), then parses the base exactly as before; responses without ¤A¤ are unchanged. - UstkParams gains lastActionId + result (new UstkActionResult{OK,CANCEL}). - Bump 1.0.5 -> 1.0.6. Co-Authored-By: Claude Opus 4.8 --- pom.xml | 2 +- .../com/mobiera/ustk/util/ResponseHelper.java | 54 +++++++++++++++++-- .../mobiera/ustk/util/UstkActionResult.java | 12 +++++ .../com/mobiera/ustk/util/UstkParams.java | 22 +++++++- 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/mobiera/ustk/util/UstkActionResult.java diff --git a/pom.xml b/pom.xml index b0f643e..04ca137 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.mobiera.libs ustk-api jar - 1.0.5 + 1.0.6 Ustk API Ustk API https://github.com/mobiera/ustk-api diff --git a/src/main/java/com/mobiera/ustk/util/ResponseHelper.java b/src/main/java/com/mobiera/ustk/util/ResponseHelper.java index e553bb6..b469a77 100644 --- a/src/main/java/com/mobiera/ustk/util/ResponseHelper.java +++ b/src/main/java/com/mobiera/ustk/util/ResponseHelper.java @@ -52,25 +52,69 @@ public static String buildUstkString(String campaignId, String endpointId, Strin } } return retval; - + } - + + /** + * Separator that delimits an optional action result extension appended + * after the ustk response body, e.g. by the micro applet. The reply on the + * wire is {@code ¤A¤} where {@code } is + * {@code } (`O0`/`O1`/`O2` = OK, `C1`/`C2` = cancelled). + */ + public static String USTK_ACTION_SEPARATOR = "¤A¤"; + + /** + * Builds the response string an applet that cannot wrap the value itself + * (the micro applet) must echo back: a full double-separator ustk response + * ({@code ¤U¤¤U¤}) terminated with the action separator, so that the + * applet's 2-byte result token lands right after {@code ¤A¤}. Pass this as + * the REPLY_APPENDIX value. + */ + public static String buildMicroResponseString(String campaignId, String endpointId, String requestId, Long adId, Long campaignScheduleId, Boolean testing) { + return USTK_RESPONSE_SEPARATOR + buildUstkString(campaignId, endpointId, requestId, adId, campaignScheduleId, testing) + USTK_ACTION_SEPARATOR; + } + public static UstkParams parseUstkString(String message) { + if (message == null) return null; + + // Strip the optional action result extension "¤A¤" first, so the + // base (the ¤U¤¤U¤ ustk response) parses exactly as without it. + Integer lastActionId = null; + UstkActionResult result = null; + int actionIdx = message.indexOf(USTK_ACTION_SEPARATOR); + if (actionIdx >= 0) { + String token = message.substring(actionIdx + USTK_ACTION_SEPARATOR.length()); + message = message.substring(0, actionIdx); + if (token.length() >= 2) { + char r = token.charAt(0); + char d = token.charAt(1); + if ((r == 'O' || r == 'C') && (d >= '0' && d <= '9')) { + result = (r == 'C') ? UstkActionResult.CANCEL : UstkActionResult.OK; + lastActionId = d - '0'; + } + } + } + String value = getUstkValue(message); if (value == null) return null; String[] infos = value.split("-"); + UstkParams params = null; if (infos != null) { if (infos.length == 5) { - return UstkParams.build(infos[0], infos[1], infos[2], infos[3], infos[4], null); + params = UstkParams.build(infos[0], infos[1], infos[2], infos[3], infos[4], null); } else if (infos.length == 6) { boolean test = false; if (infos[4].equals("t")) { test = true; } - return UstkParams.build(infos[0], infos[1], infos[2], infos[3], infos[4], test); + params = UstkParams.build(infos[0], infos[1], infos[2], infos[3], infos[4], test); } } - return null; + if (params != null) { + params.setLastActionId(lastActionId); + params.setResult(result); + } + return params; } public static String SLEEPY_FLOW_SEPARATOR = "¤S¤"; diff --git a/src/main/java/com/mobiera/ustk/util/UstkActionResult.java b/src/main/java/com/mobiera/ustk/util/UstkActionResult.java new file mode 100644 index 0000000..ba2217b --- /dev/null +++ b/src/main/java/com/mobiera/ustk/util/UstkActionResult.java @@ -0,0 +1,12 @@ +package com.mobiera.ustk.util; + +/** + * Outcome of a ustk response that carries an action result extension (the + * {@code ¤A¤} separator), e.g. the micro applet reply. {@code OK} means the + * user accepted the action(s); {@code CANCEL} means the user cancelled / went + * back. + */ +public enum UstkActionResult { + OK, + CANCEL; +} diff --git a/src/main/java/com/mobiera/ustk/util/UstkParams.java b/src/main/java/com/mobiera/ustk/util/UstkParams.java index 00ee81e..ac9a982 100644 --- a/src/main/java/com/mobiera/ustk/util/UstkParams.java +++ b/src/main/java/com/mobiera/ustk/util/UstkParams.java @@ -7,7 +7,11 @@ public class UstkParams { String adId; String campaignScheduleId; Boolean testing; - + /* Action result extension (¤A¤ separator), e.g. micro applet replies. + * Null when the response carries no action result (SAT/PICO). */ + Integer lastActionId; + UstkActionResult result; + public static UstkParams build(String campaignId, String enpointId, String requestId, String adId, String campaignScheduleId, Boolean testing) { // ¤U¤¤U¤58129251--60568407-85353303-85453443-n @@ -146,4 +150,20 @@ public String getCampaignScheduleId() { public void setCampaignScheduleId(String campaignScheduleId) { this.campaignScheduleId = campaignScheduleId; } + + public Integer getLastActionId() { + return lastActionId; + } + + public void setLastActionId(Integer lastActionId) { + this.lastActionId = lastActionId; + } + + public UstkActionResult getResult() { + return result; + } + + public void setResult(UstkActionResult result) { + this.result = result; + } }