diff --git a/aas.py b/aas.py index 4a3997c..311a24c 100644 --- a/aas.py +++ b/aas.py @@ -46,11 +46,22 @@ class ArisuAutoSweeper(AzurLaneAutoScript): from tasks.tactical_challenge.tactical_challenge import TacticalChallenge TacticalChallenge(config=self.config, device=self.device).run() + def task(self): + from tasks.task.task import Task + Task(config=self.config, device=self.device).run() + + def shop(self): + from tasks.shop.shop import Shop + Shop(config=self.config, device=self.device).run() + + def momotalk(self): + from tasks.momotalk.momotalk import MomoTalk + MomoTalk(config=self.config, device=self.device).run() + def data_update(self): from tasks.item.data_update import DataUpdate DataUpdate(config=self.config, device=self.device).run() - if __name__ == '__main__': aas = ArisuAutoSweeper('aas') aas.loop() diff --git a/assets/en/momotalk/BEGIN_STORY.png b/assets/en/momotalk/BEGIN_STORY.png new file mode 100644 index 0000000..6d43b89 Binary files /dev/null and b/assets/en/momotalk/BEGIN_STORY.png differ diff --git a/assets/en/momotalk/CHAT_AREA.png b/assets/en/momotalk/CHAT_AREA.png new file mode 100644 index 0000000..ed61a8e Binary files /dev/null and b/assets/en/momotalk/CHAT_AREA.png differ diff --git a/assets/en/momotalk/CONFIRM_SKIP.png b/assets/en/momotalk/CONFIRM_SKIP.png new file mode 100644 index 0000000..1da4357 Binary files /dev/null and b/assets/en/momotalk/CONFIRM_SKIP.png differ diff --git a/assets/en/momotalk/CONFIRM_SORT.png b/assets/en/momotalk/CONFIRM_SORT.png new file mode 100644 index 0000000..e34d453 Binary files /dev/null and b/assets/en/momotalk/CONFIRM_SORT.png differ diff --git a/assets/en/momotalk/FIRST_UNREAD.png b/assets/en/momotalk/FIRST_UNREAD.png new file mode 100644 index 0000000..deb3791 Binary files /dev/null and b/assets/en/momotalk/FIRST_UNREAD.png differ diff --git a/assets/en/momotalk/MENU.png b/assets/en/momotalk/MENU.png new file mode 100644 index 0000000..ee8bc4e Binary files /dev/null and b/assets/en/momotalk/MENU.png differ diff --git a/assets/en/momotalk/MESSAGE_OFF.png b/assets/en/momotalk/MESSAGE_OFF.png new file mode 100644 index 0000000..45f9158 Binary files /dev/null and b/assets/en/momotalk/MESSAGE_OFF.png differ diff --git a/assets/en/momotalk/MESSAGE_ON.png b/assets/en/momotalk/MESSAGE_ON.png new file mode 100644 index 0000000..3ecc259 Binary files /dev/null and b/assets/en/momotalk/MESSAGE_ON.png differ diff --git a/assets/en/momotalk/NOTIFICATION_BADGE.png b/assets/en/momotalk/NOTIFICATION_BADGE.png new file mode 100644 index 0000000..0753e80 Binary files /dev/null and b/assets/en/momotalk/NOTIFICATION_BADGE.png differ diff --git a/assets/en/momotalk/REPLY.png b/assets/en/momotalk/REPLY.png new file mode 100644 index 0000000..7ba2246 Binary files /dev/null and b/assets/en/momotalk/REPLY.png differ diff --git a/assets/en/momotalk/SELECT_STUDENT.png b/assets/en/momotalk/SELECT_STUDENT.png new file mode 100644 index 0000000..8dcc592 Binary files /dev/null and b/assets/en/momotalk/SELECT_STUDENT.png differ diff --git a/assets/en/momotalk/SKIP.png b/assets/en/momotalk/SKIP.png new file mode 100644 index 0000000..b46c299 Binary files /dev/null and b/assets/en/momotalk/SKIP.png differ diff --git a/assets/en/momotalk/SORT_OFF.png b/assets/en/momotalk/SORT_OFF.png new file mode 100644 index 0000000..bd1203a Binary files /dev/null and b/assets/en/momotalk/SORT_OFF.png differ diff --git a/assets/en/momotalk/SORT_ON.png b/assets/en/momotalk/SORT_ON.png new file mode 100644 index 0000000..4d917da Binary files /dev/null and b/assets/en/momotalk/SORT_ON.png differ diff --git a/assets/en/momotalk/STORY.png b/assets/en/momotalk/STORY.png new file mode 100644 index 0000000..b9b8a50 Binary files /dev/null and b/assets/en/momotalk/STORY.png differ diff --git a/assets/en/momotalk/UNREAD.png b/assets/en/momotalk/UNREAD.png new file mode 100644 index 0000000..091294e Binary files /dev/null and b/assets/en/momotalk/UNREAD.png differ diff --git a/assets/en/momotalk/UNREAD_OFF.png b/assets/en/momotalk/UNREAD_OFF.png new file mode 100644 index 0000000..1038f12 Binary files /dev/null and b/assets/en/momotalk/UNREAD_OFF.png differ diff --git a/assets/en/momotalk/UNREAD_ON.png b/assets/en/momotalk/UNREAD_ON.png new file mode 100644 index 0000000..c8a43d0 Binary files /dev/null and b/assets/en/momotalk/UNREAD_ON.png differ diff --git a/assets/en/shop/CONFIRM_PURCHASE.BUTTON.png b/assets/en/shop/CONFIRM_PURCHASE.BUTTON.png new file mode 100644 index 0000000..131d1d2 Binary files /dev/null and b/assets/en/shop/CONFIRM_PURCHASE.BUTTON.png differ diff --git a/assets/en/shop/CONFIRM_PURCHASE.png b/assets/en/shop/CONFIRM_PURCHASE.png new file mode 100644 index 0000000..c81e0b9 Binary files /dev/null and b/assets/en/shop/CONFIRM_PURCHASE.png differ diff --git a/assets/en/shop/CONFIRM_REFRESH.BUTTON.png b/assets/en/shop/CONFIRM_REFRESH.BUTTON.png new file mode 100644 index 0000000..695912e Binary files /dev/null and b/assets/en/shop/CONFIRM_REFRESH.BUTTON.png differ diff --git a/assets/en/shop/CONFIRM_REFRESH.png b/assets/en/shop/CONFIRM_REFRESH.png new file mode 100644 index 0000000..ec30fc4 Binary files /dev/null and b/assets/en/shop/CONFIRM_REFRESH.png differ diff --git a/assets/en/shop/ITEM_LIST.png b/assets/en/shop/ITEM_LIST.png new file mode 100644 index 0000000..791f09c Binary files /dev/null and b/assets/en/shop/ITEM_LIST.png differ diff --git a/assets/en/shop/NORMAL_OFF.png b/assets/en/shop/NORMAL_OFF.png new file mode 100644 index 0000000..bb8b179 Binary files /dev/null and b/assets/en/shop/NORMAL_OFF.png differ diff --git a/assets/en/shop/NORMAL_ON.png b/assets/en/shop/NORMAL_ON.png new file mode 100644 index 0000000..d2a453a Binary files /dev/null and b/assets/en/shop/NORMAL_ON.png differ diff --git a/assets/en/shop/OCR_REFRESH.png b/assets/en/shop/OCR_REFRESH.png new file mode 100644 index 0000000..eb095d0 Binary files /dev/null and b/assets/en/shop/OCR_REFRESH.png differ diff --git a/assets/en/shop/PURCHASE.png b/assets/en/shop/PURCHASE.png new file mode 100644 index 0000000..ea6cffe Binary files /dev/null and b/assets/en/shop/PURCHASE.png differ diff --git a/assets/en/shop/REFRESH.png b/assets/en/shop/REFRESH.png new file mode 100644 index 0000000..d49e512 Binary files /dev/null and b/assets/en/shop/REFRESH.png differ diff --git a/assets/en/shop/TC_OFF.png b/assets/en/shop/TC_OFF.png new file mode 100644 index 0000000..2cf1d34 Binary files /dev/null and b/assets/en/shop/TC_OFF.png differ diff --git a/assets/en/shop/TC_ON.png b/assets/en/shop/TC_ON.png new file mode 100644 index 0000000..16f9d8a Binary files /dev/null and b/assets/en/shop/TC_ON.png differ diff --git a/assets/en/task/CLAIM.png b/assets/en/task/CLAIM.png new file mode 100644 index 0000000..c0cb101 Binary files /dev/null and b/assets/en/task/CLAIM.png differ diff --git a/assets/en/task/CLAIM_ALL.png b/assets/en/task/CLAIM_ALL.png new file mode 100644 index 0000000..f5a69a4 Binary files /dev/null and b/assets/en/task/CLAIM_ALL.png differ diff --git a/config/template.json b/config/template.json index a41942a..887e481 100644 --- a/config/template.json +++ b/config/template.json @@ -33,6 +33,22 @@ "ServerUpdate": "04:00" } }, + "DataUpdate": { + "Scheduler": { + "Enable": true, + "NextRun": "2020-01-01 00:00:00", + "Command": "DataUpdate", + "ServerUpdate": "04:00" + }, + "ItemStorage": { + "AP": {}, + "Credit": {}, + "Pyroxene": {}, + "BountyTicket": {}, + "ScrimmageTicket": {}, + "TacticalChallengeTicket": {} + } + }, "Cafe": { "Scheduler": { "Enable": true, @@ -54,20 +70,55 @@ "Substitute": false } }, - "Circle": { + "Shop": { "Scheduler": { - "Enable": true, + "Enable": false, "NextRun": "2020-01-01 00:00:00", - "Command": "Circle", - "ServerUpdate": "04:00" - } - }, - "Mail": { - "Scheduler": { - "Enable": true, - "NextRun": "2020-01-01 00:00:00", - "Command": "Mail", + "Command": "Shop", "ServerUpdate": "04:00" + }, + "NormalShop": { + "Enable": false, + "Purchases": 1, + "1": false, + "2": false, + "3": false, + "4": false, + "5": false, + "6": false, + "7": false, + "8": false, + "9": false, + "10": false, + "11": false, + "12": false, + "13": false, + "14": false, + "15": false, + "16": false, + "17": false, + "18": false, + "19": false, + "20": false + }, + "TacticalChallengeShop": { + "Enable": false, + "Purchases": 1, + "1": false, + "2": false, + "3": false, + "4": false, + "5": false, + "6": false, + "7": false, + "8": false, + "9": false, + "10": false, + "11": false, + "12": false, + "13": false, + "14": false, + "15": false } }, "Bounty": { @@ -127,20 +178,36 @@ "PlayerSelect": 0 } }, - "DataUpdate": { + "Circle": { "Scheduler": { "Enable": true, "NextRun": "2020-01-01 00:00:00", - "Command": "DataUpdate", + "Command": "Circle", + "ServerUpdate": "04:00" + } + }, + "Task": { + "Scheduler": { + "Enable": false, + "NextRun": "2020-01-01 00:00:00", + "Command": "Task", + "ServerUpdate": "04:00" + } + }, + "Mail": { + "Scheduler": { + "Enable": true, + "NextRun": "2020-01-01 00:00:00", + "Command": "Mail", + "ServerUpdate": "04:00" + } + }, + "Momotalk": { + "Scheduler": { + "Enable": false, + "NextRun": "2020-01-01 00:00:00", + "Command": "Momotalk", "ServerUpdate": "04:00" - }, - "ItemStorage": { - "AP": {}, - "Credit": {}, - "Pyroxene": {}, - "BountyTicket": {}, - "ScrimmageTicket": {}, - "TacticalChallengeTicket": {} } } } \ No newline at end of file diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 85d4080..bb8d15d 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -162,6 +162,85 @@ } } }, + "DataUpdate": { + "Scheduler": { + "Enable": { + "type": "state", + "value": true, + "option": [ + true + ], + "option_bold": [ + true + ] + }, + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + }, + "Command": { + "type": "input", + "value": "DataUpdate", + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "04:00", + "display": "hide" + } + }, + "ItemStorage": { + "AP": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredAP", + "order": 1, + "color": "#62ea6e" + }, + "Credit": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredInt", + "order": 2, + "color": "#fdec00" + }, + "Pyroxene": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredInt", + "order": 3, + "color": "#21befc" + }, + "BountyTicket": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredBountyTicket", + "order": 4, + "color": "#94cb44" + }, + "ScrimmageTicket": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredScrimmageTicket", + "order": 5, + "color": "#f86c6a" + }, + "TacticalChallengeTicket": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredTacticalChallengeTicket", + "order": 6, + "color": "#7ac8e5" + } + } + }, "Cafe": { "Scheduler": { "Enable": { @@ -239,11 +318,11 @@ } } }, - "Circle": { + "Shop": { "Scheduler": { "Enable": { "type": "checkbox", - "value": true, + "value": false, "option": [ true, false @@ -256,7 +335,7 @@ }, "Command": { "type": "input", - "value": "Circle", + "value": "Shop", "display": "hide" }, "ServerUpdate": { @@ -264,32 +343,177 @@ "value": "04:00", "display": "hide" } - } - }, - "Mail": { - "Scheduler": { + }, + "NormalShop": { "Enable": { "type": "checkbox", - "value": true, + "value": false + }, + "Purchases": { + "type": "select", + "value": 1, "option": [ - true, - false + 1, + 2, + 3, + 4 ] }, - "NextRun": { - "type": "datetime", - "value": "2020-01-01 00:00:00", - "validate": "datetime" + "1": { + "type": "checkbox", + "value": false }, - "Command": { - "type": "input", - "value": "Mail", - "display": "hide" + "2": { + "type": "checkbox", + "value": false }, - "ServerUpdate": { - "type": "input", - "value": "04:00", - "display": "hide" + "3": { + "type": "checkbox", + "value": false + }, + "4": { + "type": "checkbox", + "value": false + }, + "5": { + "type": "checkbox", + "value": false + }, + "6": { + "type": "checkbox", + "value": false + }, + "7": { + "type": "checkbox", + "value": false + }, + "8": { + "type": "checkbox", + "value": false + }, + "9": { + "type": "checkbox", + "value": false + }, + "10": { + "type": "checkbox", + "value": false + }, + "11": { + "type": "checkbox", + "value": false + }, + "12": { + "type": "checkbox", + "value": false + }, + "13": { + "type": "checkbox", + "value": false + }, + "14": { + "type": "checkbox", + "value": false + }, + "15": { + "type": "checkbox", + "value": false + }, + "16": { + "type": "checkbox", + "value": false + }, + "17": { + "type": "checkbox", + "value": false + }, + "18": { + "type": "checkbox", + "value": false + }, + "19": { + "type": "checkbox", + "value": false + }, + "20": { + "type": "checkbox", + "value": false + } + }, + "TacticalChallengeShop": { + "Enable": { + "type": "checkbox", + "value": false + }, + "Purchases": { + "type": "select", + "value": 1, + "option": [ + 1, + 2, + 3, + 4 + ] + }, + "1": { + "type": "checkbox", + "value": false + }, + "2": { + "type": "checkbox", + "value": false + }, + "3": { + "type": "checkbox", + "value": false + }, + "4": { + "type": "checkbox", + "value": false + }, + "5": { + "type": "checkbox", + "value": false + }, + "6": { + "type": "checkbox", + "value": false + }, + "7": { + "type": "checkbox", + "value": false + }, + "8": { + "type": "checkbox", + "value": false + }, + "9": { + "type": "checkbox", + "value": false + }, + "10": { + "type": "checkbox", + "value": false + }, + "11": { + "type": "checkbox", + "value": false + }, + "12": { + "type": "checkbox", + "value": false + }, + "13": { + "type": "checkbox", + "value": false + }, + "14": { + "type": "checkbox", + "value": false + }, + "15": { + "type": "checkbox", + "value": false } } }, @@ -523,16 +747,14 @@ } } }, - "DataUpdate": { + "Circle": { "Scheduler": { "Enable": { - "type": "state", + "type": "checkbox", "value": true, "option": [ - true - ], - "option_bold": [ - true + true, + false ] }, "NextRun": { @@ -542,7 +764,7 @@ }, "Command": { "type": "input", - "value": "DataUpdate", + "value": "Circle", "display": "hide" }, "ServerUpdate": { @@ -550,55 +772,86 @@ "value": "04:00", "display": "hide" } - }, - "ItemStorage": { - "AP": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredAP", - "order": 1, - "color": "#62ea6e" + } + }, + "Task": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": false, + "option": [ + true, + false + ] }, - "Credit": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredInt", - "order": 2, - "color": "#fdec00" + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" }, - "Pyroxene": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredInt", - "order": 3, - "color": "#21befc" + "Command": { + "type": "input", + "value": "Task", + "display": "hide" }, - "BountyTicket": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredBountyTicket", - "order": 4, - "color": "#94cb44" + "ServerUpdate": { + "type": "input", + "value": "04:00", + "display": "hide" + } + } + }, + "Mail": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": true, + "option": [ + true, + false + ] }, - "ScrimmageTicket": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredScrimmageTicket", - "order": 5, - "color": "#f86c6a" + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" }, - "TacticalChallengeTicket": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredTacticalChallengeTicket", - "order": 6, - "color": "#7ac8e5" + "Command": { + "type": "input", + "value": "Mail", + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "04:00", + "display": "hide" + } + } + }, + "Momotalk": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": false, + "option": [ + true, + false + ] + }, + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + }, + "Command": { + "type": "input", + "value": "Momotalk", + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "04:00", + "display": "hide" } } } diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index a973c08..6ebb6c8 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -140,6 +140,53 @@ TacticalChallenge: value: 0 option: [ 0, 1, 2, 3 ] +NormalShop: + Enable: false + Purchases: + value: 1 + option: [ 1, 2, 3, 4 ] + "1": false + "2": false + "3": false + "4": false + "5": false + "6": false + "7": false + "8": false + "9": false + "10": false + "11": false + "12": false + "13": false + "14": false + "15": false + "16": false + "17": false + "18": false + "19": false + "20": false + +TacticalChallengeShop: + Enable: false + Purchases: + value: 1 + option: [ 1, 2, 3, 4 ] + "1": false + "2": false + "3": false + "4": false + "5": false + "6": false + "7": false + "8": false + "9": false + "10": false + "11": false + "12": false + "13": false + "14": false + "15": false + ItemStorage: AP: stored: StoredAP diff --git a/module/config/argument/menu.json b/module/config/argument/menu.json index 7ecbc17..e0e2e9a 100644 --- a/module/config/argument/menu.json +++ b/module/config/argument/menu.json @@ -4,20 +4,35 @@ "page": "setting", "tasks": [ "Alas", - "Restart" + "Restart", + "DataUpdate" ] }, "Daily": { - "menu": "list", + "menu": "collapse", "page": "setting", "tasks": [ "Cafe", - "Circle", - "Mail", + "Shop" + ] + }, + "Farm": { + "menu": "collapse", + "page": "setting", + "tasks": [ "Bounty", "Scrimmage", - "TacticalChallenge", - "DataUpdate" + "TacticalChallenge" + ] + }, + "Reward": { + "menu": "collapse", + "page": "setting", + "tasks": [ + "Circle", + "Task", + "Mail", + "Momotalk" ] } } \ No newline at end of file diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index b7f5839..33dc2ea 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -15,21 +15,31 @@ Alas: - Optimization Restart: - Scheduler + DataUpdate: + - Scheduler + - ItemStorage # ==================== Daily ==================== Daily: - menu: 'list' + menu: 'collapse' page: 'setting' tasks: Cafe: - Scheduler - Cafe - Invitation - Circle: - - Scheduler - Mail: + Shop: - Scheduler + - NormalShop + - TacticalChallengeShop + +# ==================== Farm ==================== + +Farm: + menu: 'collapse' + page: 'setting' + tasks: Bounty: - Scheduler - Bounty @@ -45,6 +55,18 @@ Daily: TacticalChallenge: - Scheduler - TacticalChallenge - DataUpdate: + +# ==================== Rewards ==================== + +Reward: + menu: 'collapse' + page: 'setting' + tasks: + Circle: - Scheduler - - ItemStorage \ No newline at end of file + Task: + - Scheduler + Mail: + - Scheduler + Momotalk: + - Scheduler \ No newline at end of file diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 0d0553f..9a66e6e 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -85,6 +85,49 @@ class GeneratedConfig: # Group `TacticalChallenge` TacticalChallenge_PlayerSelect = 0 # 0, 1, 2, 3 + # Group `NormalShop` + NormalShop_Enable = False + NormalShop_Purchases = 1 # 1, 2, 3, 4 + NormalShop_1 = False + NormalShop_2 = False + NormalShop_3 = False + NormalShop_4 = False + NormalShop_5 = False + NormalShop_6 = False + NormalShop_7 = False + NormalShop_8 = False + NormalShop_9 = False + NormalShop_10 = False + NormalShop_11 = False + NormalShop_12 = False + NormalShop_13 = False + NormalShop_14 = False + NormalShop_15 = False + NormalShop_16 = False + NormalShop_17 = False + NormalShop_18 = False + NormalShop_19 = False + NormalShop_20 = False + + # Group `TacticalChallengeShop` + TacticalChallengeShop_Enable = False + TacticalChallengeShop_Purchases = 1 # 1, 2, 3, 4 + TacticalChallengeShop_1 = False + TacticalChallengeShop_2 = False + TacticalChallengeShop_3 = False + TacticalChallengeShop_4 = False + TacticalChallengeShop_5 = False + TacticalChallengeShop_6 = False + TacticalChallengeShop_7 = False + TacticalChallengeShop_8 = False + TacticalChallengeShop_9 = False + TacticalChallengeShop_10 = False + TacticalChallengeShop_11 = False + TacticalChallengeShop_12 = False + TacticalChallengeShop_13 = False + TacticalChallengeShop_14 = False + TacticalChallengeShop_15 = False + # Group `ItemStorage` ItemStorage_AP = {} ItemStorage_Credit = {} diff --git a/module/config/config_manual.py b/module/config/config_manual.py index 71fbef3..d1fe26f 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -9,7 +9,7 @@ class ManualConfig: SCHEDULER_PRIORITY = """ Restart > Cafe > TacticalChallenge > Circle > Mail - > DataUpdate > Bounty > Scrimmage + > DataUpdate > Bounty > Scrimmage > Task > Shop > Momotalk """ """ diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 13c3fc5..258def7 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -7,6 +7,14 @@ "Daily": { "name": "Daily", "help": "" + }, + "Farm": { + "name": "Farm", + "help": "" + }, + "Reward": { + "name": "Reward", + "help": "" } }, "Task": { @@ -18,16 +26,16 @@ "name": "Error Handling", "help": "" }, + "DataUpdate": { + "name": "Dashboard Upd", + "help": "" + }, "Cafe": { "name": "Cafe", "help": "" }, - "Circle": { - "name": "Club", - "help": "" - }, - "Mail": { - "name": "Mailbox", + "Shop": { + "name": "Shop", "help": "" }, "Bounty": { @@ -42,8 +50,20 @@ "name": "Tactical Challenge", "help": "" }, - "DataUpdate": { - "name": "Dashboard Upd", + "Circle": { + "name": "Club", + "help": "" + }, + "Task": { + "name": "Tasks", + "help": "" + }, + "Mail": { + "name": "Mailbox", + "help": "" + }, + "Momotalk": { + "name": "MomoTalk", "help": "" } }, @@ -420,6 +440,182 @@ "3": "Third" } }, + "NormalShop": { + "_info": { + "name": "Normal Shop Settings", + "help": "" + }, + "Enable": { + "name": "Enable", + "help": "" + }, + "Purchases": { + "name": "Number of Purchases", + "help": "Default can be purchased once + number of refreshes = number of purchases, for example, 2 purchases = 1 default purchase + 1 refresh purchase", + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "1": { + "name": "1", + "help": "x5 Novice Activity Report - 12,500 Credits" + }, + "2": { + "name": "2", + "help": "x5 Normal Activity Report - 125,000 Credits" + }, + "3": { + "name": "3", + "help": "x3 Advanced Activity Report - 300,000 Credits" + }, + "4": { + "name": "4", + "help": "x1 Superior Activity Report - 500,000 Credits" + }, + "5": { + "name": "5", + "help": "x5 Lesser Enhancement Stone - 10,000 Credits" + }, + "6": { + "name": "6", + "help": "x5 Normal Enhancement Stone - 40,000 Credits" + }, + "7": { + "name": "7", + "help": "x3 Advanced Enhancement Stone - 96,000 Credits" + }, + "8": { + "name": "8", + "help": "x1 Superior Enhancement Stone - 128,000 Credits" + }, + "9": { + "name": "9", + "help": "x5 Lesser Enhancement Stone - 10,000 Credits" + }, + "10": { + "name": "10", + "help": "x5 Normal Enhancement Stone - 40,000 Credits" + }, + "11": { + "name": "11", + "help": "x3 Advanced Enhancement Stone - 96,000 Credits" + }, + "12": { + "name": "12", + "help": "x1 Superior Enhancement Stone - 128,000 Credits" + }, + "13": { + "name": "13", + "help": "x10 Lesser Enhancement Stone - 20,000 Credits" + }, + "14": { + "name": "14", + "help": "x10 Normal Enhancement Stone - 80,000 Credits" + }, + "15": { + "name": "15", + "help": "x6 Advanced Enhancement Stone - 192,000 Credits" + }, + "16": { + "name": "16", + "help": "x2 Superior Enhancement Stone - 256,000 Credits" + }, + "17": { + "name": "17", + "help": "x1 Random Selection - 8,000 Credits" + }, + "18": { + "name": "18", + "help": "x1 Random Selection - 8,000 Credits" + }, + "19": { + "name": "19", + "help": "x1 Random Selection - 25,000 Credits" + }, + "20": { + "name": "20", + "help": "x1 Random Selection - 25,000 Credits" + } + }, + "TacticalChallengeShop": { + "_info": { + "name": "Tactical Challenge Shop Settings", + "help": "" + }, + "Enable": { + "name": "Enable", + "help": "" + }, + "Purchases": { + "name": "Number of Purchases", + "help": "", + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "1": { + "name": "1", + "help": "x5 Shizuko's Eleph - 50 Coins" + }, + "2": { + "name": "2", + "help": "x5 Mashiro's Eleph - 50 Coins" + }, + "3": { + "name": "3", + "help": "x5 Saya's Eleph - 50 Coins" + }, + "4": { + "name": "4", + "help": "x5 Fuuka's Eleph - 50 Coins" + }, + "5": { + "name": "5", + "help": "x5 Utaha's Eleph - 50 Coins" + }, + "6": { + "name": "6", + "help": "x1 Lesser Drink(30 AP) - 15 Coins" + }, + "7": { + "name": "7", + "help": "x1 Normal Drink(60 AP) - 30 Coins" + }, + "8": { + "name": "8", + "help": "x10 Novice Activity Report - 5 Coins" + }, + "9": { + "name": "9", + "help": "x5 Normal Activity Report - 25 Coins" + }, + "10": { + "name": "10", + "help": "x3 Advanced Activity Report - 60 Coins" + }, + "11": { + "name": "11", + "help": "x1 Superior Activity Report - 100 Coins" + }, + "12": { + "name": "12", + "help": "x5000 Credits - 4 Coins" + }, + "13": { + "name": "13", + "help": "x25k Credits - 20 Coins" + }, + "14": { + "name": "14", + "help": "x75k Credits - 60 Coins" + }, + "15": { + "name": "15", + "help": "x125k Credits - 100 Coins" + } + }, "ItemStorage": { "_info": { "name": "ItemStorage._info.name", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 7592686..a561212 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -7,6 +7,14 @@ "Daily": { "name": "每日", "help": "" + }, + "Farm": { + "name": "Menu.Farm.name", + "help": "Menu.Farm.help" + }, + "Reward": { + "name": "Menu.Reward.name", + "help": "Menu.Reward.help" } }, "Task": { @@ -18,17 +26,17 @@ "name": "异常处理", "help": "" }, + "DataUpdate": { + "name": "仪表盘更新", + "help": "" + }, "Cafe": { "name": "咖啡厅", "help": "" }, - "Circle": { - "name": "公会", - "help": "社团 / 小组" - }, - "Mail": { - "name": "邮箱", - "help": "" + "Shop": { + "name": "Task.Shop.name", + "help": "Task.Shop.help" }, "Bounty": { "name": "悬赏通缉", @@ -42,9 +50,21 @@ "name": "战术对抗赛", "help": "战术大赛 / 竞技场" }, - "DataUpdate": { - "name": "仪表盘更新", + "Circle": { + "name": "公会", + "help": "社团 / 小组" + }, + "Task": { + "name": "Task.Task.name", + "help": "Task.Task.help" + }, + "Mail": { + "name": "邮箱", "help": "" + }, + "Momotalk": { + "name": "Task.Momotalk.name", + "help": "Task.Momotalk.help" } }, "Scheduler": { @@ -420,6 +440,182 @@ "3": "第三位" } }, + "NormalShop": { + "_info": { + "name": "NormalShop._info.name", + "help": "NormalShop._info.help" + }, + "Enable": { + "name": "NormalShop.Enable.name", + "help": "NormalShop.Enable.help" + }, + "Purchases": { + "name": "NormalShop.Purchases.name", + "help": "NormalShop.Purchases.help", + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "1": { + "name": "1", + "help": "" + }, + "2": { + "name": "2", + "help": "" + }, + "3": { + "name": "3", + "help": "" + }, + "4": { + "name": "4", + "help": "" + }, + "5": { + "name": "5", + "help": "" + }, + "6": { + "name": "6", + "help": "" + }, + "7": { + "name": "7", + "help": "" + }, + "8": { + "name": "8", + "help": "" + }, + "9": { + "name": "9", + "help": "" + }, + "10": { + "name": "10", + "help": "" + }, + "11": { + "name": "11", + "help": "" + }, + "12": { + "name": "12", + "help": "" + }, + "13": { + "name": "13", + "help": "" + }, + "14": { + "name": "14", + "help": "" + }, + "15": { + "name": "15", + "help": "" + }, + "16": { + "name": "16", + "help": "" + }, + "17": { + "name": "NormalShop.17.name", + "help": "NormalShop.17.help" + }, + "18": { + "name": "NormalShop.18.name", + "help": "NormalShop.18.help" + }, + "19": { + "name": "NormalShop.19.name", + "help": "NormalShop.19.help" + }, + "20": { + "name": "NormalShop.20.name", + "help": "NormalShop.20.help" + } + }, + "TacticalChallengeShop": { + "_info": { + "name": "TacticalChallengeShop._info.name", + "help": "TacticalChallengeShop._info.help" + }, + "Enable": { + "name": "TacticalChallengeShop.Enable.name", + "help": "TacticalChallengeShop.Enable.help" + }, + "Purchases": { + "name": "TacticalChallengeShop.Purchases.name", + "help": "TacticalChallengeShop.Purchases.help", + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "1": { + "name": "1", + "help": "" + }, + "2": { + "name": "2", + "help": "" + }, + "3": { + "name": "3", + "help": "" + }, + "4": { + "name": "4", + "help": "" + }, + "5": { + "name": "5", + "help": "" + }, + "6": { + "name": "6", + "help": "" + }, + "7": { + "name": "7", + "help": "" + }, + "8": { + "name": "8", + "help": "" + }, + "9": { + "name": "9", + "help": "" + }, + "10": { + "name": "10", + "help": "" + }, + "11": { + "name": "11", + "help": "" + }, + "12": { + "name": "12", + "help": "" + }, + "13": { + "name": "13", + "help": "" + }, + "14": { + "name": "14", + "help": "" + }, + "15": { + "name": "15", + "help": "" + } + }, "ItemStorage": { "_info": { "name": "ItemStorage._info.name", diff --git a/module/config/stored/stored_generated.py b/module/config/stored/stored_generated.py index 64feb48..4e991de 100644 --- a/module/config/stored/stored_generated.py +++ b/module/config/stored/stored_generated.py @@ -1,6 +1,10 @@ from module.config.stored.classes import ( StoredAP, + StoredBase, StoredBountyTicket, + StoredCounter, + StoredExpiredAt0400, + StoredExpiredAtMonday0400, StoredInt, StoredScrimmageTicket, StoredTacticalChallengeTicket, diff --git a/tasks/momotalk/assets/assets_momotalk.py b/tasks/momotalk/assets/assets_momotalk.py new file mode 100644 index 0000000..4fcb873 --- /dev/null +++ b/tasks/momotalk/assets/assets_momotalk.py @@ -0,0 +1,203 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +BEGIN_STORY = ButtonWrapper( + name='BEGIN_STORY', + jp=None, + en=Button( + file='./assets/en/momotalk/BEGIN_STORY.png', + area=(796, 540, 1059, 591), + search=(776, 520, 1079, 611), + color=(107, 197, 230), + button=(796, 540, 1059, 591), + ), +) +CHAT_AREA = ButtonWrapper( + name='CHAT_AREA', + jp=None, + en=Button( + file='./assets/en/momotalk/CHAT_AREA.png', + area=(760, 149, 1149, 628), + search=(740, 129, 1169, 648), + color=(206, 212, 217), + button=(760, 149, 1149, 628), + ), +) +CONFIRM_SKIP = ButtonWrapper( + name='CONFIRM_SKIP', + jp=None, + en=Button( + file='./assets/en/momotalk/CONFIRM_SKIP.png', + area=(674, 486, 871, 555), + search=(654, 466, 891, 575), + color=(112, 207, 241), + button=(674, 486, 871, 555), + ), +) +CONFIRM_SORT = ButtonWrapper( + name='CONFIRM_SORT', + jp=None, + en=Button( + file='./assets/en/momotalk/CONFIRM_SORT.png', + area=(239, 407, 657, 439), + search=(219, 387, 677, 459), + color=(248, 249, 249), + button=(239, 407, 657, 439), + ), +) +FIRST_UNREAD = ButtonWrapper( + name='FIRST_UNREAD', + jp=None, + en=Button( + file='./assets/en/momotalk/FIRST_UNREAD.png', + area=(636, 239, 661, 265), + search=(616, 219, 681, 285), + color=(251, 86, 45), + button=(636, 239, 661, 265), + ), +) +MENU = ButtonWrapper( + name='MENU', + jp=None, + en=Button( + file='./assets/en/momotalk/MENU.png', + area=(1156, 15, 1251, 63), + search=(1136, 0, 1271, 83), + color=(215, 219, 222), + button=(1156, 15, 1251, 63), + ), +) +MESSAGE_OFF = ButtonWrapper( + name='MESSAGE_OFF', + jp=None, + en=Button( + file='./assets/en/momotalk/MESSAGE_OFF.png', + area=(143, 273, 203, 298), + search=(123, 253, 223, 318), + color=(93, 105, 122), + button=(143, 273, 203, 298), + ), +) +MESSAGE_ON = ButtonWrapper( + name='MESSAGE_ON', + jp=None, + en=Button( + file='./assets/en/momotalk/MESSAGE_ON.png', + area=(143, 271, 203, 301), + search=(123, 251, 223, 321), + color=(160, 168, 182), + button=(143, 271, 203, 301), + ), +) +NOTIFICATION_BADGE = ButtonWrapper( + name='NOTIFICATION_BADGE', + jp=None, + en=Button( + file='./assets/en/momotalk/NOTIFICATION_BADGE.png', + area=(171, 109, 200, 128), + search=(151, 89, 220, 148), + color=(242, 101, 47), + button=(171, 109, 200, 128), + ), +) +REPLY = ButtonWrapper( + name='REPLY', + jp=None, + en=Button( + file='./assets/en/momotalk/REPLY.png', + area=(791, 431, 855, 462), + search=(771, 411, 875, 482), + color=(198, 210, 216), + button=(791, 431, 855, 462), + ), +) +SELECT_STUDENT = ButtonWrapper( + name='SELECT_STUDENT', + jp=None, + en=Button( + file='./assets/en/momotalk/SELECT_STUDENT.png', + area=(839, 369, 998, 403), + search=(819, 349, 1018, 423), + color=(241, 242, 242), + button=(839, 369, 998, 403), + ), +) +SKIP = ButtonWrapper( + name='SKIP', + jp=None, + en=Button( + file='./assets/en/momotalk/SKIP.png', + area=(1192, 103, 1229, 141), + search=(1172, 83, 1249, 161), + color=(108, 125, 145), + button=(1192, 103, 1229, 141), + ), +) +SORT_OFF = ButtonWrapper( + name='SORT_OFF', + jp=None, + en=Button( + file='./assets/en/momotalk/SORT_OFF.png', + area=(591, 158, 662, 199), + search=(571, 138, 682, 219), + color=(235, 237, 238), + button=(591, 158, 662, 199), + ), +) +SORT_ON = ButtonWrapper( + name='SORT_ON', + jp=None, + en=Button( + file='./assets/en/momotalk/SORT_ON.png', + area=(594, 159, 658, 196), + search=(574, 139, 678, 216), + color=(233, 235, 236), + button=(594, 159, 658, 196), + ), +) +STORY = ButtonWrapper( + name='STORY', + jp=None, + en=Button( + file='./assets/en/momotalk/STORY.png', + area=(790, 529, 979, 557), + search=(770, 509, 999, 577), + color=(220, 208, 214), + button=(790, 529, 979, 557), + ), +) +UNREAD = ButtonWrapper( + name='UNREAD', + jp=None, + en=Button( + file='./assets/en/momotalk/UNREAD.png', + area=(454, 160, 568, 193), + search=(434, 140, 588, 213), + color=(241, 242, 243), + button=(454, 160, 568, 193), + ), +) +UNREAD_OFF = ButtonWrapper( + name='UNREAD_OFF', + jp=None, + en=Button( + file='./assets/en/momotalk/UNREAD_OFF.png', + area=(456, 273, 658, 316), + search=(436, 253, 678, 336), + color=(252, 252, 251), + button=(456, 273, 658, 316), + ), +) +UNREAD_ON = ButtonWrapper( + name='UNREAD_ON', + jp=None, + en=Button( + file='./assets/en/momotalk/UNREAD_ON.png', + area=(456, 272, 658, 314), + search=(436, 252, 678, 334), + color=(245, 120, 144), + button=(456, 272, 658, 314), + ), +) diff --git a/tasks/momotalk/momotalk.py b/tasks/momotalk/momotalk.py new file mode 100644 index 0000000..35f4974 --- /dev/null +++ b/tasks/momotalk/momotalk.py @@ -0,0 +1,61 @@ +from enum import Enum + +from module.base.timer import Timer +from module.logger import logger +from tasks.tactical_challenge.assets.assets_tactical_challenge import * +from tasks.momotalk.ui import MomoTalkUI + +class MomoTalkStatus(Enum): + OPEN = 0 + SORT = 1 + CHECK = 2 + CHAT = 3 + STORY = 4 + FINISHED = -1 + +class MomoTalk(MomoTalkUI): + def handle_momotalk(self, status): + match status: + case MomoTalkStatus.OPEN: + if self.open_momotalk(): + return MomoTalkStatus.SORT + return MomoTalkStatus.FINISHED + case MomoTalkStatus.SORT: + if self.sort_messages(): + return MomoTalkStatus.CHECK + case MomoTalkStatus.CHECK: + if self.check_first_student(): + return MomoTalkStatus.CHAT + return MomoTalkStatus.FINISHED + case MomoTalkStatus.CHAT: + if self.chat(): + return MomoTalkStatus.STORY + return MomoTalkStatus.OPEN + case MomoTalkStatus.STORY: + if self.skip_story(): + return MomoTalkStatus.OPEN + case MomoTalkStatus.FINISHED: + return status + case _: + logger.warning(f'Invalid status: {status}') + return status + + def run(self): + action_timer = Timer(0.5, 1) + status = MomoTalkStatus.OPEN + + while 1: + self.device.screenshot() + + if self.ui_additional(): + continue + + if action_timer.reached_and_reset(): + logger.attr('Status', status) + status = self.handle_momotalk(status) + + if status == MomoTalkStatus.FINISHED: + break + + self.config.task_delay(server_update=True) + diff --git a/tasks/momotalk/ui.py b/tasks/momotalk/ui.py new file mode 100644 index 0000000..8ac7fd6 --- /dev/null +++ b/tasks/momotalk/ui.py @@ -0,0 +1,189 @@ +from module.base.timer import Timer +from module.base.base import ModuleBase +from module.logger import logger +from module.ui.switch import Switch +from module.base.utils import point_in_area, area_size +from tasks.base.ui import UI +from tasks.base.page import page_main, page_momo_talk +from tasks.momotalk.assets.assets_momotalk import * +import cv2 +import numpy as np + +"""None of the switches works""" +SWITCH_MESSAGE = Switch("Message_switch") +SWITCH_MESSAGE.add_state("on", MESSAGE_ON) +SWITCH_MESSAGE.add_state("off", MESSAGE_OFF) + +SWITCH_UNREAD = Switch("Unread_switch") +SWITCH_UNREAD.add_state("on", UNREAD_ON) +SWITCH_UNREAD.add_state("off", UNREAD_OFF) + +SWITCH_SORT = Switch("Sort_switch") +SWITCH_SORT.add_state("on", SORT_ON) +SWITCH_SORT.add_state("off", SORT_OFF) + +"""Required for template matching as reply and story +button can be found in different locations""" +REPLY_TEMPLATE = REPLY.matched_button.image +STORY_TEMPLATE = STORY.matched_button.image + +class MomoTalkUI(UI): + def __init__(self, config, device): + super().__init__(config, device) + self.swipe_vector_range = (0.65, 0.85) + self.list = CHAT_AREA + + def swipe_page(self, direction: str, main: ModuleBase, vector_range=None, reverse=False): + """ + Args: + direction: up, down + main: + vector_range (tuple[float, float]): + reverse (bool): + """ + if vector_range is None: + vector_range = self.swipe_vector_range + vector = np.random.uniform(*vector_range) + width, height = area_size(self.list.button) + if direction == 'up': + vector = (0, vector * height) + elif direction == 'down': + vector = (0, -vector * height) + else: + logger.warning(f'Unknown swipe direction: {direction}') + return + + if reverse: + vector = (-vector[0], -vector[1]) + main.device.swipe_vector(vector, self.list.button) + + def select_then_check(self, dest_enter: ButtonWrapper, dest_check: ButtonWrapper, similarity=0.85): + timer = Timer(5, 10).start() + while 1: + self.device.screenshot() + self.appear_then_click(dest_enter, interval=1, similarity=similarity) + if self.appear(dest_check, similarity=similarity): + return True + if timer.reached(): + return False + + def select_then_disappear(self, dest_enter: ButtonWrapper, dest_check: ButtonWrapper, force_select=False): + timer = Timer(5, 10).start() + while 1: + self.device.screenshot() + if force_select or self.appear(dest_enter): + self.click_with_interval(dest_enter, interval=1) + if not self.appear(dest_check): + return True + if timer.reached(): + return False + + def set_switch(self, switch): + """ + Set switch to on. However, unsure why is inaccurate in momotalk. + Returns: + True if switch is set, False if switch not found + """ + if not switch.appear(main=self): + logger.info(f'{switch.name} not found') + return False + switch.set('on', main=self) + + return True + + def click_all(self, template, x_add=0, y_add=0): + """ + Find the all the locations of the template adding an offset if specified and click them. + TODO: filter coords that are not inside the chat area as otherwise it will close momotalk. + If after filter, no coords then swipe. + """ + click_coords = self.device.click_methods.get(self.config.Emulator_ControlMethod, self.device.click_adb) + image = self.device.screenshot() + result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) + threshold = 0.8 + locations = np.where(result >= threshold) + seen = set() + for pt in zip(*locations[::-1]): + center_pt = (int(pt[0] + template.shape[1] / 2 + x_add), int(pt[1] + template.shape[0] / 2 + y_add)) + seen.add(center_pt) + if seen: + seen = filter(lambda x: point_in_area(x, CHAT_AREA.area), seen) + [click_coords(coords[0], coords[1]) for coords in seen] + self.swipe_page("down", self) + return True + return False + + def open_momotalk(self): + """ + Go to main and check if there are any red notifications in momotalk. + If yes, open it otherwise it means no student available for interaction. + """ + self.ui_ensure(page_main) + if self.match_color(NOTIFICATION_BADGE, threshold=80): + self.ui_ensure(page_momo_talk) + while not self.select_then_check(MESSAGE_OFF, MESSAGE_ON): + pass + return True + logger.warn("No students available for interaction") + return False + + def sort_messages(self): + """ + Switch from newest to unread and sort the messages in descending order + """ + logger.info("Sorting messages...") + steps = [UNREAD, CONFIRM_SORT, UNREAD_OFF, UNREAD_ON] + for i in range(len(steps)-2): + self.select_then_check(steps[i], steps[i+1], similarity=0.95) + return not self.appear(CONFIRM_SORT) and self.appear(UNREAD) and self.appear(SORT_ON) + + def check_first_student(self): + """ + If the first student has a red notification return True and start chat. + Otherwise it means no students are available for interaction. + """ + if self.match_color(FIRST_UNREAD, threshold=80) and self.select_then_disappear(FIRST_UNREAD, SELECT_STUDENT, force_select=True): + return True + logger.warn("No students available for interaction") + return False + + def chat(self): + """ + Waits for the chat area to be stable and then + check if a reply or story button is found and click them. + If the begin story button is found skip story. + """ + logger.info("Chatting with student...") + stability_counter = 0 + while 1: + self.wait_until_stable(CHAT_AREA, timer=Timer(10, 10)) + if self.appear(BEGIN_STORY): + logger.info("Begin Story detected") + return True + if self.click_all(REPLY_TEMPLATE, y_add=62): + logger.info("Clicked on reply") + stability_counter = 0 + continue + if self.click_all(STORY_TEMPLATE, y_add=62): + logger.info("Clicked on story") + stability_counter = 0 + continue + logger.info("No new message detected") + stability_counter += 1 + if stability_counter > 3: + return False + + def skip_story(self): + """ + Skip story by executing a series of steps. Returns True if the confirm skip + button is clicked and disappears + """ + logger.info("Attempting to skip story...") + steps = [BEGIN_STORY, MENU, SKIP] + for step in steps: + self.appear_then_click(step) + if self.appear_then_click(CONFIRM_SKIP) and not self.appear(CONFIRM_SKIP, interval=5): + logger.info("Skipped story successfully") + return True + return False + diff --git a/tasks/shop/assets/assets_shop.py b/tasks/shop/assets/assets_shop.py new file mode 100644 index 0000000..e06b0cb --- /dev/null +++ b/tasks/shop/assets/assets_shop.py @@ -0,0 +1,115 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +CONFIRM_PURCHASE = ButtonWrapper( + name='CONFIRM_PURCHASE', + jp=None, + en=Button( + file='./assets/en/shop/CONFIRM_PURCHASE.png', + area=(467, 231, 807, 309), + search=(447, 211, 827, 329), + color=(217, 218, 219), + button=(668, 458, 865, 514), + ), +) +CONFIRM_REFRESH = ButtonWrapper( + name='CONFIRM_REFRESH', + jp=None, + en=Button( + file='./assets/en/shop/CONFIRM_REFRESH.png', + area=(474, 271, 806, 306), + search=(454, 251, 826, 326), + color=(202, 203, 204), + button=(675, 434, 863, 500), + ), +) +ITEM_LIST = ButtonWrapper( + name='ITEM_LIST', + jp=None, + en=Button( + file='./assets/en/shop/ITEM_LIST.png', + area=(625, 127, 1244, 610), + search=(605, 107, 1264, 630), + color=(193, 206, 213), + button=(625, 127, 1244, 610), + ), +) +NORMAL_OFF = ButtonWrapper( + name='NORMAL_OFF', + jp=None, + en=Button( + file='./assets/en/shop/NORMAL_OFF.png', + area=(4, 111, 213, 167), + search=(0, 91, 233, 187), + color=(248, 248, 245), + button=(4, 111, 213, 167), + ), +) +NORMAL_ON = ButtonWrapper( + name='NORMAL_ON', + jp=None, + en=Button( + file='./assets/en/shop/NORMAL_ON.png', + area=(4, 109, 212, 170), + search=(0, 89, 232, 190), + color=(57, 78, 96), + button=(4, 109, 212, 170), + ), +) +OCR_REFRESH = ButtonWrapper( + name='OCR_REFRESH', + jp=None, + en=Button( + file='./assets/en/shop/OCR_REFRESH.png', + area=(712, 302, 762, 344), + search=(692, 282, 782, 364), + color=(225, 225, 226), + button=(712, 302, 762, 344), + ), +) +PURCHASE = ButtonWrapper( + name='PURCHASE', + jp=None, + en=Button( + file='./assets/en/shop/PURCHASE.png', + area=(1102, 640, 1227, 684), + search=(1082, 620, 1247, 704), + color=(226, 206, 65), + button=(1102, 640, 1227, 684), + ), +) +REFRESH = ButtonWrapper( + name='REFRESH', + jp=None, + en=Button( + file='./assets/en/shop/REFRESH.png', + area=(1098, 643, 1223, 682), + search=(1078, 623, 1243, 702), + color=(231, 234, 237), + button=(1098, 643, 1223, 682), + ), +) +TC_OFF = ButtonWrapper( + name='TC_OFF', + jp=None, + en=Button( + file='./assets/en/shop/TC_OFF.png', + area=(2, 503, 209, 558), + search=(0, 483, 229, 578), + color=(239, 242, 244), + button=(2, 503, 209, 558), + ), +) +TC_ON = ButtonWrapper( + name='TC_ON', + jp=None, + en=Button( + file='./assets/en/shop/TC_ON.png', + area=(3, 493, 208, 548), + search=(0, 473, 228, 568), + color=(62, 84, 99), + button=(3, 493, 208, 548), + ), +) diff --git a/tasks/shop/shop.py b/tasks/shop/shop.py new file mode 100644 index 0000000..8e6d132 --- /dev/null +++ b/tasks/shop/shop.py @@ -0,0 +1,118 @@ +from enum import Flag + +from module.base.timer import Timer +from module.exception import RequestHumanTakeover +from module.logger import logger +from module.ui.switch import Switch +from tasks.base.assets.assets_base_page import BACK +from tasks.base.page import page_main, page_shop +from tasks.shop.assets.assets_shop import * +from tasks.shop.ui import ShopUI + + +class ShopStatus(Flag): + SELECT_SHOP = 0 + SELECT_ITEMS = 1 + PURCHASE = 2 + REFRESH = 3 + END = 4 + FINISH = -1 + +class Shop(ShopUI): + @property + def shop_info(self): + """Similiar to bounty_info and scrimmage_info. + Returns a list with elements the select button, check button, how many times do make purchases and the list of items""" + info = [] + if self.config.NormalShop_Enable: + normal_config = self.config.cross_get(["Shop", "NormalShop"]) + normal_items = [num for num in range(1, 21) if normal_config[str(num)]] + if normal_items: + SWITCH_NORMAL = Switch('NormalShop_Switch') + SWITCH_NORMAL.add_state('on', NORMAL_ON) + SWITCH_NORMAL.add_state('off', NORMAL_OFF) + info.append([SWITCH_NORMAL, self.config.NormalShop_Purchases, normal_items]) + + if self.config.TacticalChallengeShop_Enable: + tc_config = self.config.cross_get(["Shop", "TacticalChallengeShop"]) + tc_items = [num for num in range(1, 16) if tc_config[str(num)]] + if tc_items: + SWITCH_TC = Switch('TacticalChallengeShop_Switch') + SWITCH_TC.add_state('on', TC_ON) + SWITCH_TC.add_state('off', TC_OFF) + info.append([SWITCH_TC, self.config.TacticalChallengeShop_Purchases, tc_items]) + + return info + + @property + def valid_task(self) -> list: + task = self.shop_info + if not task: + logger.warning('Shop enabled but no task set') + return task + + @property + def current_shop(self): + return self.task[0][0] + + @property + def current_purchase_count(self): + return self.task[0][1] + + @property + def current_item_list(self): + return self.task[0][2] + + def handle_shop(self, status): + match status: + case ShopStatus.SELECT_SHOP: + if not self.task: + return ShopStatus.FINISH + if self.select_shop(self.current_shop): + self.reset_swipe_flags() + return ShopStatus.SELECT_ITEMS + case ShopStatus.SELECT_ITEMS: + self.select_items(self.current_item_list) + return ShopStatus.PURCHASE + case ShopStatus.PURCHASE: + if self.make_purchase(): + return ShopStatus.REFRESH + return ShopStatus.END + case ShopStatus.REFRESH: + if self.refresh_shop(self.current_purchase_count): + return ShopStatus.SELECT_SHOP + return ShopStatus.END + case ShopStatus.END: + if self.appear(page_shop.check_button): + self.task.pop(0) + return ShopStatus.SELECT_SHOP + self.click_with_interval(BACK, interval=2) + case ShopStatus.FINISH: + return status + case _: + logger.warning(f'Invalid status: {status}') + return status + + def run(self): + """Reset the shop and items position by going main and then shop""" + self.ui_ensure(page_main) + self.ui_ensure(page_shop) + + self.task = self.valid_task + action_timer = Timer(0.5, 1) + status = ShopStatus.SELECT_SHOP + + while 1: + self.device.screenshot() + + if self.ui_additional(): + continue + + if action_timer.reached_and_reset(): + logger.attr('Status', status) + status = self.handle_shop(status) + + if status == ShopStatus.FINISH: + break + + self.config.task_delay(server_update=True) diff --git a/tasks/shop/ui.py b/tasks/shop/ui.py new file mode 100644 index 0000000..6365cf3 --- /dev/null +++ b/tasks/shop/ui.py @@ -0,0 +1,138 @@ +import numpy as np + +from module.base.timer import Timer +from module.base.base import ModuleBase +from module.base.utils import area_size +from module.logger import logger +from module.ocr.ocr import DigitCounter +from tasks.base.ui import UI +from tasks.shop.assets.assets_shop import * + +ITEM_POSITIONS = { + 1: (650, 200), 2: (805, 200), 3: (960, 200), 4: (1110, 200), + 5: (650, 460), 6: (805, 460), 7: (960, 460), 8: (1110, 460), + 9: (650, 200), 10: (805, 200), 11: (960, 200), 12: (1110, 200), + 13: (650, 460), 14: (805, 460), 15: (960, 460), 16: (1110, 460), + 17: (650, 460), 18: (805, 460), 19: (960, 460), 20: (1110, 460), +} + +class ShopUI(UI): + def __init__(self, config, device): + super().__init__(config, device) + + self.click_coords = self.device.click_methods.get(self.config.Emulator_ControlMethod, self.device.click_adb) + self.swipe_vector_range = (0.85, 0.9) + self.swipe_flags = {8:False, 16: False} + self.list = ITEM_LIST + + def swipe_page(self, direction: str, main: ModuleBase, vector_range=None, reverse=False): + """ + Args: + direction: up, down + main: + vector_range (tuple[float, float]): + reverse (bool): + """ + if vector_range is None: + vector_range = self.swipe_vector_range + vector = np.random.uniform(*vector_range) + width, height = area_size(self.list.button) + if direction == 'up': + vector = (0, vector * height) + elif direction == 'down': + vector = (0, -vector * height) + else: + logger.warning(f'Unknown swipe direction: {direction}') + return + + if reverse: + vector = (-vector[0], -vector[1]) + main.device.swipe_vector(vector, self.list.button) + + def select_then_check(self, dest_enter: ButtonWrapper, dest_check: ButtonWrapper): + timer = Timer(5, 10).start() + while 1: + self.device.screenshot() + self.appear_then_click(dest_enter, interval=1) + if self.appear(dest_check): + return True + if timer.reached(): + return False + + def select_shop(self, shop_switch): + """ + Set skip switch to on + Returns: + True if switch is set, False if switch not found + """ + if not shop_switch.appear(main=self): + logger.info(f'{shop_switch.name} not found') + return False + shop_switch.set('on', main=self) + + return True + + def select_items(self, item_list): + """ + Select items in the item list checking if swipe is required each time. + However, swipes are inaccurate and clicks too fast. + """ + timer = Timer(1).start() + for item in item_list: + if self.should_swipe(item): + self.swipe_page('down', self) + + self.wait_until_stable( + self.list.button, + timer=Timer(3, 0), + timeout=Timer(1.5, 5) + ) + while not timer.reached_and_reset(): + pass + self.click_coords(*ITEM_POSITIONS[item]) + + def should_swipe(self, item): + """ + Return True based on two checkpoints: + one at 8 and the other at 16. + Only once for each checkpoint. + """ + if (8 < item < 16) and not self.swipe_flags[8]: + self.swipe_flags[8] = True + return True + elif item > 16 and not self.swipe_flags[16]: + self.swipe_flags[16] = True + return True + return False + + def reset_swipe_flags(self): + self.swipe_flags[8], self.swipe_flags[16] = False, False + + def make_purchase(self): + if self.select_then_check(PURCHASE, CONFIRM_PURCHASE) and self.appear_then_click(CONFIRM_PURCHASE): + return True + logger.warning("No items were selected. Unable to purchase.") + return False + + def refresh_shop(self, need_count): + """ + Refresh the shop + """ + refresh_count = self.get_refresh_count() + if refresh_count: + purchased_count = 4 - refresh_count + if need_count > purchased_count and self.appear_then_click(CONFIRM_REFRESH): + logger.info("Refreshed the shop") + return True + return False + + def get_refresh_count(self): + if not self.select_then_check(REFRESH, CONFIRM_REFRESH): + logger.warning('OCR failed due to invalid page') + return False + count, _, total = DigitCounter(OCR_REFRESH).ocr_single_line(self.device.image) + if total == 0: + logger.warning('Invalid count') + return False + return count + diff --git a/tasks/task/assets/assets_task.py b/tasks/task/assets/assets_task.py new file mode 100644 index 0000000..9b461bb --- /dev/null +++ b/tasks/task/assets/assets_task.py @@ -0,0 +1,27 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +CLAIM = ButtonWrapper( + name='CLAIM', + jp=None, + en=Button( + file='./assets/en/task/CLAIM.png', + area=(935, 639, 1015, 698), + search=(915, 619, 1035, 718), + color=(234, 214, 69), + button=(935, 639, 1015, 698), + ), +) +CLAIM_ALL = ButtonWrapper( + name='CLAIM_ALL', + jp=None, + en=Button( + file='./assets/en/task/CLAIM_ALL.png', + area=(1054, 642, 1243, 700), + search=(1034, 622, 1263, 720), + color=(236, 219, 67), + button=(1054, 642, 1243, 700), + ), +) diff --git a/tasks/task/task.py b/tasks/task/task.py new file mode 100644 index 0000000..cbb9d27 --- /dev/null +++ b/tasks/task/task.py @@ -0,0 +1,27 @@ +from module.base.timer import Timer +from module.logger import logger +from tasks.base.page import page_task +from tasks.base.ui import UI +from tasks.task.assets.assets_task import * + +class Task(UI): + def run(self): + self.ui_ensure(page_task) + action_timer = Timer(1).start() + + while 1: + self.device.screenshot() + if self.ui_additional(): + continue + if action_timer.reached_and_reset(): + if self.match_color(CLAIM_ALL): + self.device.click(CLAIM_ALL) + logger.info("Click Claim All") + continue + if self.match_color(CLAIM): + self.device.click(CLAIM) + logger.info("Click Claim") + continue + break + + self.config.task_delay(minute=120)