From b95a8dfbba6cd6709308f0452d69f346a8a5c8d1 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 20:29:08 +0200 Subject: [PATCH 01/10] cookies file --- .../miner/browser/BrowserController.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java index 26ad7b5a..bc3eb167 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java @@ -14,6 +14,8 @@ import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Cookie; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; @@ -69,6 +71,12 @@ public void login() throws LoginException, IOException{ @Nullable private String askUserLogin(){ log.error("Not logged in, please input cookies"); + + var cookiesFile = Paths.get("/cookies.json"); + if(Files.exists(cookiesFile)){ + return Files.readString(cookiesFile); + } + try{ return CommonUtils.getUserInput("Provide your session cookies under JSON format (you can use an extension like Cookie-Editor): "); } From dbe7b6964d76cbab478df1d3b9111ce32446ecc4 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 20:41:54 +0200 Subject: [PATCH 02/10] Make it cleaner --- .../ROOT/examples/global-config-schema.json | 886 +++++++++--------- .../ROOT/examples/streamer-config-schema.json | 558 +++++------ .../passport/browser/BrowserPassportApi.java | 5 +- .../miner/browser/BrowserController.java | 18 +- .../config/login/BrowserConfiguration.java | 3 + .../factory/ConfigurationFactoryTest.java | 1 + .../config-with-more-customization.json | 3 +- 7 files changed, 745 insertions(+), 729 deletions(-) diff --git a/miner/docs/modules/ROOT/examples/global-config-schema.json b/miner/docs/modules/ROOT/examples/global-config-schema.json index 684960b5..4709a2f7 100644 --- a/miner/docs/modules/ROOT/examples/global-config-schema.json +++ b/miner/docs/modules/ROOT/examples/global-config-schema.json @@ -1,541 +1,541 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "Path": { - "type": "object" + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "$defs" : { + "Path" : { + "type" : "object" } }, - "type": "object", - "properties": { - "accounts": { - "description": "List of account configurations.", - "type": "array", - "items": { - "type": "object", - "properties": { - "analytics": { - "allOf": [ + "type" : "object", + "properties" : { + "accounts" : { + "description" : "List of account configurations.", + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "analytics" : { + "allOf" : [ { - "type": "object", - "properties": { - "database": { - "allOf": [ + "type" : "object", + "properties" : { + "database" : { + "allOf" : [ { - "type": "object", - "properties": { - "jdbcUrl": { - "type": "string", - "description": "JDBC connection URL." + "type" : "object", + "properties" : { + "jdbcUrl" : { + "type" : "string", + "description" : "JDBC connection URL." }, - "maxPoolSize": { - "type": "integer", - "description": "Maximum number of connections to the database. Default: 10" + "maxPoolSize" : { + "type" : "integer", + "description" : "Maximum number of connections to the database. Default: 10" }, - "password": { - "type": "string", - "description": "Database password." + "password" : { + "type" : "string", + "description" : "Database password." }, - "username": { - "type": "string", - "description": "Database username." + "username" : { + "type" : "string", + "description" : "Database username." } }, - "description": "Database configuration." + "description" : "Database configuration." }, { - "description": "Database settings." + "description" : "Database settings." } ] }, - "enabled": { - "type": "boolean", - "description": "Enable or disable data collection. Default: false" + "enabled" : { + "type" : "boolean", + "description" : "Enable or disable data collection. Default: false" }, - "recordChatsPredictions": { - "type": "boolean", - "description": "Record other chat members predictions. Default: false" + "recordChatsPredictions" : { + "type" : "boolean", + "description" : "Record other chat members predictions. Default: false" } }, - "description": "Analytics settings define a way to collect data on your twitch account as time passes." + "description" : "Analytics settings define a way to collect data on your twitch account as time passes." }, { - "description": "Analytics settings, recording account's evolution, bets, predictions." + "description" : "Analytics settings, recording account's evolution, bets, predictions." } ] }, - "chatMode": { - "allOf": [ + "chatMode" : { + "allOf" : [ { - "type": "string", - "enum": ["WS", "IRC"], - "description": "Way of joining Twitch's chat." + "type" : "string", + "enum" : ["WS", "IRC"], + "description" : "Way of joining Twitch's chat." }, { - "description": "Method used to join chat. Default: WS" + "description" : "Method used to join chat. Default: WS" } ] }, - "defaultStreamerSettings": { - "allOf": [ + "defaultStreamerSettings" : { + "allOf" : [ { - "type": "object", - "properties": { - "claimMoments": { - "type": "boolean", - "description": "Claim moments. Default: false" + "type" : "object", + "properties" : { + "claimMoments" : { + "type" : "boolean", + "description" : "Claim moments. Default: false" }, - "enabled": { - "type": "boolean", - "description": "Enable mining for this streamer. Default: true" + "enabled" : { + "type" : "boolean", + "description" : "Enable mining for this streamer. Default: true" }, - "followRaid": { - "type": "boolean", - "description": "Follow raids to get bonus points. Default: false" + "followRaid" : { + "type" : "boolean", + "description" : "Follow raids to get bonus points. Default: false" }, - "index": { - "type": "integer", - "description": "The streamer index. This value is used when streamers have the same score from the defined priorities, the one with the lowest index will be picked first. Default: 2147483647" + "index" : { + "type" : "integer", + "description" : "The streamer index. This value is used when streamers have the same score from the defined priorities, the one with the lowest index will be picked first. Default: 2147483647" }, - "joinChat": { - "type": "boolean", - "description": "Join chat. Default: false" + "joinChat" : { + "type" : "boolean", + "description" : "Join chat. Default: false" }, - "makePredictions": { - "type": "boolean", - "description": "Place predictions. Default: false" + "makePredictions" : { + "type" : "boolean", + "description" : "Place predictions. Default: false" }, - "participateCampaigns": { - "type": "boolean", - "description": "Participate in campaigns and claim rewards (drops). Default: false" + "participateCampaigns" : { + "type" : "boolean", + "description" : "Participate in campaigns and claim rewards (drops). Default: false" }, - "predictions": { - "allOf": [ + "predictions" : { + "allOf" : [ { - "type": "object", - "properties": { - "actions": { - "description": "Actions to perform before a bet is placed.", - "type": "array", - "items": { - "type": "object", - "description": "Ensure that the amount placed is lower than the top predictor.", - "properties": { - "type": { - "const": "stealth" + "type" : "object", + "properties" : { + "actions" : { + "description" : "Actions to perform before a bet is placed.", + "type" : "array", + "items" : { + "type" : "object", + "description" : "Ensure that the amount placed is lower than the top predictor.", + "properties" : { + "type" : { + "const" : "stealth" } }, - "required": ["type"] + "required" : ["type"] } }, - "amountCalculator": { - "anyOf": [ + "amountCalculator" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "amount": { - "type": "integer", - "description": "Amount to place." + "type" : "object", + "properties" : { + "amount" : { + "type" : "integer", + "description" : "Amount to place." } }, - "description": "Always bet the same amount." + "description" : "Always bet the same amount." }, { - "type": "object", - "properties": { - "type": { - "const": "constant" + "type" : "object", + "properties" : { + "type" : { + "const" : "constant" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" + "description" : "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "max": { - "type": "integer", - "description": "Maximum number of points." + "type" : "object", + "properties" : { + "max" : { + "type" : "integer", + "description" : "Maximum number of points." }, - "percentage": { - "type": "number", - "description": "Percentage of your owned points to place, as a decimal value, between 0 and 1." + "percentage" : { + "type" : "number", + "description" : "Percentage of your owned points to place, as a decimal value, between 0 and 1." } }, - "description": "Place a percentage of your points (with a limit)." + "description" : "Place a percentage of your points (with a limit)." }, { - "type": "object", - "properties": { - "type": { - "const": "percentage" + "type" : "object", + "properties" : { + "type" : { + "const" : "percentage" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" + "description" : "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" } ] }, - "delayCalculator": { - "anyOf": [ + "delayCalculator" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "seconds": { - "type": "integer", - "description": "Number of seconds before the end to place the bet." + "type" : "object", + "properties" : { + "seconds" : { + "type" : "integer", + "description" : "Number of seconds before the end to place the bet." } }, - "description": "Place the bet a certain amount of time before the end of the original prediction." + "description" : "Place the bet a certain amount of time before the end of the original prediction." }, { - "type": "object", - "properties": { - "type": { - "const": "fromEnd" + "type" : "object", + "properties" : { + "type" : { + "const" : "fromEnd" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate when to place the bet. Default: 10s from end" + "description" : "How to calculate when to place the bet. Default: 10s from end" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "seconds": { - "type": "integer", - "description": "Number of seconds after the start to place the bet." + "type" : "object", + "properties" : { + "seconds" : { + "type" : "integer", + "description" : "Number of seconds after the start to place the bet." } }, - "description": "Place the bet a certain amount of time after the beginning of the original prediction." + "description" : "Place the bet a certain amount of time after the beginning of the original prediction." }, { - "type": "object", - "properties": { - "type": { - "const": "fromStart" + "type" : "object", + "properties" : { + "type" : { + "const" : "fromStart" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate when to place the bet. Default: 10s from end" + "description" : "How to calculate when to place the bet. Default: 10s from end" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "percent": { - "type": "number", - "description": "The percentage of the timer, as a decimal value, between 0 and 1." + "type" : "object", + "properties" : { + "percent" : { + "type" : "number", + "description" : "The percentage of the timer, as a decimal value, between 0 and 1." } }, - "description": "Place the bet after 'percent'% of the original timer elapsed." + "description" : "Place the bet after 'percent'% of the original timer elapsed." }, { - "type": "object", - "properties": { - "type": { - "const": "percentage" + "type" : "object", + "properties" : { + "type" : { + "const" : "percentage" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate when to place the bet. Default: 10s from end" + "description" : "How to calculate when to place the bet. Default: 10s from end" } ] }, - "minimumPointsRequired": { - "type": "integer", - "description": "Minimum amount of points to have to place a bet. If this threshold is not reached, no bet is placed. Default: fromEnd(10)" + "minimumPointsRequired" : { + "type" : "integer", + "description" : "Minimum amount of points to have to place a bet. If this threshold is not reached, no bet is placed. Default: fromEnd(10)" }, - "outcomePicker": { - "anyOf": [ + "outcomePicker" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the most users.", - "properties": { - "type": { - "const": "mostUsers" + "type" : "object", + "description" : "Choose the outcome with the most users.", + "properties" : { + "type" : { + "const" : "mostUsers" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the least users.", - "properties": { - "type": { - "const": "leastUsers" + "type" : "object", + "description" : "Choose the outcome with the least users.", + "properties" : { + "type" : { + "const" : "leastUsers" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the most points. This is the same as 'the outcome with lower odds'.", - "properties": { - "type": { - "const": "mostPoints" + "type" : "object", + "description" : "Choose the outcome with the most points. This is the same as 'the outcome with lower odds'.", + "properties" : { + "type" : { + "const" : "mostPoints" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the least points. his is the same as 'the outcome with higher odds'.", - "properties": { - "type": { - "const": "leastPoints" + "type" : "object", + "description" : "Choose the outcome with the least points. his is the same as 'the outcome with higher odds'.", + "properties" : { + "type" : { + "const" : "leastPoints" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "percentageGap": { - "type": "number", - "description": "The percent gap of the user count, as decimal, between 0 and 1. (i.e. Setting this to 0.1, will mean that the condition switches states when the difference between sides is 10%, so 45% of the users on one side and 55% on the other)." + "type" : "object", + "properties" : { + "percentageGap" : { + "type" : "number", + "description" : "The percent gap of the user count, as decimal, between 0 and 1. (i.e. Setting this to 0.1, will mean that the condition switches states when the difference between sides is 10%, so 45% of the users on one side and 55% on the other)." } }, - "description": "Choose the outcome with the most users. However, if the two most picked outcomes have a user count similar, choose the outcome with the least points (higher odds)." + "description" : "Choose the outcome with the most users. However, if the two most picked outcomes have a user count similar, choose the outcome with the least points (higher odds)." }, { - "type": "object", - "properties": { - "type": { - "const": "smart" + "type" : "object", + "properties" : { + "type" : { + "const" : "smart" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the biggest predictor.", - "properties": { - "type": { - "const": "biggestPredictor" + "type" : "object", + "description" : "Choose the outcome with the biggest predictor.", + "properties" : { + "type" : { + "const" : "biggestPredictor" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "minTotalBetsPlacedByUser": { - "type": "integer", - "description": "Only user with at least this number of bets are considered in the calculation. Default: 5" + "type" : "object", + "properties" : { + "minTotalBetsPlacedByUser" : { + "type" : "integer", + "description" : "Only user with at least this number of bets are considered in the calculation. Default: 5" }, - "minTotalBetsPlacedOnOutcome": { - "type": "integer", - "description": "Need at least x bets placed the chosen outcome. Default: 5" + "minTotalBetsPlacedOnOutcome" : { + "type" : "integer", + "description" : "Need at least x bets placed the chosen outcome. Default: 5" }, - "minTotalBetsPlacedOnPrediction": { - "type": "integer", - "description": "Need at least x bets placed to bet on this prediction. Default: 10" + "minTotalBetsPlacedOnPrediction" : { + "type" : "integer", + "description" : "Need at least x bets placed to bet on this prediction. Default: 10" } }, - "description": "Choose the outcome that's backed by other users with the highest average return-on-investment. Requires analytics to be enabled and recordChatsPredictions to be activated." + "description" : "Choose the outcome that's backed by other users with the highest average return-on-investment. Requires analytics to be enabled and recordChatsPredictions to be activated." }, { - "type": "object", - "properties": { - "type": { - "const": "mostTrusted" + "type" : "object", + "properties" : { + "type" : { + "const" : "mostTrusted" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] } }, - "description": "Prediction settings" + "description" : "Prediction settings" }, { - "description": "Prediction settings." + "description" : "Prediction settings." } ] }, - "priorities": { - "description": "A list of conditions that, if met, will prioritize this streamer.", - "type": "array", - "items": { - "anyOf": [ + "priorities" : { + "description" : "A list of conditions that, if met, will prioritize this streamer.", + "type" : "array", + "items" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." } }, - "description": "Adds a constant value to the score of the streamer." + "description" : "Adds a constant value to the score of the streamer." }, { - "type": "object", - "properties": { - "type": { - "const": "constant" + "type" : "object", + "properties" : { + "type" : { + "const" : "constant" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." }, - "score2": { - "type": "integer", - "description": "Score for a T2 sub." + "score2" : { + "type" : "integer", + "description" : "Score for a T2 sub." }, - "score3": { - "type": "integer", - "description": "Score for a T3 sub." + "score3" : { + "type" : "integer", + "description" : "Score for a T3 sub." } }, - "description": "Return a score if the logged-in user is subscribed to the streamer." + "description" : "Return a score if the logged-in user is subscribed to the streamer." }, { - "type": "object", - "properties": { - "type": { - "const": "subscribed" + "type" : "object", + "properties" : { + "type" : { + "const" : "subscribed" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." }, - "threshold": { - "type": "integer", - "description": "Current points must strictly be above this value to give the score." + "threshold" : { + "type" : "integer", + "description" : "Current points must strictly be above this value to give the score." } }, - "description": "Return a score if owned points are above a defined value." + "description" : "Return a score if owned points are above a defined value." }, { - "type": "object", - "properties": { - "type": { - "const": "pointsAbove" + "type" : "object", + "properties" : { + "type" : { + "const" : "pointsAbove" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." }, - "threshold": { - "type": "integer", - "description": "Current points must strictly be below this value to give the score." + "threshold" : { + "type" : "integer", + "description" : "Current points must strictly be below this value to give the score." } }, - "description": "Return a score if owned points are below a defined value." + "description" : "Return a score if owned points are below a defined value." }, { - "type": "object", - "properties": { - "type": { - "const": "pointsBelow" + "type" : "object", + "properties" : { + "type" : { + "const" : "pointsBelow" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." } }, - "description": "Return a score if the streamer has a potential watch streak to claim." + "description" : "Return a score if the streamer has a potential watch streak to claim." }, { - "type": "object", - "properties": { - "type": { - "const": "watchStreak" + "type" : "object", + "properties" : { + "type" : { + "const" : "watchStreak" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." } }, - "description": "Return a score if a drop campaign may be progressed by watching this stream." + "description" : "Return a score if a drop campaign may be progressed by watching this stream." }, { - "type": "object", - "properties": { - "type": { - "const": "drops" + "type" : "object", + "properties" : { + "type" : { + "const" : "drops" } }, - "required": ["type"] + "required" : ["type"] } ] } @@ -543,187 +543,187 @@ } } }, - "description": "Streamer settings" + "description" : "Streamer settings" }, { - "description": "Default streamer settings." + "description" : "Default streamer settings." } ] }, - "discord": { - "type": "object", - "properties": { - "embeds": { - "type": "boolean", - "description": "Use embeds in the messages or not. Default: false" + "discord" : { + "type" : "object", + "properties" : { + "embeds" : { + "type" : "boolean", + "description" : "Use embeds in the messages or not. Default: false" }, - "webhookUrl": { - "type": "object", - "properties": { - "authority": { - "type": "string" + "webhookUrl" : { + "type" : "object", + "properties" : { + "authority" : { + "type" : "string" }, - "file": { - "type": "string" + "file" : { + "type" : "string" }, - "host": { - "type": "string" + "host" : { + "type" : "string" }, - "port": { - "type": "integer" + "port" : { + "type" : "integer" }, - "protocol": { - "type": "string" + "protocol" : { + "type" : "string" }, - "ref": { - "type": "string" + "ref" : { + "type" : "string" } }, - "description": "Discord webhook url to publish events to." + "description" : "Discord webhook url to publish events to." } }, - "description": "Discord settings to send notifications." + "description" : "Discord settings to send notifications." }, - "enabled": { - "type": "boolean", - "description": "If the account is marked as enabled it'll be mined. Default: true" + "enabled" : { + "type" : "boolean", + "description" : "If the account is marked as enabled it'll be mined. Default: true" }, - "loadFollows": { - "type": "boolean", - "description": "Load streamers to scrape from follow list. Default: false" + "loadFollows" : { + "type" : "boolean", + "description" : "Load streamers to scrape from follow list. Default: false" }, - "loginMethod": { - "anyOf": [ + "loginMethod" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "authenticationFolder": { - "$ref": "#/$defs/Path", - "description": "Path to a folder that contains authentication files used to log back in after a restart. Default: ./authentication" + "type" : "object", + "properties" : { + "authenticationFolder" : { + "$ref" : "#/$defs/Path", + "description" : "Path to a folder that contains authentication files used to log back in after a restart. Default: ./authentication" }, - "password": { - "type": "string", - "description": "Password of your Twitch account." + "password" : { + "type" : "string", + "description" : "Password of your Twitch account." }, - "use2FA": { - "type": "boolean", - "description": "If this account uses 2FA set this to true to directly ask for it. Default: false" + "use2FA" : { + "type" : "boolean", + "description" : "If this account uses 2FA set this to true to directly ask for it. Default: false" } }, - "description": "Login though Twitch's Passport API." + "description" : "Login though Twitch's Passport API." }, { - "type": "object", - "properties": { - "type": { - "const": "http" + "type" : "object", + "properties" : { + "type" : { + "const" : "http" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "Login method to use." + "description" : "Login method to use." }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "binary": { - "type": "string", - "description": "Binary of the browser to use. Used only if not using a REMOTE_XXX driver." + "type" : "object", + "properties" : { + "binary" : { + "type" : "string", + "description" : "Binary of the browser to use. Used only if not using a REMOTE_XXX driver." }, - "disableShm": { - "type": "boolean", - "description": "Disable SHM usage. Default: false" + "disableShm" : { + "type" : "boolean", + "description" : "Disable SHM usage. Default: false" }, - "driver": { - "allOf": [ + "driver" : { + "allOf" : [ { - "type": "string", - "enum": ["CHROME", "FIREFOX", "REMOTE_CHROME", "REMOTE_FIREFOX"], - "description": "Selenium driver to use." + "type" : "string", + "enum" : ["CHROME", "FIREFOX", "REMOTE_CHROME", "REMOTE_FIREFOX"], + "description" : "Selenium driver to use." }, { - "description": "Driver to use. Default: CHROME" + "description" : "Driver to use. Default: CHROME" } ] }, - "headless": { - "type": "boolean", - "description": "Run browser headless. Not recommended. Default: false" + "headless" : { + "type" : "boolean", + "description" : "Run browser headless. Not recommended. Default: false" }, - "remoteHost": { - "type": "string", - "description": "Remote host of the selenium grid. Must be defined if using REMOTE_XXX driver." + "remoteHost" : { + "type" : "string", + "description" : "Remote host of the selenium grid. Must be defined if using REMOTE_XXX driver." }, - "screenshots": { - "type": "boolean", - "description": "Take screenshots on error. Default: false" + "screenshots" : { + "type" : "boolean", + "description" : "Take screenshots on error. Default: false" }, - "userAgent": { - "type": "string", - "description": "User-Agent to use. Default: use controlled browser User-Agent" + "userAgent" : { + "type" : "string", + "description" : "User-Agent to use. Default: use controlled browser User-Agent" }, - "userDir": { - "type": "string", - "description": "User directory to use. Should be a different one per account used to mine." + "userDir" : { + "type" : "string", + "description" : "User directory to use. Should be a different one per account used to mine." } }, - "description": "Login though controlled browser (selenium)." + "description" : "Login though controlled browser (selenium)." }, { - "type": "object", - "properties": { - "type": { - "const": "browser" + "type" : "object", + "properties" : { + "type" : { + "const" : "browser" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "Login method to use." + "description" : "Login method to use." } ] }, - "reloadEvery": { - "type": "integer", - "description": "Reload streamer settings every x minutes. Zero or negative value disables it. Default: 0" + "reloadEvery" : { + "type" : "integer", + "description" : "Reload streamer settings every x minutes. Zero or negative value disables it. Default: 0" }, - "streamerConfigDirectories": { - "description": "Paths containing overrides for streamer configurations.", - "type": "array", - "items": { - "type": "object", - "properties": { - "path": { - "$ref": "#/$defs/Path", - "description": "Path to a folder that contains streamer configurations." + "streamerConfigDirectories" : { + "description" : "Paths containing overrides for streamer configurations.", + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "path" : { + "$ref" : "#/$defs/Path", + "description" : "Path to a folder that contains streamer configurations." }, - "recursive": { - "type": "boolean", - "description": "If set to true, this folder will be scanned recursively. Default: false" + "recursive" : { + "type" : "boolean", + "description" : "If set to true, this folder will be scanned recursively. Default: false" } }, - "description": "Folder used to override streamer configurations." + "description" : "Folder used to override streamer configurations." } }, - "username": { - "type": "string", - "description": "Mining account's username." + "username" : { + "type" : "string", + "description" : "Mining account's username." }, - "versionProvider": { - "allOf": [ + "versionProvider" : { + "allOf" : [ { - "type": "string", - "enum": ["MANIFEST", "WEBPAGE"], - "description": "Way to get current Twitch's version." + "type" : "string", + "enum" : ["MANIFEST", "WEBPAGE"], + "description" : "Way to get current Twitch's version." }, { - "description": "Method used to get twitch version. Default: WEBPAGE" + "description" : "Method used to get twitch version. Default: WEBPAGE" } ] } }, - "description": "Mining account configuration." + "description" : "Mining account configuration." } } }, - "description": "Global configuration." + "description" : "Global configuration." } \ No newline at end of file diff --git a/miner/docs/modules/ROOT/examples/streamer-config-schema.json b/miner/docs/modules/ROOT/examples/streamer-config-schema.json index b5ab89d5..5f284517 100644 --- a/miner/docs/modules/ROOT/examples/streamer-config-schema.json +++ b/miner/docs/modules/ROOT/examples/streamer-config-schema.json @@ -1,466 +1,466 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "claimMoments": { - "type": "boolean", - "description": "Claim moments. Default: false" + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "object", + "properties" : { + "claimMoments" : { + "type" : "boolean", + "description" : "Claim moments. Default: false" }, - "enabled": { - "type": "boolean", - "description": "Enable mining for this streamer. Default: true" + "enabled" : { + "type" : "boolean", + "description" : "Enable mining for this streamer. Default: true" }, - "followRaid": { - "type": "boolean", - "description": "Follow raids to get bonus points. Default: false" + "followRaid" : { + "type" : "boolean", + "description" : "Follow raids to get bonus points. Default: false" }, - "index": { - "type": "integer", - "description": "The streamer index. This value is used when streamers have the same score from the defined priorities, the one with the lowest index will be picked first. Default: 2147483647" + "index" : { + "type" : "integer", + "description" : "The streamer index. This value is used when streamers have the same score from the defined priorities, the one with the lowest index will be picked first. Default: 2147483647" }, - "joinChat": { - "type": "boolean", - "description": "Join chat. Default: false" + "joinChat" : { + "type" : "boolean", + "description" : "Join chat. Default: false" }, - "makePredictions": { - "type": "boolean", - "description": "Place predictions. Default: false" + "makePredictions" : { + "type" : "boolean", + "description" : "Place predictions. Default: false" }, - "participateCampaigns": { - "type": "boolean", - "description": "Participate in campaigns and claim rewards (drops). Default: false" + "participateCampaigns" : { + "type" : "boolean", + "description" : "Participate in campaigns and claim rewards (drops). Default: false" }, - "predictions": { - "allOf": [ + "predictions" : { + "allOf" : [ { - "type": "object", - "properties": { - "actions": { - "description": "Actions to perform before a bet is placed.", - "type": "array", - "items": { - "type": "object", - "description": "Ensure that the amount placed is lower than the top predictor.", - "properties": { - "type": { - "const": "stealth" + "type" : "object", + "properties" : { + "actions" : { + "description" : "Actions to perform before a bet is placed.", + "type" : "array", + "items" : { + "type" : "object", + "description" : "Ensure that the amount placed is lower than the top predictor.", + "properties" : { + "type" : { + "const" : "stealth" } }, - "required": ["type"] + "required" : ["type"] } }, - "amountCalculator": { - "anyOf": [ + "amountCalculator" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "amount": { - "type": "integer", - "description": "Amount to place." + "type" : "object", + "properties" : { + "amount" : { + "type" : "integer", + "description" : "Amount to place." } }, - "description": "Always bet the same amount." + "description" : "Always bet the same amount." }, { - "type": "object", - "properties": { - "type": { - "const": "constant" + "type" : "object", + "properties" : { + "type" : { + "const" : "constant" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" + "description" : "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "max": { - "type": "integer", - "description": "Maximum number of points." + "type" : "object", + "properties" : { + "max" : { + "type" : "integer", + "description" : "Maximum number of points." }, - "percentage": { - "type": "number", - "description": "Percentage of your owned points to place, as a decimal value, between 0 and 1." + "percentage" : { + "type" : "number", + "description" : "Percentage of your owned points to place, as a decimal value, between 0 and 1." } }, - "description": "Place a percentage of your points (with a limit)." + "description" : "Place a percentage of your points (with a limit)." }, { - "type": "object", - "properties": { - "type": { - "const": "percentage" + "type" : "object", + "properties" : { + "type" : { + "const" : "percentage" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" + "description" : "How to calculate the amount to the bet. Default: percentage(percentage: 20, max: 50000)" } ] }, - "delayCalculator": { - "anyOf": [ + "delayCalculator" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "seconds": { - "type": "integer", - "description": "Number of seconds before the end to place the bet." + "type" : "object", + "properties" : { + "seconds" : { + "type" : "integer", + "description" : "Number of seconds before the end to place the bet." } }, - "description": "Place the bet a certain amount of time before the end of the original prediction." + "description" : "Place the bet a certain amount of time before the end of the original prediction." }, { - "type": "object", - "properties": { - "type": { - "const": "fromEnd" + "type" : "object", + "properties" : { + "type" : { + "const" : "fromEnd" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate when to place the bet. Default: 10s from end" + "description" : "How to calculate when to place the bet. Default: 10s from end" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "seconds": { - "type": "integer", - "description": "Number of seconds after the start to place the bet." + "type" : "object", + "properties" : { + "seconds" : { + "type" : "integer", + "description" : "Number of seconds after the start to place the bet." } }, - "description": "Place the bet a certain amount of time after the beginning of the original prediction." + "description" : "Place the bet a certain amount of time after the beginning of the original prediction." }, { - "type": "object", - "properties": { - "type": { - "const": "fromStart" + "type" : "object", + "properties" : { + "type" : { + "const" : "fromStart" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate when to place the bet. Default: 10s from end" + "description" : "How to calculate when to place the bet. Default: 10s from end" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "percent": { - "type": "number", - "description": "The percentage of the timer, as a decimal value, between 0 and 1." + "type" : "object", + "properties" : { + "percent" : { + "type" : "number", + "description" : "The percentage of the timer, as a decimal value, between 0 and 1." } }, - "description": "Place the bet after 'percent'% of the original timer elapsed." + "description" : "Place the bet after 'percent'% of the original timer elapsed." }, { - "type": "object", - "properties": { - "type": { - "const": "percentage" + "type" : "object", + "properties" : { + "type" : { + "const" : "percentage" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to calculate when to place the bet. Default: 10s from end" + "description" : "How to calculate when to place the bet. Default: 10s from end" } ] }, - "minimumPointsRequired": { - "type": "integer", - "description": "Minimum amount of points to have to place a bet. If this threshold is not reached, no bet is placed. Default: fromEnd(10)" + "minimumPointsRequired" : { + "type" : "integer", + "description" : "Minimum amount of points to have to place a bet. If this threshold is not reached, no bet is placed. Default: fromEnd(10)" }, - "outcomePicker": { - "anyOf": [ + "outcomePicker" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the most users.", - "properties": { - "type": { - "const": "mostUsers" + "type" : "object", + "description" : "Choose the outcome with the most users.", + "properties" : { + "type" : { + "const" : "mostUsers" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the least users.", - "properties": { - "type": { - "const": "leastUsers" + "type" : "object", + "description" : "Choose the outcome with the least users.", + "properties" : { + "type" : { + "const" : "leastUsers" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the most points. This is the same as 'the outcome with lower odds'.", - "properties": { - "type": { - "const": "mostPoints" + "type" : "object", + "description" : "Choose the outcome with the most points. This is the same as 'the outcome with lower odds'.", + "properties" : { + "type" : { + "const" : "mostPoints" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the least points. his is the same as 'the outcome with higher odds'.", - "properties": { - "type": { - "const": "leastPoints" + "type" : "object", + "description" : "Choose the outcome with the least points. his is the same as 'the outcome with higher odds'.", + "properties" : { + "type" : { + "const" : "leastPoints" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "percentageGap": { - "type": "number", - "description": "The percent gap of the user count, as decimal, between 0 and 1. (i.e. Setting this to 0.1, will mean that the condition switches states when the difference between sides is 10%, so 45% of the users on one side and 55% on the other)." + "type" : "object", + "properties" : { + "percentageGap" : { + "type" : "number", + "description" : "The percent gap of the user count, as decimal, between 0 and 1. (i.e. Setting this to 0.1, will mean that the condition switches states when the difference between sides is 10%, so 45% of the users on one side and 55% on the other)." } }, - "description": "Choose the outcome with the most users. However, if the two most picked outcomes have a user count similar, choose the outcome with the least points (higher odds)." + "description" : "Choose the outcome with the most users. However, if the two most picked outcomes have a user count similar, choose the outcome with the least points (higher odds)." }, { - "type": "object", - "properties": { - "type": { - "const": "smart" + "type" : "object", + "properties" : { + "type" : { + "const" : "smart" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "description": "Choose the outcome with the biggest predictor.", - "properties": { - "type": { - "const": "biggestPredictor" + "type" : "object", + "description" : "Choose the outcome with the biggest predictor.", + "properties" : { + "type" : { + "const" : "biggestPredictor" } }, - "required": ["type"] + "required" : ["type"] }, { - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "minTotalBetsPlacedByUser": { - "type": "integer", - "description": "Only user with at least this number of bets are considered in the calculation. Default: 5" + "type" : "object", + "properties" : { + "minTotalBetsPlacedByUser" : { + "type" : "integer", + "description" : "Only user with at least this number of bets are considered in the calculation. Default: 5" }, - "minTotalBetsPlacedOnOutcome": { - "type": "integer", - "description": "Need at least x bets placed the chosen outcome. Default: 5" + "minTotalBetsPlacedOnOutcome" : { + "type" : "integer", + "description" : "Need at least x bets placed the chosen outcome. Default: 5" }, - "minTotalBetsPlacedOnPrediction": { - "type": "integer", - "description": "Need at least x bets placed to bet on this prediction. Default: 10" + "minTotalBetsPlacedOnPrediction" : { + "type" : "integer", + "description" : "Need at least x bets placed to bet on this prediction. Default: 10" } }, - "description": "Choose the outcome that's backed by other users with the highest average return-on-investment. Requires analytics to be enabled and recordChatsPredictions to be activated." + "description" : "Choose the outcome that's backed by other users with the highest average return-on-investment. Requires analytics to be enabled and recordChatsPredictions to be activated." }, { - "type": "object", - "properties": { - "type": { - "const": "mostTrusted" + "type" : "object", + "properties" : { + "type" : { + "const" : "mostTrusted" } }, - "required": ["type"] + "required" : ["type"] } ], - "description": "How to choose what outcome to place the bet on. Default: smart(0.2)" + "description" : "How to choose what outcome to place the bet on. Default: smart(0.2)" } ] } }, - "description": "Prediction settings" + "description" : "Prediction settings" }, { - "description": "Prediction settings." + "description" : "Prediction settings." } ] }, - "priorities": { - "description": "A list of conditions that, if met, will prioritize this streamer.", - "type": "array", - "items": { - "anyOf": [ + "priorities" : { + "description" : "A list of conditions that, if met, will prioritize this streamer.", + "type" : "array", + "items" : { + "anyOf" : [ { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." } }, - "description": "Adds a constant value to the score of the streamer." + "description" : "Adds a constant value to the score of the streamer." }, { - "type": "object", - "properties": { - "type": { - "const": "constant" + "type" : "object", + "properties" : { + "type" : { + "const" : "constant" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." }, - "score2": { - "type": "integer", - "description": "Score for a T2 sub." + "score2" : { + "type" : "integer", + "description" : "Score for a T2 sub." }, - "score3": { - "type": "integer", - "description": "Score for a T3 sub." + "score3" : { + "type" : "integer", + "description" : "Score for a T3 sub." } }, - "description": "Return a score if the logged-in user is subscribed to the streamer." + "description" : "Return a score if the logged-in user is subscribed to the streamer." }, { - "type": "object", - "properties": { - "type": { - "const": "subscribed" + "type" : "object", + "properties" : { + "type" : { + "const" : "subscribed" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." }, - "threshold": { - "type": "integer", - "description": "Current points must strictly be above this value to give the score." + "threshold" : { + "type" : "integer", + "description" : "Current points must strictly be above this value to give the score." } }, - "description": "Return a score if owned points are above a defined value." + "description" : "Return a score if owned points are above a defined value." }, { - "type": "object", - "properties": { - "type": { - "const": "pointsAbove" + "type" : "object", + "properties" : { + "type" : { + "const" : "pointsAbove" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." }, - "threshold": { - "type": "integer", - "description": "Current points must strictly be below this value to give the score." + "threshold" : { + "type" : "integer", + "description" : "Current points must strictly be below this value to give the score." } }, - "description": "Return a score if owned points are below a defined value." + "description" : "Return a score if owned points are below a defined value." }, { - "type": "object", - "properties": { - "type": { - "const": "pointsBelow" + "type" : "object", + "properties" : { + "type" : { + "const" : "pointsBelow" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." } }, - "description": "Return a score if the streamer has a potential watch streak to claim." + "description" : "Return a score if the streamer has a potential watch streak to claim." }, { - "type": "object", - "properties": { - "type": { - "const": "watchStreak" + "type" : "object", + "properties" : { + "type" : { + "const" : "watchStreak" } }, - "required": ["type"] + "required" : ["type"] } ] }, { - "allOf": [ + "allOf" : [ { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Score to give." + "type" : "object", + "properties" : { + "score" : { + "type" : "integer", + "description" : "Score to give." } }, - "description": "Return a score if a drop campaign may be progressed by watching this stream." + "description" : "Return a score if a drop campaign may be progressed by watching this stream." }, { - "type": "object", - "properties": { - "type": { - "const": "drops" + "type" : "object", + "properties" : { + "type" : { + "const" : "drops" } }, - "required": ["type"] + "required" : ["type"] } ] } @@ -468,5 +468,5 @@ } } }, - "description": "Streamer settings" + "description" : "Streamer settings" } \ No newline at end of file diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApi.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApi.java index 2af1ff21..8ab0fcdb 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApi.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApi.java @@ -11,6 +11,7 @@ import org.openqa.selenium.Cookie; import org.openqa.selenium.WebDriver; import java.io.IOException; +import java.nio.file.Paths; import java.util.Optional; @Log4j2 @@ -24,7 +25,9 @@ public TwitchLogin login() throws LoginException{ log.info("Logging in"); try(var browser = BrowserFactory.createBrowser(browserConfiguration)){ var controller = browser.setup(); - controller.login(); + var cookiesPath = Optional.ofNullable(browserConfiguration.getCookiesPath()).map(Paths::get).orElse(null); + + controller.login(cookiesPath); return extractPassportInfo(browser.getDriver().manage()); } catch(IOException e){ diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java index bc3eb167..dd2ae98c 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/BrowserController.java @@ -13,9 +13,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Cookie; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; @@ -44,6 +45,10 @@ private void openTurboPage(){ } public void login() throws LoginException, IOException{ + login(null); + } + + public void login(@Nullable Path cookiesPath) throws LoginException, IOException{ log.info("Logging in"); openTurboPage(); @@ -53,7 +58,7 @@ public void login() throws LoginException, IOException{ } var manager = driver.getWebDriver().manage(); - var userInput = askUserLogin(); + var userInput = askUserLogin(cookiesPath); if(Objects.nonNull(userInput)){ JacksonUtils.read(userInput, new TypeReference>(){}) .stream() @@ -69,11 +74,14 @@ public void login() throws LoginException, IOException{ @SneakyThrows @Nullable - private String askUserLogin(){ + private String askUserLogin(@Nullable Path cookiesFile){ log.error("Not logged in, please input cookies"); - var cookiesFile = Paths.get("/cookies.json"); - if(Files.exists(cookiesFile)){ + if(Objects.nonNull(cookiesFile)){ + log.info("User defined cookies file, using it"); + if(!Files.exists(cookiesFile)){ + throw new FileNotFoundException("File does not exist: " + cookiesFile.toAbsolutePath()); + } return Files.readString(cookiesFile); } diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java index 3622214e..4dc3b518 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java @@ -51,4 +51,7 @@ public class BrowserConfiguration implements ILoginMethod{ @JsonProperty("remoteHost") @JsonPropertyDescription("Remote host of the selenium grid. Must be defined if using REMOTE_XXX driver.") private String remoteHost; + @JsonProperty("remoteHost") + @JsonPropertyDescription("Path to a JSON file containing your exported cookies.") + private String cookiesPath; } diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/ConfigurationFactoryTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/ConfigurationFactoryTest.java index 851ca261..a59463e5 100644 --- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/ConfigurationFactoryTest.java +++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/ConfigurationFactoryTest.java @@ -96,6 +96,7 @@ void getInstanceOverridden() throws MalformedURLException{ .driver(BrowserDriver.REMOTE_CHROME) .remoteHost("http://selenium-hub:4444/wd/hub") .userDir("/home/seluser/profiles/channelpointsminer") + .cookiesPath("/path/to/cookies.json") .build()) .loadFollows(true) .enabled(false) diff --git a/miner/src/test/resources/config/config-with-more-customization.json b/miner/src/test/resources/config/config-with-more-customization.json index a3413960..91285520 100644 --- a/miner/src/test/resources/config/config-with-more-customization.json +++ b/miner/src/test/resources/config/config-with-more-customization.json @@ -6,7 +6,8 @@ "type" : "browser", "driver" : "REMOTE_CHROME", "remoteHost" : "http://selenium-hub:4444/wd/hub", - "userDir" : "/home/seluser/profiles/channelpointsminer" + "userDir" : "/home/seluser/profiles/channelpointsminer", + "cookiesPath" : "/path/to/cookies.json" }, "loadFollows" : true, "enabled" : false, From f6c0e004e019958dd0fae5305027951df9d7eb6a Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 20:52:02 +0200 Subject: [PATCH 03/10] Fix property name --- miner/docs/modules/ROOT/examples/global-config-schema.json | 4 ++++ .../miner/config/login/BrowserConfiguration.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/miner/docs/modules/ROOT/examples/global-config-schema.json b/miner/docs/modules/ROOT/examples/global-config-schema.json index 4709a2f7..7b38a703 100644 --- a/miner/docs/modules/ROOT/examples/global-config-schema.json +++ b/miner/docs/modules/ROOT/examples/global-config-schema.json @@ -632,6 +632,10 @@ "type" : "string", "description" : "Binary of the browser to use. Used only if not using a REMOTE_XXX driver." }, + "cookiesPath" : { + "type" : "string", + "description" : "Path to a JSON file containing your exported cookies." + }, "disableShm" : { "type" : "boolean", "description" : "Disable SHM usage. Default: false" diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java index 4dc3b518..2176d8b8 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/config/login/BrowserConfiguration.java @@ -51,7 +51,7 @@ public class BrowserConfiguration implements ILoginMethod{ @JsonProperty("remoteHost") @JsonPropertyDescription("Remote host of the selenium grid. Must be defined if using REMOTE_XXX driver.") private String remoteHost; - @JsonProperty("remoteHost") + @JsonProperty("cookiesPath") @JsonPropertyDescription("Path to a JSON file containing your exported cookies.") private String cookiesPath; } From 9fd889b148985908fe154febc45ea2619de88cd3 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 20:58:21 +0200 Subject: [PATCH 04/10] Fix tests --- .../browser/BrowserPassportApiTest.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApiTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApiTest.java index 5b543c1f..16026394 100644 --- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApiTest.java +++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/api/passport/browser/BrowserPassportApiTest.java @@ -15,6 +15,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; +import java.nio.file.Paths; import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -22,6 +23,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -74,6 +76,24 @@ void loginIsExtracted() throws LoginException{ } } + @Test + void loginIsExtractedWithCookies() throws LoginException, IOException{ + try(var browserFactory = mockStatic(BrowserFactory.class)){ + browserFactory.when(() -> BrowserFactory.createBrowser(browserConfiguration)).thenReturn(browser); + + var pathStr = "/path/to/cookies.json"; + when(browserConfiguration.getCookiesPath()).thenReturn(pathStr); + + assertThat(tested.login()).isEqualTo(TwitchLogin.builder() + .username(USERNAME) + .accessToken(ACCESS_TOKEN) + .cookies(COOKIES) + .build()); + + verify(browserController).login(Paths.get(pathStr)); + } + } + @Test void exceptionBecauseNoLogin(){ try(var browserFactory = mockStatic(BrowserFactory.class)){ @@ -101,7 +121,7 @@ void onExceptionLoggingIn() throws LoginException, IOException{ try(var browserFactory = mockStatic(BrowserFactory.class)){ browserFactory.when(() -> BrowserFactory.createBrowser(browserConfiguration)).thenReturn(browser); - doThrow(new LoginException("For tests")).when(browserController).login(); + doThrow(new LoginException("For tests")).when(browserController).login(null); assertThrows(LoginException.class, tested::login); } @@ -114,7 +134,7 @@ void onExceptionCreatingBrowser(){ when(browser.setup()).thenThrow(new RuntimeException("For tests")); - assertThrows(RuntimeException.class, () -> tested.login()); + assertThrows(RuntimeException.class, tested::login); } } } \ No newline at end of file From 03c3feae115c0190db612672581965f2398ae7fa Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:39:27 +0200 Subject: [PATCH 05/10] Deserialize sameSite --- .../miner/browser/CookieData.java | 2 ++ .../util/json/CookieSameSiteDeserializer.java | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java index e11ed450..fb89ec69 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import fr.raksrinana.channelpointsminer.miner.util.json.CookieSameSiteDeserializer; import fr.raksrinana.channelpointsminer.miner.util.json.SecondsTimestampDeserializer; import lombok.AllArgsConstructor; import lombok.Builder; @@ -39,6 +40,7 @@ public class CookieData{ @JsonProperty("path") private String path; @JsonProperty("sameSite") + @JsonDeserialize(using = CookieSameSiteDeserializer.class) private String sameSite; @JsonProperty("secure") private boolean secure; diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java new file mode 100644 index 00000000..3af2dacb --- /dev/null +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java @@ -0,0 +1,29 @@ +package fr.raksrinana.channelpointsminer.miner.util.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; +import java.util.Objects; + +public class CookieSameSiteDeserializer extends StdDeserializer{ + public CookieSameSiteDeserializer(){ + this(null); + } + + protected CookieSameSiteDeserializer(Class vc){ + super(vc); + } + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{ + var value = p.getValueAsString(); + if(Objects.isNull(value) || value.isBlank()){ + return null; + } + if(Objects.equals("no_restriction", value)){ + return "None"; + } + return value; + } +} From 67c63f561a678415d647bff12399166a5a877f0d Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:49:12 +0200 Subject: [PATCH 06/10] tests --- .../json/CookieSameSiteDeserializerTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java new file mode 100644 index 00000000..e6ddd244 --- /dev/null +++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java @@ -0,0 +1,36 @@ +package fr.raksrinana.channelpointsminer.miner.util.json; + +import com.fasterxml.jackson.databind.JsonDeserializer; +import fr.raksrinana.channelpointsminer.miner.tests.ParallelizableTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; +import static org.assertj.core.api.Assertions.assertThat; + +@ParallelizableTest +class CookieSameSiteDeserializerTest extends DeserializerTest{ + private static Stream mappings(){ + return Stream.of( + Arguments.arguments("no_restriction", "None"), + Arguments.arguments("other", "other") + ); + } + + @ParameterizedTest + @MethodSource("mappings") + void noRestrictionValue(String value, String expected){ + assertThat(deserialize("\"%s\"".formatted(value))).isEqualTo(expected); + } + + @Test + void empty(){ + assertThat(deserialize("\"\"")).isNull(); + } + + @Override + protected JsonDeserializer getDeserializer(){ + return new CookieSameSiteDeserializer(); + } +} \ No newline at end of file From 95a79291f75310176d8a7c8699ca4ae3d86f3020 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 22:25:39 +0200 Subject: [PATCH 07/10] Lax values --- .../miner/util/json/CookieSameSiteDeserializer.java | 5 +---- .../miner/util/json/CookieSameSiteDeserializerTest.java | 8 ++++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java index 3af2dacb..d12b9f28 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializer.java @@ -18,12 +18,9 @@ protected CookieSameSiteDeserializer(Class vc){ @Override public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{ var value = p.getValueAsString(); - if(Objects.isNull(value) || value.isBlank()){ - return null; - } if(Objects.equals("no_restriction", value)){ return "None"; } - return value; + return "Lax"; } } diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java index e6ddd244..4d87ed9a 100644 --- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java +++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/util/json/CookieSameSiteDeserializerTest.java @@ -11,10 +11,14 @@ @ParallelizableTest class CookieSameSiteDeserializerTest extends DeserializerTest{ + + public static final String DEFAULT_VALUE = "Lax"; + private static Stream mappings(){ return Stream.of( Arguments.arguments("no_restriction", "None"), - Arguments.arguments("other", "other") + Arguments.arguments("unspecified", DEFAULT_VALUE), + Arguments.arguments("other", DEFAULT_VALUE) ); } @@ -26,7 +30,7 @@ void noRestrictionValue(String value, String expected){ @Test void empty(){ - assertThat(deserialize("\"\"")).isNull(); + assertThat(deserialize("\"\"")).isEqualTo(DEFAULT_VALUE); } @Override From 99086c1215ba4d2020581bd1bf74958a991fd640 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 22:30:14 +0200 Subject: [PATCH 08/10] Display logged in user instead of config value --- .../fr/raksrinana/channelpointsminer/miner/miner/Miner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java index 78906852..a05a9f83 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java @@ -314,7 +314,7 @@ public Collection getStreamers(){ @Override @NotNull public String getUsername(){ - return accountConfiguration.getUsername(); + return twitchLogin.getUsername(); } @Override From 22c2a389f4c45d813bd14096ca9acafd4c46f713 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 22:42:59 +0200 Subject: [PATCH 09/10] Check if logged account is correct --- .../channelpointsminer/miner/miner/Miner.java | 7 ++++++- .../miner/miner/MinerTest.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java index a05a9f83..52d0a482 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/miner/Miner.java @@ -156,6 +156,11 @@ private void login(){ var listenMessages = analyticsConfiguration.isEnabled() && analyticsConfiguration.isRecordChatsPredictions(); twitchLogin = passportApi.login(); + + if(!accountConfiguration.getUsername().equalsIgnoreCase(twitchLogin.getUsername())){ + throw new IllegalStateException("Failed to log in, expected account %s but was %s".formatted(accountConfiguration.getUsername(), twitchLogin.getUsername())); + } + var versionProvider = ApiFactory.createVersionProvider(accountConfiguration.getVersionProvider()); var integrityProvider = ApiFactory.createIntegrityProvider(twitchLogin, versionProvider, accountConfiguration.getLoginMethod()); gqlApi = ApiFactory.createGqlApi(twitchLogin, integrityProvider); @@ -314,7 +319,7 @@ public Collection getStreamers(){ @Override @NotNull public String getUsername(){ - return twitchLogin.getUsername(); + return accountConfiguration.getUsername(); } @Override diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/miner/MinerTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/miner/MinerTest.java index c74e1027..61591c5f 100644 --- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/miner/MinerTest.java +++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/miner/MinerTest.java @@ -74,6 +74,7 @@ @ParallelizableTest @ExtendWith(MockitoExtension.class) class MinerTest{ + private static final String USERNAME = "username"; private static final String STREAMER_USERNAME = "streamer-username"; private static final String STREAMER_ID = "streamer-id"; private static final String USER_ID = "user-id"; @@ -132,6 +133,7 @@ class MinerTest{ void setUp() throws LoginException, IOException{ tested = new Miner(accountConfiguration, passportApi, streamerSettingsFactory, webSocketPool, scheduledExecutorService, executorService, database); + lenient().when(accountConfiguration.getUsername()).thenReturn(USERNAME); lenient().when(accountConfiguration.getReloadEvery()).thenReturn(0); lenient().when(accountConfiguration.isLoadFollows()).thenReturn(false); lenient().when(accountConfiguration.getChatMode()).thenReturn(CHAT_MODE); @@ -145,6 +147,7 @@ void setUp() throws LoginException, IOException{ lenient().when(streamerSettings.isFollowRaid()).thenReturn(false); lenient().when(streamerSettings.isMakePredictions()).thenReturn(false); lenient().when(streamerSettings.isJoinIrc()).thenReturn(false); + lenient().when(twitchLogin.getUsername()).thenReturn(USERNAME); lenient().when(twitchLogin.fetchUserId(gqlApi)).thenReturn(USER_ID); lenient().when(twitchLogin.getAccessToken()).thenReturn(ACCESS_TOKEN); @@ -300,6 +303,19 @@ void setupIsDoneWithAnalyticsAndPredictionRecording() throws LoginException, IOE } } + @Test + void wrongAccountLoggedIn(){ + try(var apiFactory = mockStatic(ApiFactory.class)){ + apiFactory.when(() -> ApiFactory.createTwitchApi(twitchLogin)).thenReturn(twitchApi); + + when(twitchLogin.getUsername()).thenReturn("wrong"); + + var caught = assertThrows(IllegalStateException.class, () -> tested.start()); + + assertThat(caught).hasRootCauseMessage("Failed to log in, expected account username but was wrong"); + } + } + @Test void captchaLogin() throws LoginException, IOException{ when(passportApi.login()).thenThrow(new CaptchaSolveRequired(400, -1, "For tests")); From e08157f98a2b7efcc613a269509a2ed3d777d754 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com> Date: Mon, 10 Oct 2022 22:52:24 +0200 Subject: [PATCH 10/10] Schema cookies file --- .../global-config-cookies-schema.json | 42 +++++++++++++++++++ .../ROOT/pages/configuration/index.adoc | 9 ++++ .../miner/browser/CookieData.java | 4 +- .../miner/ConfigSchemaGenerator.java | 1 + .../miner/CookiesFileType.java | 7 ++++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 miner/docs/modules/ROOT/examples/global-config-cookies-schema.json create mode 100644 miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/CookiesFileType.java diff --git a/miner/docs/modules/ROOT/examples/global-config-cookies-schema.json b/miner/docs/modules/ROOT/examples/global-config-cookies-schema.json new file mode 100644 index 00000000..0c326a3a --- /dev/null +++ b/miner/docs/modules/ROOT/examples/global-config-cookies-schema.json @@ -0,0 +1,42 @@ +{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "domain" : { + "type" : "string" + }, + "expirationDate" : { + "type" : "string" + }, + "hostOnly" : { + "type" : "boolean" + }, + "httpOnly" : { + "type" : "boolean" + }, + "name" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "sameSite" : { + "type" : "string" + }, + "secure" : { + "type" : "boolean" + }, + "session" : { + "type" : "boolean" + }, + "storeId" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + } + } +} \ No newline at end of file diff --git a/miner/docs/modules/ROOT/pages/configuration/index.adoc b/miner/docs/modules/ROOT/pages/configuration/index.adoc index b177ecd0..2614eaeb 100644 --- a/miner/docs/modules/ROOT/pages/configuration/index.adoc +++ b/miner/docs/modules/ROOT/pages/configuration/index.adoc @@ -31,6 +31,15 @@ include::example$global-config-schema.json[] ---- ==== +.Cookies file JSON schema +[%collapsible] +==== +[source,json] +---- +include::example$global-config-cookies-schema.json[] +---- +==== + === Streamer configuration .Streamer configuration JSON schema diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java index fb89ec69..56aaeef5 100644 --- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java +++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/browser/CookieData.java @@ -35,7 +35,7 @@ public class CookieData{ private boolean hostOnly; @JsonProperty("httpOnly") private boolean httpOnly; - @JsonProperty("name") + @JsonProperty(value = "name", required = true) private String name; @JsonProperty("path") private String path; @@ -48,7 +48,7 @@ public class CookieData{ private boolean session; @JsonProperty("storeId") private String storeId; - @JsonProperty("value") + @JsonProperty(value = "value", required = true) private String value; @Nullable diff --git a/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/ConfigSchemaGenerator.java b/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/ConfigSchemaGenerator.java index ada532dd..6f3804bb 100644 --- a/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/ConfigSchemaGenerator.java +++ b/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/ConfigSchemaGenerator.java @@ -26,6 +26,7 @@ public static void main(String[] args) throws IOException{ var exampleFolder = Paths.get("miner/docs/modules/ROOT/examples"); generate(generator, Configuration.class, exampleFolder.resolve("global-config-schema.json")); + generate(generator, CookiesFileType.class, exampleFolder.resolve("global-config-cookies-schema.json")); generate(generator, StreamerSettings.class, exampleFolder.resolve("streamer-config-schema.json")); } diff --git a/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/CookiesFileType.java b/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/CookiesFileType.java new file mode 100644 index 00000000..a89799b3 --- /dev/null +++ b/miner/src/schema/java/fr/raksrinana/channelpointsminer/miner/CookiesFileType.java @@ -0,0 +1,7 @@ +package fr.raksrinana.channelpointsminer.miner; + +import fr.raksrinana.channelpointsminer.miner.browser.CookieData; +import java.util.Collection; + +public abstract class CookiesFileType implements Collection{ +}