mirror of
https://github.com/TheFunny/ArisuAutoSweeper
synced 2025-12-16 22:05:12 +00:00
feat:lesson
This commit is contained in:
parent
2cad7ebbac
commit
668d77363a
4
aas.py
4
aas.py
@ -62,6 +62,10 @@ class ArisuAutoSweeper(AzurLaneAutoScript):
|
||||
from tasks.mission.mission import Mission
|
||||
Mission(config=self.config, device=self.device).run()
|
||||
|
||||
def schedule(self):
|
||||
from tasks.schedule.schedule import Schedule
|
||||
Schedule(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()
|
||||
|
||||
BIN
assets/en/schedule/CONFIRM.png
Normal file
BIN
assets/en/schedule/CONFIRM.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/en/schedule/FIRST_ITEM.png
Normal file
BIN
assets/en/schedule/FIRST_ITEM.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
assets/en/schedule/LOCATIONS.png
Normal file
BIN
assets/en/schedule/LOCATIONS.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/en/schedule/LOCATIONS_POPUP.png
Normal file
BIN
assets/en/schedule/LOCATIONS_POPUP.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/en/schedule/OCR_TICKET.png
Normal file
BIN
assets/en/schedule/OCR_TICKET.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/en/schedule/START_LESSON.png
Normal file
BIN
assets/en/schedule/START_LESSON.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@ -70,6 +70,37 @@
|
||||
"Substitute": false
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"Scheduler": {
|
||||
"Enable": false,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Schedule",
|
||||
"ServerUpdate": "04:00"
|
||||
},
|
||||
"Schedule": {
|
||||
"OnError": "skip"
|
||||
},
|
||||
"Choice1": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice2": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice3": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice4": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice5": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
}
|
||||
},
|
||||
"Shop": {
|
||||
"Scheduler": {
|
||||
"Enable": false,
|
||||
|
||||
@ -318,6 +318,158 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"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": "Schedule",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"OnError": {
|
||||
"type": "select",
|
||||
"value": "skip",
|
||||
"option": [
|
||||
"stop",
|
||||
"skip"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Choice1": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice2": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice3": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice4": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice5": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Shop": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
|
||||
@ -93,6 +93,47 @@ Invitation:
|
||||
type: textarea
|
||||
Substitute: false
|
||||
|
||||
Schedule:
|
||||
OnError:
|
||||
value: skip
|
||||
option: [ stop, skip ]
|
||||
|
||||
Choice1:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice2:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice3:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice4:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice5:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
|
||||
Bounty:
|
||||
OnError:
|
||||
value: skip
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"page": "setting",
|
||||
"tasks": [
|
||||
"Cafe",
|
||||
"Schedule",
|
||||
"Shop"
|
||||
]
|
||||
},
|
||||
|
||||
@ -29,6 +29,14 @@ Daily:
|
||||
- Scheduler
|
||||
- Cafe
|
||||
- Invitation
|
||||
Schedule:
|
||||
- Scheduler
|
||||
- Schedule
|
||||
- Choice1
|
||||
- Choice2
|
||||
- Choice3
|
||||
- Choice4
|
||||
- Choice5
|
||||
Shop:
|
||||
- Scheduler
|
||||
- NormalShop
|
||||
|
||||
@ -52,6 +52,29 @@ class GeneratedConfig:
|
||||
Invitation_Name = None
|
||||
Invitation_Substitute = False
|
||||
|
||||
# Group `Schedule`
|
||||
Schedule_OnError = 'skip' # stop, skip
|
||||
|
||||
# Group `Choice1`
|
||||
Choice1_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice1_Classrooms = None
|
||||
|
||||
# Group `Choice2`
|
||||
Choice2_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice2_Classrooms = None
|
||||
|
||||
# Group `Choice3`
|
||||
Choice3_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice3_Classrooms = None
|
||||
|
||||
# Group `Choice4`
|
||||
Choice4_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice4_Classrooms = None
|
||||
|
||||
# Group `Choice5`
|
||||
Choice5_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice5_Classrooms = None
|
||||
|
||||
# Group `Bounty`
|
||||
Bounty_OnError = 'skip' # stop, skip
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ class ManualConfig:
|
||||
SCHEDULER_PRIORITY = """
|
||||
Restart
|
||||
> Cafe > TacticalChallenge > Circle > Mail
|
||||
> DataUpdate > Bounty > Scrimmage > Task > Shop > Mission > Momotalk
|
||||
> DataUpdate > Bounty > Scrimmage > Schedule > Task > Shop > Mission > Momotalk
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
@ -34,6 +34,10 @@
|
||||
"name": "Cafe",
|
||||
"help": ""
|
||||
},
|
||||
"Schedule": {
|
||||
"name": "Lesson",
|
||||
"help": "AAS will execute Lesson starting from Choice 1 to Choice 5.\nIt will ignore any Choice that have Location set as None or the text area for classrooms is empty.\nIf any of the active Choices have incorrect input, it will perform the action set in Error handling."
|
||||
},
|
||||
"Shop": {
|
||||
"name": "Shop",
|
||||
"help": ""
|
||||
@ -277,6 +281,143 @@
|
||||
"help": "Whether to replace the existing student with their alt.\nIf not, try to match the next student"
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"_info": {
|
||||
"name": "Lesson Settings",
|
||||
"help": ""
|
||||
},
|
||||
"OnError": {
|
||||
"name": "Error Handling",
|
||||
"help": "Perform the selected action when an error occurs (ticket not enough or any invalid setting)",
|
||||
"stop": "Stop script",
|
||||
"skip": "Skip current task"
|
||||
}
|
||||
},
|
||||
"Choice1": {
|
||||
"_info": {
|
||||
"name": "Choice 1",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": "Type a number from 1 to 9 that represents the classroom position in the locations popup.\nUse > to connect multiple classrooms and AAS will select them following the order they appear. Example:\n5 > 3 > 2 > 4 > 1"
|
||||
}
|
||||
},
|
||||
"Choice2": {
|
||||
"_info": {
|
||||
"name": "Choice 2",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Choice3": {
|
||||
"_info": {
|
||||
"name": "Choice 3",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Choice4": {
|
||||
"_info": {
|
||||
"name": "Choice 4",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Choice5": {
|
||||
"_info": {
|
||||
"name": "Choice 5",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Bounty": {
|
||||
"_info": {
|
||||
"name": "Bounty Settings",
|
||||
|
||||
@ -34,6 +34,10 @@
|
||||
"name": "咖啡厅",
|
||||
"help": ""
|
||||
},
|
||||
"Schedule": {
|
||||
"name": "Task.Schedule.name",
|
||||
"help": "Task.Schedule.help"
|
||||
},
|
||||
"Shop": {
|
||||
"name": "商店",
|
||||
"help": ""
|
||||
@ -277,6 +281,143 @@
|
||||
"help": "若咖啡厅已存在所邀请学生的不同服装,选择是否替换该学生\n若不替换,则尝试匹配下一位学生"
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"_info": {
|
||||
"name": "Schedule._info.name",
|
||||
"help": "Schedule._info.help"
|
||||
},
|
||||
"OnError": {
|
||||
"name": "Schedule.OnError.name",
|
||||
"help": "Schedule.OnError.help",
|
||||
"stop": "stop",
|
||||
"skip": "skip"
|
||||
}
|
||||
},
|
||||
"Choice1": {
|
||||
"_info": {
|
||||
"name": "Choice1._info.name",
|
||||
"help": "Choice1._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice1.Location.name",
|
||||
"help": "Choice1.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice1.Classrooms.name",
|
||||
"help": "Choice1.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice2": {
|
||||
"_info": {
|
||||
"name": "Choice2._info.name",
|
||||
"help": "Choice2._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice2.Location.name",
|
||||
"help": "Choice2.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice2.Classrooms.name",
|
||||
"help": "Choice2.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice3": {
|
||||
"_info": {
|
||||
"name": "Choice3._info.name",
|
||||
"help": "Choice3._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice3.Location.name",
|
||||
"help": "Choice3.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice3.Classrooms.name",
|
||||
"help": "Choice3.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice4": {
|
||||
"_info": {
|
||||
"name": "Choice4._info.name",
|
||||
"help": "Choice4._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice4.Location.name",
|
||||
"help": "Choice4.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice4.Classrooms.name",
|
||||
"help": "Choice4.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice5": {
|
||||
"_info": {
|
||||
"name": "Choice5._info.name",
|
||||
"help": "Choice5._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice5.Location.name",
|
||||
"help": "Choice5.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice5.Classrooms.name",
|
||||
"help": "Choice5.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Bounty": {
|
||||
"_info": {
|
||||
"name": "悬赏通缉设置",
|
||||
|
||||
@ -3,6 +3,61 @@ 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 = ButtonWrapper(
|
||||
name='CONFIRM',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/schedule/CONFIRM.png',
|
||||
area=(532, 528, 748, 589),
|
||||
search=(512, 508, 768, 609),
|
||||
color=(110, 207, 241),
|
||||
button=(532, 528, 748, 589),
|
||||
),
|
||||
)
|
||||
FIRST_ITEM = ButtonWrapper(
|
||||
name='FIRST_ITEM',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/schedule/FIRST_ITEM.png',
|
||||
area=(727, 137, 1103, 239),
|
||||
search=(707, 117, 1123, 259),
|
||||
color=(200, 209, 220),
|
||||
button=(727, 137, 1103, 239),
|
||||
),
|
||||
)
|
||||
LOCATIONS = ButtonWrapper(
|
||||
name='LOCATIONS',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/schedule/LOCATIONS.png',
|
||||
area=(1075, 638, 1256, 693),
|
||||
search=(1055, 618, 1276, 713),
|
||||
color=(107, 202, 237),
|
||||
button=(1075, 638, 1256, 693),
|
||||
),
|
||||
)
|
||||
LOCATIONS_POPUP = ButtonWrapper(
|
||||
name='LOCATIONS_POPUP',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/schedule/LOCATIONS_POPUP.png',
|
||||
area=(534, 101, 750, 135),
|
||||
search=(514, 81, 770, 155),
|
||||
color=(194, 202, 210),
|
||||
button=(534, 101, 750, 135),
|
||||
),
|
||||
)
|
||||
OCR_TICKET = ButtonWrapper(
|
||||
name='OCR_TICKET',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/schedule/OCR_TICKET.png',
|
||||
area=(220, 79, 266, 121),
|
||||
search=(200, 59, 286, 141),
|
||||
color=(214, 225, 229),
|
||||
button=(220, 79, 266, 121),
|
||||
),
|
||||
)
|
||||
SCROLL = ButtonWrapper(
|
||||
name='SCROLL',
|
||||
jp=Button(
|
||||
@ -20,3 +75,14 @@ SCROLL = ButtonWrapper(
|
||||
button=(727, 137, 1103, 671),
|
||||
),
|
||||
)
|
||||
START_LESSON = ButtonWrapper(
|
||||
name='START_LESSON',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/schedule/START_LESSON.png',
|
||||
area=(506, 523, 773, 585),
|
||||
search=(486, 503, 793, 605),
|
||||
color=(110, 205, 239),
|
||||
button=(506, 523, 773, 585),
|
||||
),
|
||||
)
|
||||
|
||||
131
tasks/schedule/schedule.py
Normal file
131
tasks/schedule/schedule.py
Normal file
@ -0,0 +1,131 @@
|
||||
from enum import Flag
|
||||
|
||||
from module.base.timer import Timer
|
||||
from module.exception import RequestHumanTakeover
|
||||
from module.logger import logger
|
||||
from tasks.base.assets.assets_base_page import BACK
|
||||
from tasks.base.page import page_schedule
|
||||
from tasks.schedule.ui import ScheduleUI
|
||||
from tasks.base.assets.assets_base_page import SCHEDULE_CHECK
|
||||
|
||||
import re
|
||||
|
||||
class ScheduleStatus(Flag):
|
||||
OCR = 0
|
||||
ENTER = 1
|
||||
SELECT = 2
|
||||
END = 3
|
||||
FINISH = 4
|
||||
|
||||
|
||||
class Schedule(ScheduleUI):
|
||||
@property
|
||||
def schedule_info(self):
|
||||
info = []
|
||||
input_valid = True
|
||||
schedule_config = self.config.cross_get("Schedule")
|
||||
choices = ["Choice1", "Choice2", "Choice3", "Choice4", "Choice5"]
|
||||
|
||||
for choice in choices:
|
||||
location, classrooms = schedule_config[choice]["Location"], schedule_config[choice]["Classrooms"]
|
||||
if location == "None" or not classrooms or (isinstance(classrooms, str) and classrooms.replace(" ", "") == ""):
|
||||
continue
|
||||
elif isinstance(classrooms, int):
|
||||
classrooms_list = [str(classrooms)]
|
||||
else:
|
||||
classrooms = classrooms.strip()
|
||||
classrooms = re.sub(r'[ \t\r\n]', '', classrooms)
|
||||
classrooms = re.sub(r'[>﹥›˃ᐳ❯]', '>', classrooms)
|
||||
classrooms_list = list(set(classrooms.split('>')))
|
||||
|
||||
if self.valid_classroom(classrooms_list):
|
||||
info.append([location, classrooms_list])
|
||||
else:
|
||||
logger.error(f"Failed to read {choice}")
|
||||
input_valid = False
|
||||
|
||||
return info if input_valid else []
|
||||
|
||||
def valid_classroom(self, classrooms_list):
|
||||
if not classrooms_list:
|
||||
return False
|
||||
for classroom in classrooms_list:
|
||||
if not classroom.isdigit():
|
||||
return False
|
||||
if not 1 <= int(classroom) <= 9:
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def valid_task(self) -> list:
|
||||
task = self.schedule_info
|
||||
if not task:
|
||||
logger.warning('Lessons enabled but no task set')
|
||||
self.error_handler()
|
||||
return task
|
||||
|
||||
def error_handler(self):
|
||||
action = self.config.Schedule_OnError
|
||||
if action == 'stop':
|
||||
raise RequestHumanTakeover
|
||||
elif action == 'skip':
|
||||
with self.config.multi_set():
|
||||
self.config.task_delay(server_update=True)
|
||||
self.config.task_stop()
|
||||
|
||||
@property
|
||||
def current_location(self):
|
||||
return self.task[0][0]
|
||||
|
||||
@property
|
||||
def current_classrooms(self):
|
||||
return self.task[0][1]
|
||||
|
||||
def handle_schedule(self, status):
|
||||
match status:
|
||||
case ScheduleStatus.OCR:
|
||||
if self.task:
|
||||
self.ticket = self.get_ticket()
|
||||
if self.ticket not in [0, None]:
|
||||
return ScheduleStatus.ENTER
|
||||
return ScheduleStatus.FINISH
|
||||
case ScheduleStatus.ENTER:
|
||||
if self.enter_location(self.current_location):
|
||||
return ScheduleStatus.SELECT
|
||||
else:
|
||||
self.error_handler()
|
||||
case ScheduleStatus.SELECT:
|
||||
if self.select_classrooms(self.ticket, self.current_classrooms):
|
||||
self.task.pop(0)
|
||||
return ScheduleStatus.END
|
||||
return ScheduleStatus.FINISH
|
||||
case ScheduleStatus.END:
|
||||
if self.appear(SCHEDULE_CHECK):
|
||||
return ScheduleStatus.OCR
|
||||
self.click_with_interval(BACK, interval=2)
|
||||
case ScheduleStatus.FINISH:
|
||||
return status
|
||||
case _:
|
||||
logger.warning(f'Invalid status: {status}')
|
||||
return status
|
||||
|
||||
def run(self):
|
||||
self.ui_ensure(page_schedule)
|
||||
self.task = self.valid_task
|
||||
action_timer = Timer(0.5, 1)
|
||||
status = ScheduleStatus.OCR
|
||||
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
|
||||
if self.ui_additional():
|
||||
continue
|
||||
|
||||
if action_timer.reached_and_reset():
|
||||
logger.attr('Status', status)
|
||||
status = self.handle_schedule(status)
|
||||
|
||||
if status == ScheduleStatus.FINISH:
|
||||
break
|
||||
|
||||
self.config.task_delay(server_update=True)
|
||||
149
tasks/schedule/scroll_select.py
Normal file
149
tasks/schedule/scroll_select.py
Normal file
@ -0,0 +1,149 @@
|
||||
"""
|
||||
Original Author: sanmusen214(https://github.com/sanmusen214)
|
||||
Adapted from https://github.com/sanmusen214/BAAH/blob/1.2/modules/AllTask/SubTask/ScrollSelect.py
|
||||
"""
|
||||
|
||||
from module.logger import logger
|
||||
from module.base.timer import Timer
|
||||
|
||||
|
||||
class ScrollSelect:
|
||||
"""
|
||||
Scroll and select the corresponding level by clicking on the right-side window.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
targetind : int
|
||||
Index of the target level
|
||||
window_starty:
|
||||
Y-coordinate of the upper edge of the window
|
||||
first_item_endy:
|
||||
Y-coordinate of the lower edge of the first item
|
||||
window_endy:
|
||||
Y-coordinate of the lower edge of the window
|
||||
clickx: int
|
||||
Base X-coordinate for sliding and clicking the button
|
||||
hasexpectimage: function
|
||||
Function to determine the appearance of the expected image after clicking, returns a boolean
|
||||
swipeoffsetx: int
|
||||
X offset of the base X-coordinate during sliding to prevent accidental button clicks
|
||||
finalclick: bool
|
||||
Whether to click on clickx and the last row after the sliding ends
|
||||
"""
|
||||
def __init__(self, window_button, first_item_button, expected_button, clickx, swipeoffsetx=-100, finalclick=True) -> None:
|
||||
# TODO: Actually, only concerned about the height of one element, completely displaying the Y of the first button, completely displaying the Y of the bottom button, the number of complete elements that the window can contain, the height of the last element in the window, and the left offset and response distance.
|
||||
self.window_starty = window_button.area[1]
|
||||
self.window_endy = window_button.area[3]
|
||||
self.first_item_endy = first_item_button.area[3]
|
||||
self.windowheight = window_button.height
|
||||
self.itemheight = first_item_button.height
|
||||
self.clickx = clickx
|
||||
self.expected_button = expected_button
|
||||
self.swipeoffsetx = swipeoffsetx
|
||||
self.responsey = 40
|
||||
self.finalclick = finalclick
|
||||
|
||||
def compute_swipe(self, main, x1, y1, distance, responsey):
|
||||
"""
|
||||
Swipe vertically from bottom to top, actual swipe distance calculated based on the distance between two target points, considering inertia.
|
||||
"""
|
||||
distance = abs(distance)
|
||||
logger.info(f"Swipe distance: {distance}")
|
||||
# 0-50
|
||||
if distance < 50:
|
||||
main.device.swipe((x1, y1), (x1, y1 - (distance + responsey)), duration=2)
|
||||
else:
|
||||
# Effective swipe distance for the Chinese server is 60
|
||||
main.device.swipe((x1, y1), (x1, int(y1 - (distance + responsey - 4 * (1 + distance / 100)))), duration=1 + distance / 100)
|
||||
|
||||
def select_location(self, main, target_index) -> None:
|
||||
click_coords = main.device.click_methods.get(main.config.Emulator_ControlMethod, main.device.click_adb)
|
||||
logger.info("Scroll and select the {}-th level".format(target_index + 1))
|
||||
self.scroll_right_up(main, scrollx=self.clickx + self.swipeoffsetx)
|
||||
# Calculate how many complete elements are on one page
|
||||
itemcount = self.windowheight // self.itemheight
|
||||
# Calculate how much height the last incomplete element on this page occupies
|
||||
lastitemheight = self.windowheight % self.itemheight
|
||||
# Height below the incomplete element
|
||||
hiddenlastitemheight = self.itemheight - lastitemheight
|
||||
# Center point of the height of the first element
|
||||
start_center_y = self.window_starty + self.itemheight // 2
|
||||
# Center point of the last complete element on this page
|
||||
end_center_y = start_center_y + (itemcount - 1) * self.itemheight
|
||||
# If the target element is on the current page
|
||||
if target_index < itemcount:
|
||||
# Center point of the target element
|
||||
target_center_y = start_center_y + self.itemheight * target_index
|
||||
self.run_until(main,
|
||||
lambda: click_coords(self.clickx, target_center_y),
|
||||
lambda: main.appear(self.expected_button),
|
||||
)
|
||||
else:
|
||||
# Start scrolling from the gap in the middle of the levels
|
||||
scroll_start_from_y = self.window_endy - self.itemheight // 2
|
||||
# The target element is on subsequent pages
|
||||
# Calculate how much the page should be scrolled
|
||||
scrolltotal_distance = (target_index - itemcount) * self.itemheight + hiddenlastitemheight
|
||||
logger.info("Height hidden by the last element: %d" % hiddenlastitemheight)
|
||||
# First, slide up the hidden part, add a little distance to let the system recognize it as a swipe event
|
||||
self.compute_swipe(main, self.clickx + self.swipeoffsetx, scroll_start_from_y, hiddenlastitemheight, self.responsey)
|
||||
logger.info(f"Swipe distance: {hiddenlastitemheight}")
|
||||
# Update scrolltotal_distance
|
||||
scrolltotal_distance -= hiddenlastitemheight
|
||||
# Still need to scroll up (target_index - itemcount) * self.itemheight
|
||||
# Important: slide the height of (itemcount - 1) elements each time
|
||||
if itemcount == 1:
|
||||
scroll_distance = itemcount * self.itemheight
|
||||
else:
|
||||
scroll_distance = (itemcount - 1) * self.itemheight
|
||||
while scroll_distance <= scrolltotal_distance:
|
||||
self.compute_swipe(main, self.clickx + self.swipeoffsetx, scroll_start_from_y, scroll_distance, self.responsey)
|
||||
scrolltotal_distance -= scroll_distance
|
||||
if scrolltotal_distance > 5:
|
||||
# Last slide
|
||||
self.compute_swipe(main, self.clickx + self.swipeoffsetx, scroll_start_from_y, scrolltotal_distance, self.responsey)
|
||||
if self.finalclick:
|
||||
# Click on the last row
|
||||
self.run_until(main,
|
||||
lambda: click_coords(self.clickx, self.window_endy - self.itemheight // 2),
|
||||
lambda: main.appear(self.expected_button)
|
||||
)
|
||||
|
||||
def run_until(self, main, func1, func2, times=6, sleeptime=1.5) -> bool:
|
||||
"""
|
||||
Repeat the execution of func1 up to a maximum of times or until func2 evaluates to True.
|
||||
|
||||
func1 should perform a single valid operation or internally call a screenshot function.
|
||||
A screenshot is triggered before evaluating func2.
|
||||
|
||||
After each execution of func1, wait for sleeptime seconds.
|
||||
|
||||
If func2 evaluates to True, exit and return True. Otherwise, return False.
|
||||
|
||||
Note: The comment assumes that func1 produces a meaningful operation or internally calls a screenshot function,
|
||||
and func2 is evaluated after each execution of func1.
|
||||
"""
|
||||
for i in range(times):
|
||||
main.device.screenshot()
|
||||
if func2():
|
||||
return True
|
||||
func1()
|
||||
timer = Timer(sleeptime).start()
|
||||
while not timer.reached_and_reset():
|
||||
pass
|
||||
main.device.screenshot()
|
||||
if func2():
|
||||
return True
|
||||
logger.warning("run_until exceeded max times")
|
||||
return False
|
||||
|
||||
def scroll_right_up(self, main, scrollx=928, times=3):
|
||||
"""
|
||||
scroll to top
|
||||
"""
|
||||
for i in range(times):
|
||||
main.device.swipe((scrollx, 226), (scrollx, 561), duration=0.2)
|
||||
timer = Timer(0.5).start()
|
||||
while not timer.reached_and_reset():
|
||||
pass
|
||||
|
||||
80
tasks/schedule/ui.py
Normal file
80
tasks/schedule/ui.py
Normal file
@ -0,0 +1,80 @@
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import DigitCounter
|
||||
from tasks.base.ui import UI
|
||||
from tasks.base.assets.assets_base_page import SCHEDULE_CHECK
|
||||
from tasks.schedule.assets.assets_schedule import *
|
||||
from tasks.schedule.scroll_select import ScrollSelect
|
||||
import numpy as np
|
||||
|
||||
|
||||
SCROLL_SELECT = ScrollSelect(window_button=SCROLL, first_item_button=FIRST_ITEM, expected_button=LOCATIONS, clickx=1114)
|
||||
xs = np.linspace(299, 995, 3, dtype=int)
|
||||
ys = np.linspace(268, 573, 3, dtype=int)
|
||||
|
||||
class ScheduleUI(UI):
|
||||
def select_then_check(self, dest_enter: ButtonWrapper, dest_check: ButtonWrapper):
|
||||
timer = Timer(8, 10).start()
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
self.appear_then_click(dest_enter, interval=1)
|
||||
self.handle_affection_level_up()
|
||||
if self.appear(dest_check):
|
||||
return True
|
||||
|
||||
if timer.reached():
|
||||
return False
|
||||
|
||||
def click_then_check(self, coords, dest_check: ButtonWrapper):
|
||||
click_coords = self.device.click_methods.get(self.config.Emulator_ControlMethod, self.device.click_adb)
|
||||
timer = Timer(3, 5).start()
|
||||
wait = Timer(1).start()
|
||||
while 1:
|
||||
click_coords(*coords)
|
||||
self.device.screenshot()
|
||||
if self.appear_then_click(dest_check):
|
||||
return True
|
||||
while not wait.reached_and_reset():
|
||||
pass
|
||||
if timer.reached():
|
||||
return False
|
||||
|
||||
def enter_location(self, location):
|
||||
SCROLL_SELECT.select_location(self, location)
|
||||
if not self.appear(LOCATIONS):
|
||||
logger.error("Unable to navigate to page for location {}".format(location + 1))
|
||||
return False
|
||||
return self.select_then_check(LOCATIONS, LOCATIONS_POPUP)
|
||||
|
||||
def select_classrooms(self, ticket, classrooms):
|
||||
for classroom in classrooms:
|
||||
if ticket == 0:
|
||||
return False
|
||||
classroom = int(classroom) - 1
|
||||
col = int(classroom % len(xs))
|
||||
row = int((classroom - col) / len(ys))
|
||||
targetloc = (xs[col], ys[row])
|
||||
if not self.click_then_check(targetloc, START_LESSON):
|
||||
logger.info(f"Classroom {classroom + 1} does not exist or has already been clicked")
|
||||
continue
|
||||
if self.select_then_check(START_LESSON, CONFIRM):
|
||||
ticket -= 1
|
||||
if not self.select_then_check(CONFIRM, LOCATIONS_POPUP):
|
||||
break
|
||||
return True
|
||||
|
||||
def get_ticket(self):
|
||||
"""
|
||||
Page:
|
||||
in: page_bounty
|
||||
"""
|
||||
if not self.appear(SCHEDULE_CHECK):
|
||||
logger.warning('OCR failed due to invalid page')
|
||||
return False
|
||||
ticket, _, total = DigitCounter(OCR_TICKET).ocr_single_line(self.device.image)
|
||||
if total == 0:
|
||||
logger.warning('Invalid ticket')
|
||||
return False
|
||||
logger.attr('ScheduleTicket', ticket)
|
||||
#self.config.stored.BountyTicket.set(ticket)
|
||||
return ticket
|
||||
Loading…
Reference in New Issue
Block a user