mirror of
https://github.com/fishyboteso/fishyboteso.git
synced 2024-08-30 18:32:13 +00:00
Merge pull request #66 from fishyboteso/feature/fullauto
Full auto engine
This commit is contained in:
commit
b066f29798
@ -1,3 +1,6 @@
|
||||
apiversion = 2
|
||||
chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/619b4ab0b8ff91746afda855542e886d27b7a794/Chalutier_1.1.2.zip", 112)
|
||||
lam2 = ("LibAddonMenu-2.0", "https://www.esoui.com/downloads/dl7/LibAddonMenu-2.0r32.zip", 32)
|
||||
|
||||
fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/files/6329586/FishyQR.zip", 100)
|
||||
libgps = ("LibGPS", "https://cdn.esoui.com/downloads/file601/LibGPS_3_0_3.zip", 30)
|
||||
|
@ -47,16 +47,11 @@ def _get_factor(key):
|
||||
return config.get("full_auto_factors", {}).get(key)
|
||||
|
||||
|
||||
class Calibrate:
|
||||
class Calibrator:
|
||||
def __init__(self, engine: FullAuto):
|
||||
self._callibrate_state = -1
|
||||
self.engine = engine
|
||||
|
||||
# region getters
|
||||
@property
|
||||
def crop(self):
|
||||
return _get_factor("crop")
|
||||
|
||||
@property
|
||||
def move_factor(self):
|
||||
return _get_factor("move_factor")
|
||||
@ -65,26 +60,15 @@ class Calibrate:
|
||||
def rot_factor(self):
|
||||
return _get_factor("rot_factor")
|
||||
|
||||
@property
|
||||
def time_to_reach_bottom(self):
|
||||
return _get_factor("time_to_reach_bottom")
|
||||
|
||||
# endregion
|
||||
|
||||
def all_callibrated(self):
|
||||
return self.crop is not None and self.move_factor is not None and self.rot_factor is not None
|
||||
return self.move_factor is not None and self.rot_factor is not None
|
||||
|
||||
def toggle_show(self):
|
||||
self.engine.show_crop = not self.engine.show_crop
|
||||
|
||||
def update_crop(self, enable_crop=True):
|
||||
if enable_crop:
|
||||
self.engine.show_crop = True
|
||||
crop = get_crop_coods(self.engine.window)
|
||||
_update_factor("crop", crop)
|
||||
self.engine.window.crop = crop
|
||||
|
||||
def walk_calibrate(self):
|
||||
def _walk_calibrate(self):
|
||||
walking_time = 3
|
||||
|
||||
coods = self.engine.get_coods()
|
||||
@ -107,7 +91,7 @@ class Calibrate:
|
||||
_update_factor("move_factor", move_factor)
|
||||
logging.info("done")
|
||||
|
||||
def rotate_calibrate(self):
|
||||
def _rotate_calibrate(self):
|
||||
rotate_times = 50
|
||||
|
||||
coods = self.engine.get_coods()
|
||||
@ -131,25 +115,7 @@ class Calibrate:
|
||||
_update_factor("rot_factor", rot_factor)
|
||||
logging.info("done")
|
||||
|
||||
def time_to_reach_bottom_callibrate(self):
|
||||
self._callibrate_state = 0
|
||||
def calibrate(self):
|
||||
self._walk_calibrate()
|
||||
self._rotate_calibrate()
|
||||
|
||||
def _f8_pressed():
|
||||
self._callibrate_state += 1
|
||||
|
||||
logging.info("look straight up and press f8")
|
||||
hotkey.set_hotkey(Key.F8, _f8_pressed)
|
||||
|
||||
wait_until(lambda: self._callibrate_state == 1)
|
||||
|
||||
logging.info("as soon as you look on the floor, press f8 again")
|
||||
|
||||
y_cal_start_time = time.time()
|
||||
while self._callibrate_state == 1:
|
||||
mse.move(0, FullAuto.rotate_by)
|
||||
time.sleep(0.05)
|
||||
hotkey.free_key(Key.F8)
|
||||
|
||||
time_to_reach_bottom = time.time() - y_cal_start_time
|
||||
_update_factor("time_to_reach_bottom", time_to_reach_bottom)
|
||||
logging.info("done")
|
@ -11,16 +11,10 @@ def get_controls(engine: FullAuto):
|
||||
from fishy.engine.fullautofisher.player import Player
|
||||
controls = [
|
||||
("MODE_SELECT", {
|
||||
Key.RIGHT: (lambda: engine.controls.select_mode("CALIBRATE"), "calibrate mode"),
|
||||
Key.UP: (lambda: engine.controls.select_mode("TEST1"), "test mode"),
|
||||
Key.RIGHT: (Recorder(engine).toggle_recording, "start/stop record"),
|
||||
Key.UP: (engine.calibrator.calibrate, "calibrate mode"),
|
||||
Key.LEFT: (Player(engine).toggle_move, "start/stop play"),
|
||||
Key.DOWN: (Recorder(engine).toggle_recording, "start/stop record"),
|
||||
}),
|
||||
("CALIBRATE", {
|
||||
Key.RIGHT: (engine.calibrate.update_crop, "cropping"),
|
||||
Key.UP: (engine.calibrate.walk_calibrate, "walking"),
|
||||
Key.LEFT: (engine.calibrate.rotate_calibrate, "rotation"),
|
||||
Key.DOWN: (engine.calibrate.time_to_reach_bottom_callibrate, "look up down")
|
||||
Key.DOWN: (lambda: engine.controls.select_mode("TEST1"), "test mode"),
|
||||
}),
|
||||
("TEST1", {
|
||||
Key.RIGHT: (engine.test.print_coods, "print coordinates"),
|
||||
|
@ -14,8 +14,8 @@ import time
|
||||
import numpy as np
|
||||
import pytesseract
|
||||
|
||||
from fishy.engine.fullautofisher.tesseract import is_tesseract_installed, downlaoad_and_extract_tesseract, \
|
||||
get_values_from_image
|
||||
from fishy.constants import libgps, fishyqr, lam2
|
||||
from fishy.engine.fullautofisher.qr_detection import get_values_from_image, get_qr_location
|
||||
from fishy.engine.semifisher.fishing_mode import FishingMode
|
||||
|
||||
from fishy.engine import SemiFisherEngine
|
||||
@ -25,10 +25,9 @@ from fishy.engine.semifisher import fishing_mode, fishing_event
|
||||
from fishy.engine.common.IEngine import IEngine
|
||||
from pynput import keyboard, mouse
|
||||
|
||||
from fishy.helper import hotkey, helper
|
||||
from fishy.helper import hotkey, helper, hotkey_process
|
||||
from fishy.helper.config import config
|
||||
from fishy.helper.downloader import download_file_from_google_drive
|
||||
from fishy.helper.helper import sign
|
||||
from fishy.helper.helper import sign, addon_exists
|
||||
from fishy.helper.hotkey import Key
|
||||
|
||||
mse = mouse.Controller()
|
||||
@ -36,12 +35,11 @@ kb = keyboard.Controller()
|
||||
|
||||
|
||||
def image_pre_process(img):
|
||||
scale_percent = 200 # percent of original size
|
||||
scale_percent = 100 # percent of original size
|
||||
width = int(img.shape[1] * scale_percent / 100)
|
||||
height = int(img.shape[0] * scale_percent / 100)
|
||||
dim = (width, height)
|
||||
img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
|
||||
img = cv2.bitwise_not(img)
|
||||
return img
|
||||
|
||||
|
||||
@ -59,7 +57,7 @@ class FullAuto(IEngine):
|
||||
def __init__(self, gui_ref):
|
||||
from fishy.engine.fullautofisher.controls import Controls
|
||||
from fishy.engine.fullautofisher import controls
|
||||
from fishy.engine.fullautofisher.calibrate import Calibrate
|
||||
from fishy.engine.fullautofisher.calibrator import Calibrator
|
||||
from fishy.engine.fullautofisher.test import Test
|
||||
|
||||
super().__init__(gui_ref)
|
||||
@ -67,37 +65,41 @@ class FullAuto(IEngine):
|
||||
self._curr_rotate_y = 0
|
||||
|
||||
self.fisher = SemiFisherEngine(None)
|
||||
self.calibrate = Calibrate(self)
|
||||
self.calibrator = Calibrator(self)
|
||||
self.test = Test(self)
|
||||
self.controls = Controls(controls.get_controls(self))
|
||||
self.show_crop = False
|
||||
|
||||
def run(self):
|
||||
self.show_crop = False
|
||||
|
||||
addons_req = [libgps, lam2, fishyqr]
|
||||
for addon in addons_req:
|
||||
if not helper.addon_exists(*addon):
|
||||
helper.install_addon(*addon)
|
||||
|
||||
FullAuto.state = State.NONE
|
||||
|
||||
self.gui.bot_started(True)
|
||||
fishing_event.unsubscribe()
|
||||
self.fisher.toggle_start()
|
||||
|
||||
self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="Full auto debug")
|
||||
|
||||
try:
|
||||
if self.calibrate.crop is None:
|
||||
self.calibrate.update_crop(enable_crop=False)
|
||||
self.window.crop = self.calibrate.crop
|
||||
self.window.crop = get_qr_location(self.window.get_capture())
|
||||
if self.window.crop is None:
|
||||
logging.warning("FishyQR not found")
|
||||
self.start = False
|
||||
raise Exception("FishyQR not found")
|
||||
|
||||
if not is_tesseract_installed():
|
||||
logging.info("tesseract not found")
|
||||
downlaoad_and_extract_tesseract()
|
||||
if not self.calibrator.all_callibrated():
|
||||
logging.error("you need to calibrate first")
|
||||
|
||||
if not self.calibrate.all_callibrated():
|
||||
logging.error("you need to callibrate first")
|
||||
self.fisher.toggle_start()
|
||||
fishing_event.unsubscribe()
|
||||
|
||||
self.controls.initialize()
|
||||
while self.start and WindowClient.running():
|
||||
self.window.show(self.show_crop, func=image_pre_process)
|
||||
if not self.show_crop:
|
||||
if self.show_crop:
|
||||
self.window.show(self.show_crop, func=image_pre_process)
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
@ -121,7 +123,7 @@ class FullAuto(IEngine):
|
||||
logging.error("set target first")
|
||||
return
|
||||
|
||||
if not self.calibrate.all_callibrated():
|
||||
if not self.calibrator.all_callibrated():
|
||||
logging.error("you need to callibrate first")
|
||||
return
|
||||
|
||||
@ -140,7 +142,7 @@ class FullAuto(IEngine):
|
||||
|
||||
self.rotate_to(target_angle, from_angle)
|
||||
|
||||
walking_time = dist / self.calibrate.move_factor
|
||||
walking_time = dist / self.calibrator.move_factor
|
||||
print(f"walking for {walking_time}")
|
||||
kb.press('w')
|
||||
time.sleep(walking_time)
|
||||
@ -162,7 +164,7 @@ class FullAuto(IEngine):
|
||||
if abs(angle_diff) > 180:
|
||||
angle_diff = (360 - abs(angle_diff)) * sign(angle_diff) * -1
|
||||
|
||||
rotate_times = int(angle_diff / self.calibrate.rot_factor) * -1
|
||||
rotate_times = int(angle_diff / self.calibrator.rot_factor) * -1
|
||||
|
||||
print(f"rotate_times: {rotate_times}")
|
||||
|
||||
@ -173,17 +175,17 @@ class FullAuto(IEngine):
|
||||
def look_for_hole(self):
|
||||
self._hole_found_flag = False
|
||||
|
||||
if FishingMode.CurrentMode == fishing_mode.State.LOOK:
|
||||
if FishingMode.CurrentMode == fishing_mode.State.LOOKING:
|
||||
return True
|
||||
|
||||
def found_hole(e):
|
||||
if e == fishing_mode.State.LOOK:
|
||||
if e == fishing_mode.State.LOOKING:
|
||||
self._hole_found_flag = True
|
||||
|
||||
fishing_mode.subscribers.append(found_hole)
|
||||
|
||||
t = 0
|
||||
while not self._hole_found_flag and t <= self.calibrate.time_to_reach_bottom / 3:
|
||||
while not self._hole_found_flag and t <= 1.25:
|
||||
mse.move(0, FullAuto.rotate_by)
|
||||
time.sleep(0.05)
|
||||
t += 0.05
|
||||
|
@ -49,11 +49,14 @@ class Player:
|
||||
self.hole_complete_flag = True
|
||||
|
||||
def _start_route(self):
|
||||
FullAuto.state = State.PLAYING
|
||||
timeline = _get_rec_file()
|
||||
if not timeline:
|
||||
logging.log("data not found, can't start")
|
||||
return
|
||||
|
||||
FullAuto.state = State.PLAYING
|
||||
logging.info("starting to move")
|
||||
|
||||
forward = True
|
||||
i = 0
|
||||
while self.start_moving_flag:
|
||||
|
51
fishy/engine/fullautofisher/qr_detection.py
Normal file
51
fishy/engine/fullautofisher/qr_detection.py
Normal file
@ -0,0 +1,51 @@
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import cv2
|
||||
|
||||
from fishy.helper.helper import get_documents
|
||||
|
||||
import numpy as np
|
||||
from pyzbar.pyzbar import decode
|
||||
|
||||
|
||||
def get_qr_location(og_img):
|
||||
"""
|
||||
code from https://stackoverflow.com/a/45770227/4512396
|
||||
"""
|
||||
gray = cv2.bilateralFilter(og_img, 11, 17, 17)
|
||||
kernel = np.ones((5, 5), np.uint8)
|
||||
erosion = cv2.erode(gray, kernel, iterations=2)
|
||||
kernel = np.ones((4, 4), np.uint8)
|
||||
img = cv2.dilate(erosion, kernel, iterations=2)
|
||||
|
||||
cnt, h = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
valid_crops = []
|
||||
for i in range(len(cnt)):
|
||||
area = cv2.contourArea(cnt[i])
|
||||
if 500 < area < 100000:
|
||||
mask = np.zeros_like(img)
|
||||
cv2.drawContours(mask, cnt, i, 255, -1)
|
||||
x, y, w, h = cv2.boundingRect(cnt[i])
|
||||
qr_result = decode(og_img[y:h + y, x:w + x])
|
||||
if qr_result:
|
||||
valid_crops.append(((x, y, x + w, y + h), area))
|
||||
|
||||
return min(valid_crops, key=lambda c: c[1])[0] if valid_crops else None
|
||||
|
||||
|
||||
# noinspection PyBroadException
|
||||
def get_values_from_image(img):
|
||||
try:
|
||||
for qr in decode(img):
|
||||
vals = qr.data.decode('utf-8').split(",")
|
||||
return float(vals[0]), float(vals[1]), float(vals[2])
|
||||
|
||||
logging.error("FishyQR not found, try restarting the engine")
|
||||
return None
|
||||
except Exception:
|
||||
logging.error("Couldn't read coods, make sure 'crop' calibration is correct")
|
||||
cv2.imwrite(os.path.join(get_documents(), "fishy_failed_reads", f"{datetime.now()}.jpg"), img)
|
||||
return None
|
@ -6,8 +6,8 @@ from tkinter.filedialog import asksaveasfile
|
||||
|
||||
from fishy.engine.fullautofisher.engine import FullAuto, State
|
||||
|
||||
from fishy.helper import hotkey
|
||||
from fishy.helper.hotkey import Key
|
||||
from fishy.helper.hotkey_process import HotKey
|
||||
|
||||
|
||||
class Recorder:
|
||||
@ -35,7 +35,8 @@ class Recorder:
|
||||
def _start_recording(self):
|
||||
FullAuto.state = State.RECORDING
|
||||
logging.info("starting, press f8 to mark hole")
|
||||
hotkey.set_hotkey(Recorder.mark_hole_key, self._mark_hole)
|
||||
hk = HotKey()
|
||||
hk.start_process(self._mark_hole)
|
||||
|
||||
self.timeline = []
|
||||
|
||||
@ -52,7 +53,7 @@ class Recorder:
|
||||
else:
|
||||
logging.warning("Took too much time to record")
|
||||
|
||||
hotkey.free_key(Recorder.mark_hole_key)
|
||||
hk.stop()
|
||||
|
||||
def func():
|
||||
_file = None
|
||||
|
@ -1,50 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from zipfile import ZipFile
|
||||
|
||||
import cv2
|
||||
|
||||
import pytesseract
|
||||
|
||||
from fishy.helper.downloader import download_file_from_google_drive
|
||||
from fishy.helper.helper import get_documents
|
||||
|
||||
directory = os.path.join(os.environ["APPDATA"], "Tesseract-OCR")
|
||||
|
||||
|
||||
def downlaoad_and_extract_tesseract():
|
||||
logging.info("Tesseract-OCR downlaoding, Please wait...")
|
||||
|
||||
f = tempfile.NamedTemporaryFile(delete=False)
|
||||
download_file_from_google_drive("16llzcBlaCsG9fm-rY2dD4Gvopnhm3XoE", f)
|
||||
f.close()
|
||||
|
||||
logging.info("Tesseract-OCR downloaded, now installing")
|
||||
|
||||
with ZipFile(f.name, 'r') as z:
|
||||
z.extractall(path=directory)
|
||||
|
||||
logging.info("Tesseract-OCR installed")
|
||||
|
||||
|
||||
def is_tesseract_installed():
|
||||
return os.path.exists(os.path.join(os.environ["APPDATA"], "Tesseract-OCR"))
|
||||
|
||||
|
||||
# noinspection PyBroadException
|
||||
def get_values_from_image(img):
|
||||
try:
|
||||
pytesseract.pytesseract.tesseract_cmd = directory + '/tesseract.exe'
|
||||
tessdata_dir_config = f'--tessdata-dir "{directory}" -c tessedit_char_whitelist=0123456789.'
|
||||
|
||||
text = pytesseract.image_to_string(img, lang="eng", config=tessdata_dir_config)
|
||||
text = text.replace(" ", "")
|
||||
vals = text.split(":")
|
||||
return float(vals[0]), float(vals[1]), float(vals[2])
|
||||
except Exception:
|
||||
logging.error("Couldn't read coods, make sure 'crop' calibration is correct")
|
||||
cv2.imwrite(os.path.join(get_documents(), "fishy_failed_reads", f"{datetime.now()}.jpg"), img)
|
||||
return None
|
@ -38,7 +38,8 @@ class SemiFisherEngine(IEngine):
|
||||
|
||||
if self.get_gui:
|
||||
logging.info("Starting the bot engine, look at the fishing hole to start fishing")
|
||||
Thread(target=self._wait_and_check).start()
|
||||
Thread(target=self._wait_and_check).start()
|
||||
|
||||
while self.start and WindowClient.running():
|
||||
capture = self.fishPixWindow.get_capture()
|
||||
|
||||
@ -59,7 +60,7 @@ class SemiFisherEngine(IEngine):
|
||||
def _wait_and_check(self):
|
||||
time.sleep(10)
|
||||
if not FishEvent.FishingStarted and self.start:
|
||||
logging.warn("Doesn't look like fishing has started \nCheck out #read-me-first on our discord channel to troubleshoot the issue")
|
||||
logging.warning("Doesn't look like fishing has started \nCheck out #read-me-first on our discord channel to troubleshoot the issue")
|
||||
|
||||
def show_pixel_vals(self):
|
||||
def show():
|
||||
|
@ -1,31 +0,0 @@
|
||||
import requests
|
||||
|
||||
|
||||
def download_file_from_google_drive(id, file):
|
||||
URL = "https://docs.google.com/uc?export=download"
|
||||
|
||||
session = requests.Session()
|
||||
|
||||
response = session.get(URL, params={'id': id}, stream=True)
|
||||
token = get_confirm_token(response)
|
||||
|
||||
if token:
|
||||
params = {'id': id, 'confirm': token}
|
||||
response = session.get(URL, params=params, stream=True)
|
||||
|
||||
save_response_content(response, file)
|
||||
|
||||
|
||||
def get_confirm_token(response):
|
||||
for key, value in response.cookies.items():
|
||||
if key.startswith('download_warning'):
|
||||
return value
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def save_response_content(response, f):
|
||||
CHUNK_SIZE = 32768
|
||||
for chunk in response.iter_content(CHUNK_SIZE):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
f.write(chunk)
|
46
fishy/helper/hotkey_process.py
Normal file
46
fishy/helper/hotkey_process.py
Normal file
@ -0,0 +1,46 @@
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import mouse
|
||||
from multiprocessing import Process, Queue
|
||||
|
||||
|
||||
def event_triggered(queue, e):
|
||||
if not (type(e) == mouse.ButtonEvent and e.event_type == "up" and e.button == "left"):
|
||||
return
|
||||
|
||||
# call the parent function here
|
||||
queue.put("left click")
|
||||
|
||||
|
||||
def run(inq, outq):
|
||||
mouse.hook(lambda e: event_triggered(outq, e))
|
||||
|
||||
stop = False
|
||||
while not stop:
|
||||
time.sleep(1)
|
||||
if inq.get() == "stop":
|
||||
stop = True
|
||||
|
||||
|
||||
class HotKey:
|
||||
def __init__(self):
|
||||
self.inq = Queue()
|
||||
self.outq = Queue()
|
||||
|
||||
self.process = Process(target=run, args=(self.inq, self.outq))
|
||||
|
||||
def event_loop(self, func):
|
||||
while True:
|
||||
msg = self.outq.get()
|
||||
if msg == "left click":
|
||||
func()
|
||||
|
||||
def start_process(self, func):
|
||||
self.process.start()
|
||||
Thread(target=self.event_loop, args=(func,)).start()
|
||||
|
||||
def stop(self):
|
||||
self.inq.put("stop")
|
||||
self.process.join()
|
||||
print("hotkey process ended")
|
@ -10,7 +10,8 @@ requests
|
||||
beautifulsoup4
|
||||
whatsmyip
|
||||
pynput
|
||||
pytesseract
|
||||
keyboard
|
||||
playsound
|
||||
event-scheduler
|
||||
event-scheduler
|
||||
pyzbar
|
||||
mouse
|
||||
|
Loading…
Reference in New Issue
Block a user