Scripts used to grab or read stats in RoK
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

rok-stat-grabber.py 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import argparse
  2. import re
  3. import io
  4. import os
  5. import sys
  6. import time
  7. from datetime import datetime
  8. from ppadb.client import Client as AdbClient
  9. from PIL import Image
  10. import pytesseract
  11. # ----
  12. # Program
  13. # ----
  14. PROGRAM_NAME = "RoK Scraper"
  15. PROGRAM_VERSION = "0.3"
  16. PROGRAM_DESCRIPTION = "This program controls devices via ADB to take screenshots in Rise of Kingdoms."
  17. # TODO:
  18. # - Skip your own ranking
  19. # - Correct for device rotation
  20. # - Better logic for tap_profile()
  21. # - Draw box around skipped profiles
  22. # - Support for the last ~995-1000
  23. # - Coordinates to percentages
  24. # ----
  25. # Arguments
  26. # ----
  27. parser = argparse.ArgumentParser(description = PROGRAM_DESCRIPTION)
  28. parser.add_argument("-p", "--project", help = "project name", required = True)
  29. parser.add_argument("-s", "--serial", help = "device serial", required = True)
  30. parser.add_argument("-v", "--verbose", help = "be verbose", default = False, action = "store_true")
  31. parser.add_argument("-c", "--count", help = "how many to scrape", required = True, type = int)
  32. parser.add_argument("-n", "--start", help = "what number to start scraping at", default = 1, type = int)
  33. parser.add_argument("--debug", help = "debug", default = False, action = "store_true")
  34. arguments = parser.parse_args()
  35. # ----
  36. # Coordinates
  37. # ----
  38. ROTATE = 0 # TODO: fix this; shouldn't be needed
  39. TAP_LOCATIONS = {
  40. "1": (460, 460),
  41. "2": (460, 624),
  42. "3": (460, 780),
  43. "4": (460, 940),
  44. "Middle": (460, 980),
  45. "Middle + 1": (460, 1136),
  46. "Middle + 2": (460, 1300)
  47. }
  48. # ----
  49. # Functions
  50. # ----
  51. # TODO: pass device instead of image
  52. def read_string_from_image(image, x, y, x2, y2):
  53. return pytesseract.image_to_string(image.crop((x, y, x2, y2)), config="--psm 6").strip().replace('\n', ' ').replace('\r', '').replace('\t', ' ')
  54. def get_clipboard(device):
  55. raw = device.shell('am broadcast -n "ch.pete.adbclipboard/.ReadReceiver"')
  56. dataMatcher = re.compile("^.*\n.*data=\"(.*)\"$", re.DOTALL)
  57. dataMatch = dataMatcher.match(raw)
  58. return dataMatch.group(1)
  59. def take_screenshot(device, path):
  60. screencap = device.screencap()
  61. image = Image.open(io.BytesIO(screencap)).rotate(ROTATE, expand=1)
  62. image.save(path + ".png", "PNG")
  63. return
  64. # TODO: this can be done better
  65. def get_datetime_string():
  66. d = datetime.now()
  67. return str(d.year).zfill(4) + str(d.month).zfill(2) + str(d.day).zfill(2) + str(d.hour).zfill(2) + str(d.minute).zfill(2) + str(d.second).zfill(2)
  68. def read_single_power_ranking(device, folder):
  69. datetime = get_datetime_string()
  70. # Screenshot profile
  71. # ID, Username, Power, Kill Points
  72. time.sleep(1)
  73. take_screenshot(device, folder + datetime + "_profile")
  74. # Copy and save username
  75. device.shell("input tap 1055 455")
  76. time.sleep(1)
  77. username = get_clipboard(device)
  78. with open(projectFolder + "usernames.txt", 'a+', newline='', encoding='utf-8') as output_file:
  79. output_file.write(datetime + "\t")
  80. output_file.write(username)
  81. output_file.write("\n")
  82. if arguments.verbose: print(username, end = "", flush = True)
  83. # Click "More Info"
  84. # Screenshot "More Info"
  85. # Power, Kill Points, Highest Power, Victory, Defeat, Dead, Scout Times, Resources Gathered, Resource Assistance, Alliance Help Times
  86. device.shell("input tap 620 1070")
  87. time.sleep(2)
  88. take_screenshot(device, folder + datetime + "_more")
  89. # Click "?"
  90. # Screenshot Kills
  91. # Kill Points, T1-5 Kills, T1-5 Points
  92. device.shell("input tap 1725 250")
  93. time.sleep(1)
  94. take_screenshot(device, folder + datetime + "_kills")
  95. # Exit
  96. if arguments.verbose: print("") # TODO: say "done" or something here if successful
  97. device.shell("input tap 2230 85")
  98. time.sleep(1)
  99. device.shell("input tap 2185 160")
  100. return
  101. # Tap to enter a profile, skipping ones that cannot be clicked
  102. def tap_profile(i, folder): # TODO: fix this absolute mess of a function
  103. if i == 1:
  104. if is_profile(TAP_LOCATIONS["1"], folder, i): return 1
  105. elif is_profile(TAP_LOCATIONS["2"], folder, i + 1): return 2
  106. elif is_profile(TAP_LOCATIONS["3"], folder, i + 2): return 3
  107. elif is_profile(TAP_LOCATIONS["4"], folder, i + 3): return 4
  108. else: sys.exit("Profile not found.")
  109. elif i == 2:
  110. if is_profile(TAP_LOCATIONS["2"], folder, i): return 1
  111. elif is_profile(TAP_LOCATIONS["3"], folder, i + 1): return 2
  112. elif is_profile(TAP_LOCATIONS["4"], folder, i + 2): return 3
  113. else: sys.exit("Profile not found.")
  114. elif i == 3:
  115. if is_profile(TAP_LOCATIONS["3"], folder, i): return 1
  116. elif is_profile(TAP_LOCATIONS["4"], folder, i + 1): return 2
  117. else: sys.exit("Profile not found.")
  118. elif i == 4:
  119. if is_profile(TAP_LOCATIONS["4"], folder, i): return 1
  120. else: sys.exit("Profile not found.")
  121. else:
  122. if is_profile(TAP_LOCATIONS["Middle"], folder, i): return 1
  123. elif is_profile(TAP_LOCATIONS["Middle + 1"], folder, i + 1): return 2
  124. elif is_profile(TAP_LOCATIONS["Middle + 2"], folder, i + 2): return 3
  125. else: sys.exit("Profile not found.")
  126. # Tap and test if you are in a profile
  127. def is_profile(coordinates, folder, i):
  128. device.shell("input tap " + str(coordinates[0]) + " " + str(coordinates[1]))
  129. time.sleep(1)
  130. if read_string_from_image(Image.open(io.BytesIO(device.screencap())).rotate(ROTATE, expand=1), 1652, 1167, 1745, 1209) == "Mail":
  131. return True
  132. else:
  133. device.shell("input tap " + str(coordinates[0]) + " " + str(coordinates[1]))
  134. time.sleep(2)
  135. if read_string_from_image(Image.open(io.BytesIO(device.screencap())).rotate(ROTATE, expand=1), 1652, 1167, 1745, 1209) == "Mail":
  136. return True
  137. if arguments.verbose: print("skipped rank " + str(i) + ". ", flush = True)
  138. take_screenshot(device, folder + "skipped/" + get_datetime_string() + "_skipped_" + str(i))
  139. return False
  140. # Read from the power rankings list
  141. def read_from_power_rankings(count, start, device, folder):
  142. if arguments.verbose: print("Reading", count, "power rankings.")
  143. time.sleep(1)
  144. i = start
  145. while i < (count + 1):
  146. if arguments.verbose: print(str(i) + "/" + str(count) + ": ", end = "", flush = True)
  147. # Scrape
  148. i = i + tap_profile(i, folder)
  149. read_single_power_ranking(device, folder)
  150. # Rest
  151. time.sleep(1)
  152. if i%10 == 0: time.sleep(5)
  153. if i%25 == 0: time.sleep(10)
  154. return
  155. # ----
  156. # Main
  157. # ----
  158. if __name__ == '__main__':
  159. # Connect to ADB device
  160. client = AdbClient(host="127.0.0.1", port=5037)
  161. device = client.device(arguments.serial)
  162. print("Connected to", device.serial)
  163. # Create project folder
  164. projectFolder = "output/" + arguments.project + "/"
  165. if not os.path.exists(projectFolder):
  166. os.makedirs(projectFolder + "skipped/")
  167. # Debug
  168. if arguments.debug: take_screenshot(device, projectFolder + get_datetime_string() + "_debug")
  169. # Scrape
  170. read_from_power_rankings(arguments.count, arguments.start, device, projectFolder)