├── .gitignore ├── .idea ├── IronBot.iml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── IronBot.py ├── README.md ├── find_cursor.py └── triggers ├── attack.png ├── bank_deposit_box.png ├── bankslot.png ├── deposit_button.png ├── deposit_button_hover.png ├── enter_mysterious_entrance.png ├── exit_mysterious_door.png ├── mine_iron_ore_rocks.png ├── mine_rocks.png ├── reset_check.png ├── rock3_check.png └── screenie.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | .venv 89 | venv/ 90 | ENV/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | -------------------------------------------------------------------------------- /.idea/IronBot.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 34 | 35 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 77 | 78 | 79 | 80 | .png 81 | png 82 | 83 | 84 | 85 | 87 | 88 | 98 | 99 | 100 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 130 | 131 | 134 | 135 | 136 | 137 | 140 | 141 | 144 | 145 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 172 | 173 | 174 | 175 | 192 | 193 | 210 | 211 | 228 | 229 | 240 | 241 | 259 | 260 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 288 | 289 | 290 | 291 | 1494338938548 292 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | -------------------------------------------------------------------------------- /IronBot.py: -------------------------------------------------------------------------------- 1 | import math 2 | from random import randint, uniform 3 | import sys 4 | import time 5 | 6 | import cv2 7 | import numpy as np 8 | import pyautogui as pag 9 | 10 | 11 | class MineBot(object): 12 | """ 13 | Iron mining bot for the Dwarven Mines, using 3/3 rocks. Currently 14 | Bot generates: 15 | One full run in ~155 seconds, for ~23.25 runs/hour 16 | ~22.8k xp/hr, 17 | ~650 iron ore/hr, 18 | ~190.5k gp/hr (assuming a 293gp/ore price) 19 | """ 20 | def __init__(self): 21 | pass 22 | 23 | def mine_loop(self, rock_locations, triggers, mininglap): 24 | order = ['rock1', 'rock2', 'rock3'] 25 | trigger_order = ['rock1iron', 'rock1noiron', 'rock2iron', 'rock2noiron', 'rock3iron', 'rock3noiron'] 26 | 27 | for i in range(len(order)): 28 | # Checks for full inventory. 29 | if not self.image_match((2352, 682, 63, 55), 'triggers/bankslot.png'): 30 | return True 31 | 32 | # Checks for scorpions and moves to the location of rock #3. 33 | if i == 2: 34 | self.random_coordinate(rock_locations['movetorock3']) 35 | self.check_for_scorpion((rock_locations['movetorock3'][0], rock_locations['movetorock3'][1], 36 | rock_locations['movetorock3'][2] + 250, rock_locations['movetorock3'][3] + 250)) 37 | pag.click() 38 | 39 | self.random_coordinate(rock_locations[order[i]]) 40 | self.wait_for_trigger(triggers[trigger_order[(i*2)]]) # wait for iron 41 | pag.click() 42 | self.wait_for_trigger(triggers[trigger_order[(i*2)+1]]) # wait for success 43 | self.random_wait(0.05, 0.1) 44 | 45 | # Resets location for the beginning of the next loop. 46 | self.random_coordinate(rock_locations['reset']) 47 | self.check_for_scorpion((rock_locations['reset'][0], rock_locations['reset'][1], 48 | rock_locations['reset'][2] + 250, rock_locations['reset'][3] + 250)) 49 | pag.click() 50 | self.wait_for_trigger((1700, 50, 150, 150, 'triggers/reset_check.png')) # check to make sure made it to right location 51 | 52 | return 53 | 54 | def bank_loop(self, bank_locations, triggers): 55 | """Makes a trip to the bank to deposit the iron ore. Takes 16-17 seconds""" 56 | order = ['dgdoordown', 'depositbox', 'depositbutton', 'dgdoorup', 'startlocation'] 57 | waits = [(0.1, 0.2), (0.1, 0.2), (0.1, 0.2), (5, 6), (0.1, 0.2)] 58 | 59 | for i in range(len(order)): 60 | self.random_coordinate(bank_locations[order[i]]) 61 | if i in range(4): 62 | self.wait_for_trigger(triggers[order[i]]) 63 | if i == 4: 64 | self.check_for_scorpion((947, 1195, 250, 250)) 65 | pag.click() 66 | self.random_wait(waits[i][0], waits[i][1]) 67 | 68 | def drop_loop(self, keybind='0'): 69 | r = randint(28, 32) 70 | t = uniform(4.8, 7) 71 | clicktime = t/r 72 | for _ in range(r): 73 | pag.keyDown(keybind) 74 | self.random_wait(clicktime - .04, clicktime + .04) 75 | 76 | def bank_or_drop(self): 77 | while True: 78 | answer = input('Would you like to 1) bank (takes an extra ~10 seconds/inventory) or 2) drop the ore? ') 79 | if answer.lower() in ['b', 'ban', 'bnk', 'bakn', 'bnak', 'bank', 's', 'save', 'y', 'yes']: 80 | print('Banking selected.') 81 | return 'bank' 82 | elif answer.lower() in ['d', 'dro', 'drp', 'drpo', 'dorp', 'drop', 'n', 'no']: 83 | print('Dropping selected.') 84 | return 'drop' 85 | else: 86 | print('Not a valid option. Please try again.') 87 | 88 | def random_coordinate(self, location): 89 | """Moves cursor to random locaction still above the object to be clicked""" 90 | x = randint(location[0], location[0]+location[2]) 91 | y = randint(location[1], location[1]+location[3]) 92 | time = self.travel_time(x, y) 93 | 94 | return pag.moveTo(x, y, time) 95 | 96 | def travel_time(self, x2, y2): 97 | """Calculates cursor travel time in seconds per 240-270 pixels, based on a variable rate of movement""" 98 | rate = uniform(0.09, 0.15) 99 | x1, y1 = pag.position() 100 | distance = math.sqrt(math.pow(x2-x1, 2)+math.pow(y2-y1, 2)) 101 | 102 | return max(uniform(.08, .12), rate * (distance/randint(250, 270))) 103 | 104 | def image_match(self, r, img): 105 | pag.screenshot('triggers/screenie.png', region=r) 106 | screen = cv2.imread('triggers/screenie.png') 107 | template = cv2.imread(img) 108 | 109 | res = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED) 110 | threshold = .80 111 | loc = np.where(res >= threshold) 112 | if len(loc[0]) > 0: 113 | return True 114 | 115 | return False 116 | 117 | def wait_for_trigger(self, triggers): 118 | """Checks to see if the proper message is on screen to indicate that the rock is ready to mine""" 119 | r = triggers[0], triggers[1], triggers[2], triggers[3] 120 | img = triggers[4] 121 | while self.image_match(r, img) == False: 122 | self.random_wait(0.1, 0.2) 123 | 124 | return self.image_match(r, img) 125 | 126 | def random_wait(self, min=0.25, max=0.50): 127 | """Waits a random number of seconds between two numbers (0.25 and 0.50 default) to mimic human reaction time""" 128 | return time.sleep(uniform(min, max)) 129 | 130 | def check_for_scorpion(self, r): 131 | if self.image_match(r, 'triggers/attack.png'): 132 | pag.click(button='right') 133 | print("Successfully avoided a scorpion.") 134 | pag.moveRel(randint(-75, 75), randint(58, 78)) 135 | 136 | def logout(self, logout_location=(1194, 955, 167, 14)): 137 | pag.keyDown('escape') 138 | self.random_wait() 139 | pag.click(self.random_coordinate(logout_location)) 140 | sys.exit('Successful exit') 141 | 142 | def pause(self): 143 | pass 144 | 145 | if __name__ == '__main__': 146 | 147 | mb = MineBot() 148 | rock_locations = {'rock1': (1243, 569, 55, 62), 'rock2': (1138, 695, 34, 39), 149 | 'movetorock3': (962, 823, 50, 50), 'rock3': (1128, 691, 37, 56), 150 | 'reset': (1465, 570, 50, 50)} 151 | 152 | bank_locations = {'dgdoordown': (1630, 230, 70, 100), 'depositbox': (1079, 1086, 104, 71), 153 | 'depositbutton': (1333, 849, 30, 15), 'dgdoorup': (1625, 240, 45, 200), 154 | 'startlocation': (947, 1195, 71, 65)} 155 | 156 | rock_triggers = {'rock1iron': (1243, 569, 350, 200, 'triggers/mine_iron_ore_rocks.png'), 157 | 'rock1noiron': (1243, 569, 275, 200, 'triggers/mine_rocks.png'), 158 | 'rock2iron': (1144, 695, 350, 200, 'triggers/mine_iron_ore_rocks.png'), 159 | 'rock2noiron': (1144, 695, 275, 200, 'triggers/mine_rocks.png'), 160 | 'rock3iron': (1128, 691, 350, 200, 'triggers/mine_iron_ore_rocks.png'), 161 | 'rock3noiron': (1128, 691, 275, 200, 'triggers/mine_rocks.png')} 162 | 163 | bank_triggers = {'dgdoordown': (1630, 230, 470, 200, 'triggers/enter_mysterious_entrance.png'), 164 | 'depositbox': (1079, 1086, 500, 200, 'triggers/bank_deposit_box.png'), 165 | 'depositbutton': (1175, 750, 350, 100, 'triggers/deposit_button_hover.png'), 166 | 'dgdoorup': (1625, 240, 350, 300, 'triggers/exit_mysterious_door.png')} 167 | 168 | print('Your character must be in the NorthWest tile by the two iron ore rocks in the Dwarven Mines.\n' 169 | 'Also, the up arrow must be pushed as high as possible, and rotation must be the same as on first login.\n' 170 | 'Press Ctrl-F2 to exit.') 171 | 172 | answer = mb.bank_or_drop() 173 | 174 | time.sleep(5) 175 | 176 | try: 177 | lap = 0 178 | while True: 179 | start_time = time.time() 180 | mininglap = 1 181 | while True: 182 | full = mb.mine_loop(rock_locations, rock_triggers, mininglap) 183 | if full: 184 | break 185 | mininglap += 1 186 | if answer == 'bank': 187 | mb.bank_loop(bank_locations, bank_triggers) 188 | else: 189 | mb.drop_loop() 190 | lap += 1 191 | laptime = time.time()-start_time 192 | print("Trip number {tripno} took {time} seconds, which is a {xp} xp/hour and " 193 | "{ore} iron ore/hour pace.".format(tripno=lap, time=round(laptime, 2), 194 | xp=('{0:,.0f}'.format(60 / (laptime / 60) * 28 * 35)), 195 | ore=('{0:,.0f}'.format(60/(laptime/60)*28)))) 196 | except KeyboardInterrupt: 197 | sys.exit() 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Runescape iron ore mining bot for the Dwarven Mines. 2 | 3 | Written with python (cy2, numpy, pyautogui). 4 | 5 | This code is for demonstration purposes only, to accompany an auto clicker creation guide found at [How to Write a Runescape Auto Clicker With Python](http://www.zaxrosenberg.com/how-to-write-a-runescape-auto-clicker-with-python-part-i/). Please use this program at your own risk. 6 | 7 | Also, note that this code is resolution specific. Use the included find_cursor.py script to locate the proper pixels/ranges on your resolution and Runescape screen layout to adjust the IronBot. 8 | -------------------------------------------------------------------------------- /find_cursor.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import pyautogui 4 | 5 | 6 | if __name__ == '__main__': 7 | print('Press Ctrl-C to quit.') 8 | try: 9 | while True: 10 | # Get and print the mouse coordinates. 11 | x, y = pyautogui.position() 12 | positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4) 13 | print("\r", positionStr, end="", flush=True) 14 | time.sleep(0.25) 15 | except KeyboardInterrupt: 16 | print('\nDone.') 17 | -------------------------------------------------------------------------------- /triggers/attack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/attack.png -------------------------------------------------------------------------------- /triggers/bank_deposit_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/bank_deposit_box.png -------------------------------------------------------------------------------- /triggers/bankslot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/bankslot.png -------------------------------------------------------------------------------- /triggers/deposit_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/deposit_button.png -------------------------------------------------------------------------------- /triggers/deposit_button_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/deposit_button_hover.png -------------------------------------------------------------------------------- /triggers/enter_mysterious_entrance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/enter_mysterious_entrance.png -------------------------------------------------------------------------------- /triggers/exit_mysterious_door.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/exit_mysterious_door.png -------------------------------------------------------------------------------- /triggers/mine_iron_ore_rocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/mine_iron_ore_rocks.png -------------------------------------------------------------------------------- /triggers/mine_rocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/mine_rocks.png -------------------------------------------------------------------------------- /triggers/reset_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/reset_check.png -------------------------------------------------------------------------------- /triggers/rock3_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/rock3_check.png -------------------------------------------------------------------------------- /triggers/screenie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaxR/IronBot/2930278b8e5fa75d313e50504c47749c496f5ee6/triggers/screenie.png --------------------------------------------------------------------------------