mirror of
https://github.com/fishyboteso/fishyboteso.git
synced 2024-08-30 18:32:13 +00:00
added help menu, removed pause and stop system, tweaked window, fishing_mode, fishing_event to work with new design, config now saves on other thread
This commit is contained in:
parent
9a6f46f02d
commit
faf2c62e9c
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,4 +7,5 @@ template_folder/
|
||||
checkpoint
|
||||
venv/
|
||||
*.pickle
|
||||
fishy.egg-info/
|
||||
fishy.egg-info/
|
||||
config.json
|
@ -1,97 +1,98 @@
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import cv2
|
||||
from docopt import docopt
|
||||
from pynput.keyboard import Listener
|
||||
import pywintypes
|
||||
|
||||
from fishy.systems import *
|
||||
import logging
|
||||
|
||||
from fishy.systems.config import Config
|
||||
from fishy.systems.gui import GUI, GUIStreamHandler, GUICallback
|
||||
|
||||
"""
|
||||
Start reading from `init.py`
|
||||
"""
|
||||
from fishy.systems.gui import GUI, GUIStreamHandler, GUIEvent, GUIFunction
|
||||
|
||||
|
||||
def on_release(key):
|
||||
"""
|
||||
Reads input, finds out the resultant action and performs it
|
||||
class Fishy:
|
||||
def __init__(self, gui_ref, gui_event_buffer):
|
||||
self.gui_events = gui_event_buffer
|
||||
self.start = False
|
||||
self.fishPixWindow = None
|
||||
self.fishy_thread = None
|
||||
self.gui = gui_ref
|
||||
|
||||
:param key: key pressed
|
||||
"""
|
||||
def start_fishing(self, ip: str, action_key: str, borderless: bool, collect_r: bool):
|
||||
"""
|
||||
Starts the fishing
|
||||
code explained in comments in detail
|
||||
"""
|
||||
|
||||
c = Control.find(key)
|
||||
if c is None:
|
||||
return
|
||||
if ip != "":
|
||||
net.initialize(ip)
|
||||
|
||||
if c[0] == Control.Keywords.StartPause:
|
||||
|
||||
if not G.pause:
|
||||
logging.info("PAUSED")
|
||||
G.pause = True
|
||||
# initialize widow
|
||||
try:
|
||||
Window.Init(borderless)
|
||||
except pywintypes.error:
|
||||
logging.info("Game window not found")
|
||||
self.start = False
|
||||
return
|
||||
|
||||
if PixelLoc.config():
|
||||
logging.info("STARTED")
|
||||
G.pause = False
|
||||
else:
|
||||
logging.info("addon properly not installed, if it is installed try restarting the game.")
|
||||
# initializes fishing modes and their callbacks
|
||||
FishingMode("hook", 0, HookEvent(collect_r))
|
||||
FishingMode("stick", 1, StickEvent())
|
||||
FishingMode("look", 2, LookEvent())
|
||||
FishingMode("idle", 3, IdleEvent(ip != ""))
|
||||
|
||||
elif c[0] == Control.Keywords.Debug:
|
||||
G.debug = not G.debug
|
||||
logging.info("Starting the bot engine, look at the fishing hole to start fishing")
|
||||
|
||||
elif c[0] == Control.Keywords.Stop:
|
||||
G.stop = True
|
||||
|
||||
elif c[0] == Control.Keywords.SwitchMode:
|
||||
Control.nextState()
|
||||
logging.info(Control.getControlHelp())
|
||||
|
||||
|
||||
def hsv2rgb(img):
|
||||
return cv2.cvtColor(img, cv2.COLOR_HSV2RGB)
|
||||
|
||||
|
||||
def startFishing(ip: str, action_key: str, borderless: bool):
|
||||
"""
|
||||
Starts the fishing
|
||||
code explained in comments in detail
|
||||
"""
|
||||
|
||||
if ip != "":
|
||||
net.initialize(ip)
|
||||
|
||||
# initializes fishing modes and their callbacks
|
||||
FishingMode("hook", 0, HookEvent())
|
||||
FishingMode("stick", 1, StickEvent())
|
||||
FishingMode("look", 2, LookEvent())
|
||||
FishingMode("idle", 3, IdleEvent(ip != ""))
|
||||
|
||||
logging.info("Starting the bot engine, look at the fishing hole to start fishing")
|
||||
|
||||
fishPixWindow = Window(color=cv2.COLOR_RGB2HSV)
|
||||
|
||||
# initialize widow
|
||||
Window.Init()
|
||||
with Listener(on_release=on_release):
|
||||
while not G.stop:
|
||||
# record the time to calculate time taken to execute one loop
|
||||
self.fishPixWindow = Window(color=cv2.COLOR_RGB2HSV)
|
||||
|
||||
# check for game window and stuff
|
||||
self.gui.call(GUIFunction.STARTED, (True,))
|
||||
while self.start:
|
||||
# Services to be ran in the start of the main loop
|
||||
Window.Loop()
|
||||
|
||||
# get the PixelLoc and find the color values, to give it to `FishingMode.Loop`
|
||||
fishPixWindow.crop = PixelLoc.val
|
||||
hueValue = fishPixWindow.getCapture()[0][0][0]
|
||||
FishingMode.Loop(hueValue, G.pause)
|
||||
|
||||
# if debug is on, show the color on the PixelLoc in a window and print the hue values of it
|
||||
if G.debug:
|
||||
fishPixWindow.show("pixloc", resize=200, func=hsv2rgb)
|
||||
logging.debug(str(FishingMode.CurrentMode.label) + ":" + str(fishPixWindow.getCapture()[0][0]))
|
||||
self.fishPixWindow.crop = PixelLoc.val
|
||||
hueValue = self.fishPixWindow.getCapture()[0][0][0]
|
||||
FishingMode.Loop(hueValue)
|
||||
|
||||
# Services to be ran in the end of the main loop
|
||||
Window.LoopEnd()
|
||||
logging.info("Fishing engine stopped")
|
||||
self.gui.call(GUIFunction.STARTED, (False,))
|
||||
|
||||
def start_event_handler(self):
|
||||
while True:
|
||||
while len(self.gui_events) > 0:
|
||||
event = self.gui_events.pop(0)
|
||||
|
||||
if event[0] == GUIEvent.START_BUTTON:
|
||||
self.start = not self.start
|
||||
if self.start:
|
||||
self.fishy_thread = Thread(target=self.start_fishing, args=(*event[1],))
|
||||
self.fishy_thread.start()
|
||||
elif event[0] == GUIEvent.CHECK_PIXELVAL:
|
||||
if self.start:
|
||||
self.show_pixel_vals()
|
||||
else:
|
||||
logging.debug("Start the engine first before running this command")
|
||||
elif event[0] == GUIEvent.QUIT:
|
||||
self.start = False
|
||||
return
|
||||
|
||||
def show_pixel_vals(self):
|
||||
def show():
|
||||
freq = 0.5
|
||||
t = 0
|
||||
while t < 10.0:
|
||||
t += freq
|
||||
logging.debug(str(FishingMode.CurrentMode.label) + ":" + str(self.fishPixWindow.getCapture()[0][0]))
|
||||
time.sleep(freq)
|
||||
|
||||
logging.debug("Will display pixel values for 10 seconds")
|
||||
time.sleep(5)
|
||||
Thread(target=show, args=()).start()
|
||||
|
||||
|
||||
def main():
|
||||
@ -99,12 +100,14 @@ def main():
|
||||
rootLogger = logging.getLogger('')
|
||||
rootLogger.setLevel(logging.DEBUG)
|
||||
|
||||
gui = GUI(Config(), lambda a, b: events_buffer.append((a, b)))
|
||||
gui = GUI(Config(), lambda a, b=None: events_buffer.append((a, b)))
|
||||
gui.start()
|
||||
|
||||
new_console = GUIStreamHandler(gui)
|
||||
rootLogger.addHandler(new_console)
|
||||
G.arguments = docopt(__doc__)
|
||||
|
||||
fishy = Fishy(gui, events_buffer)
|
||||
fishy.start_event_handler()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -1,4 +1,3 @@
|
||||
from fishy.systems.controls import Control
|
||||
from fishy.systems.fishing_event import HookEvent, StickEvent, LookEvent, IdleEvent
|
||||
from fishy.systems.fishing_mode import FishingMode
|
||||
from fishy.systems.globals import G
|
||||
|
@ -1 +0,0 @@
|
||||
{"dark_mode": true}
|
@ -1,5 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
from threading import Thread
|
||||
|
||||
filename = "config.json"
|
||||
|
||||
@ -14,10 +15,14 @@ class Config:
|
||||
return self.config_dict[key]
|
||||
return default
|
||||
|
||||
def set(self, key, value):
|
||||
def set(self, key, value, save=True):
|
||||
self.config_dict[key] = value
|
||||
self.save_config()
|
||||
if save:
|
||||
self.save_config()
|
||||
|
||||
def save_config(self):
|
||||
with open(filename, 'w') as f:
|
||||
f.write(json.dumps(self.config_dict))
|
||||
def save():
|
||||
with open(filename, 'w') as f:
|
||||
f.write(json.dumps(self.config_dict))
|
||||
|
||||
Thread(target=save).start()
|
||||
|
@ -1,89 +0,0 @@
|
||||
from enum import Enum
|
||||
|
||||
from pynput.keyboard import Key
|
||||
|
||||
class Control:
|
||||
"""
|
||||
Input system for the bot.
|
||||
|
||||
variables
|
||||
current: current mode of the input
|
||||
controls: Maps different key to different keyword depending on the current mode
|
||||
"""
|
||||
current = 0
|
||||
|
||||
class Keywords(Enum):
|
||||
"""
|
||||
Enums which define different functionality to be called
|
||||
used in `fishy.py` to run different code depending on which keyword is used
|
||||
"""
|
||||
SwitchMode = "switch mode"
|
||||
StartPause = "start/pause"
|
||||
Debug = "debug"
|
||||
Stop = "stop"
|
||||
ClearPrintOnce = "clear print once"
|
||||
|
||||
controls = [
|
||||
{
|
||||
"name": "SYSTEM",
|
||||
"controls": [
|
||||
[Keywords.SwitchMode, Key.f8],
|
||||
[Keywords.StartPause, Key.f9],
|
||||
[Keywords.Stop, Key.f11]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DEBUG",
|
||||
"controls": [
|
||||
[Keywords.SwitchMode, Key.f8],
|
||||
[Keywords.ClearPrintOnce, Key.f9],
|
||||
[Keywords.Debug, Key.f10],
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def getControlHelp():
|
||||
"""
|
||||
creates a control help string depending on the current mode
|
||||
:return: string
|
||||
"""
|
||||
s = "\n\nCurrent Mode: " + Control.get()["name"]+"\n"
|
||||
for c in Control.controls[Control.current]["controls"]:
|
||||
try:
|
||||
s += c[0].value + ": " + c[1].name + "\n"
|
||||
except AttributeError:
|
||||
s += c[0].value + ": " + c[1] + "\n"
|
||||
|
||||
return s
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
"""
|
||||
returns the controls current mode control array
|
||||
:return: control array
|
||||
"""
|
||||
return Control.controls[Control.current]
|
||||
|
||||
@staticmethod
|
||||
def find(key):
|
||||
"""
|
||||
converts key into the control keyword
|
||||
:param key: key pressed
|
||||
:return: corresponding keyword
|
||||
"""
|
||||
for c in Control.get()["controls"]:
|
||||
if key == c[1]:
|
||||
return c
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def nextState():
|
||||
"""
|
||||
Changes the current mode
|
||||
"""
|
||||
Control.current += 1
|
||||
|
||||
if Control.current >= len(Control.controls):
|
||||
Control.current = 0
|
@ -24,6 +24,9 @@ class FishEvent(ABC):
|
||||
|
||||
|
||||
class HookEvent(FishEvent):
|
||||
def __init__(self, collect_r: bool):
|
||||
self.collect_r = collect_r
|
||||
|
||||
def onEnterCallback(self, previousMode):
|
||||
"""
|
||||
called when the fish hook is detected
|
||||
@ -39,7 +42,7 @@ class HookEvent(FishEvent):
|
||||
round_float(timeToHook)) + " secs. " + "Total: " + str(G.totalFishCaught))
|
||||
pyautogui.press('e')
|
||||
|
||||
if G.arguments["--collect-r"]:
|
||||
if self.collect_r:
|
||||
time.sleep(0.1)
|
||||
pyautogui.press('r')
|
||||
time.sleep(0.1)
|
||||
|
@ -46,7 +46,7 @@ class FishingMode:
|
||||
return m
|
||||
|
||||
@staticmethod
|
||||
def Loop(hueValue, pause):
|
||||
def Loop(hueValue):
|
||||
"""
|
||||
Executed in the start of the main loop in fishy.py
|
||||
Changes modes, calls mode events (callbacks) when mode is changed
|
||||
@ -69,7 +69,7 @@ class FishingMode:
|
||||
if FishingMode.CurrentCount >= FishingMode.Threshold:
|
||||
FishingMode.CurrentMode = FishingMode.GetByLabel(current_label)
|
||||
|
||||
if not pause and FishingMode.CurrentMode != FishingMode.PrevMode and FishingMode.PrevMode is not None:
|
||||
if FishingMode.CurrentMode != FishingMode.PrevMode and FishingMode.PrevMode is not None:
|
||||
|
||||
if FishingMode.PrevMode.event is not None:
|
||||
FishingMode.PrevMode.event.onExitCallback(FishingMode.CurrentMode)
|
||||
|
@ -5,7 +5,5 @@ class G:
|
||||
fishCaught = 0
|
||||
totalFishCaught = 0
|
||||
stickInitTime = 0
|
||||
stop = False
|
||||
pause = True
|
||||
debug = False
|
||||
arguments = {}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import logging
|
||||
import time
|
||||
import webbrowser
|
||||
from enum import Enum
|
||||
from logging import StreamHandler
|
||||
from tkinter import *
|
||||
from tkinter.ttk import *
|
||||
from typing import Tuple, List, Callable
|
||||
from typing import Tuple, List, Callable, Optional
|
||||
|
||||
from ttkthemes import ThemedTk
|
||||
from waiting import wait
|
||||
import threading
|
||||
|
||||
from fishy.systems import helper
|
||||
from fishy.systems.config import Config
|
||||
|
||||
|
||||
@ -24,8 +26,9 @@ class GUIStreamHandler(StreamHandler):
|
||||
|
||||
|
||||
class GUIEvent(Enum):
|
||||
START_BUTTON = 0 # args: ip: str, action_key: str, fullscreen: bool
|
||||
START_BUTTON = 0 # args: ip: str, action_key: str, fullscreen: bool, collect_r: bool
|
||||
CHECK_PIXELVAL = 1
|
||||
QUIT = 2
|
||||
|
||||
|
||||
class GUIFunction(Enum):
|
||||
@ -35,7 +38,7 @@ class GUIFunction(Enum):
|
||||
|
||||
class GUI:
|
||||
|
||||
def __init__(self, config: Config, event_trigger: Callable[[GUIEvent, Tuple], None]):
|
||||
def __init__(self, config: Config, event_trigger: Callable[[GUIEvent, Optional[Tuple]], None]):
|
||||
self.config = config
|
||||
self.start_restart = False
|
||||
self.destroyed = True
|
||||
@ -49,6 +52,8 @@ class GUI:
|
||||
self.console = None
|
||||
self.start_button = None
|
||||
|
||||
self.thread = threading.Thread(target=self.create, args=())
|
||||
|
||||
def create(self):
|
||||
self.root = ThemedTk(theme="equilux", background=True)
|
||||
self.root.title("Fiishybot for Elder Scrolls Online")
|
||||
@ -65,9 +70,15 @@ class GUI:
|
||||
|
||||
debug_menu = Menu(menubar, tearoff=0)
|
||||
debug_menu.add_command(label="Check PixelVal",
|
||||
command=lambda: logging.error("Not Implemented"))
|
||||
command=lambda: self._event_trigger(GUIEvent.CHECK_PIXELVAL))
|
||||
debug_menu.add_command(label="Log Dump")
|
||||
menubar.add_cascade(label="Debug", menu=debug_menu, command=lambda: logging.error("Not Implemented"))
|
||||
menubar.add_cascade(label="Debug", menu=debug_menu)
|
||||
|
||||
help_menu = Menu(menubar, tearoff=0)
|
||||
help_menu.add_command(label="Need Help?", command=lambda: helper.open_web("http://discord.definex.in"))
|
||||
help_menu.add_command(label="Donate Us", command=lambda: helper.open_web("https://paypal.me/AdamSaudagar"))
|
||||
menubar.add_cascade(label="Help", menu=help_menu)
|
||||
|
||||
self.root.config(menu=menubar)
|
||||
# endregion
|
||||
|
||||
@ -85,10 +96,11 @@ class GUI:
|
||||
|
||||
Label(left_frame, text="IP").grid(row=0, column=0)
|
||||
ip = Entry(left_frame)
|
||||
ip.insert(0, self.config.get("ip", ""))
|
||||
ip.grid(row=0, column=1)
|
||||
|
||||
Label(left_frame, text="Fullscreen: ").grid(row=1, column=0, pady=(5, 5))
|
||||
borderless = Checkbutton(left_frame)
|
||||
borderless = Checkbutton(left_frame, variable=IntVar(value=1 if self.config.get("borderless", False) else 0))
|
||||
borderless.grid(row=1, column=1)
|
||||
|
||||
left_frame.grid(row=0, column=0)
|
||||
@ -98,19 +110,27 @@ class GUI:
|
||||
Label(right_frame, text="Action Key:").grid(row=0, column=0)
|
||||
action_key_entry = Entry(right_frame)
|
||||
action_key_entry.grid(row=0, column=1)
|
||||
action_key_entry.insert(0, "e")
|
||||
action_key_entry.insert(0, self.config.get("action_key", "e"))
|
||||
|
||||
Label(right_frame, text="Press start").grid(row=1, columnspan=2, pady=(5, 5))
|
||||
Label(right_frame, text="Collect R: ").grid(row=1, column=0, pady=(5, 5))
|
||||
collect_r = Checkbutton(right_frame, variable=IntVar(value=1 if self.config.get("collect_r", False) else 0))
|
||||
collect_r.grid(row=1, column=1)
|
||||
|
||||
right_frame.grid(row=0, column=1, padx=(50, 0))
|
||||
|
||||
controls_frame.pack()
|
||||
|
||||
self.start_button = Button(self.root, text="START", width=25)
|
||||
self.start_button["command"] = lambda: self._event_trigger(GUIEvent.START_BUTTON, (ip.get(),
|
||||
action_key_entry.get(),
|
||||
borderless.instate(
|
||||
['selected'])))
|
||||
self.start_button = Button(self.root, text="STOP" if self._bot_running else "START", width=25)
|
||||
|
||||
def start_button_callback():
|
||||
args = (ip.get(),
|
||||
action_key_entry.get(),
|
||||
borderless.instate(['selected']),
|
||||
collect_r.instate(['selected']))
|
||||
self._event_trigger(GUIEvent.START_BUTTON, args)
|
||||
self._save_config(*args)
|
||||
|
||||
self.start_button["command"] = start_button_callback
|
||||
self.start_button.pack(pady=(15, 15))
|
||||
# endregion
|
||||
|
||||
@ -129,6 +149,7 @@ class GUI:
|
||||
self.start_restart = False
|
||||
self.create()
|
||||
if self.destroyed:
|
||||
self._event_trigger(GUIEvent.QUIT)
|
||||
break
|
||||
time.sleep(0.01)
|
||||
|
||||
@ -138,8 +159,9 @@ class GUI:
|
||||
|
||||
if func[0] == GUIFunction.LOG:
|
||||
self._write_to_console(func[1][0])
|
||||
elif func[1] == GUIFunction.STARTED:
|
||||
self.start_button["text"] = "STOP" if func[1][0] else "START"
|
||||
elif func[0] == GUIFunction.STARTED:
|
||||
self._bot_running = func[1][0]
|
||||
self.start_button["text"] = "STOP" if self._bot_running else "START"
|
||||
|
||||
def _apply_theme(self, dark):
|
||||
self.root["theme"] = "equilux" if dark else "breeze"
|
||||
@ -164,25 +186,15 @@ class GUI:
|
||||
self.console.see("end") # scroll to bottom
|
||||
self.console['state'] = 'disabled'
|
||||
|
||||
def _save_config(self, ip, action_key, borderless, collect_r):
|
||||
self.config.set("ip", ip, False)
|
||||
self.config.set("action_key", action_key, False)
|
||||
self.config.set("borderless", borderless, False)
|
||||
self.config.set("collect_r", collect_r, False)
|
||||
self.config.save_config()
|
||||
|
||||
def start(self):
|
||||
threading.Thread(target=self.create, args=()).start()
|
||||
self.thread.start()
|
||||
|
||||
def call(self, gui_func: GUIFunction, args):
|
||||
def call(self, gui_func: GUIFunction, args: Tuple = None):
|
||||
self._function_queue.append((gui_func, args))
|
||||
|
||||
# def start(ip, actionkey, fullscreen):
|
||||
# logging.info(f"{ip}, {actionkey}, {fullscreen}")
|
||||
#
|
||||
#
|
||||
# def main():
|
||||
# config = Config()
|
||||
# gui = GUI(config=config, gui_callback=GUICallback(start_callback=start))
|
||||
# gui.start()
|
||||
# wait(lambda: not gui.destroyed)
|
||||
# while not gui.destroyed:
|
||||
# gui.writeToLog("yo")
|
||||
# time.sleep(1)
|
||||
#
|
||||
#
|
||||
# if __name__ == '__main__':
|
||||
# main()
|
||||
|
@ -1,5 +1,8 @@
|
||||
import logging
|
||||
import sys
|
||||
import webbrowser
|
||||
from decimal import Decimal
|
||||
from threading import Thread
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@ -38,3 +41,8 @@ def enable_full_array_printing():
|
||||
(summarized arrays are printed by default)
|
||||
"""
|
||||
np.set_printoptions(threshold=sys.maxsize)
|
||||
|
||||
|
||||
def open_web(website):
|
||||
logging.debug("opening web, please wait...")
|
||||
Thread(target=lambda: webbrowser.open(website, new=2)).start()
|
||||
|
@ -35,23 +35,19 @@ class Window:
|
||||
self.scale = scale
|
||||
|
||||
@staticmethod
|
||||
def Init():
|
||||
def Init(borderless: bool):
|
||||
"""
|
||||
Executed once before the main loop,
|
||||
Finds the game window, and calculates the offset to remove the title bar
|
||||
"""
|
||||
try:
|
||||
Window.hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
||||
rect = win32gui.GetWindowRect(Window.hwnd)
|
||||
clientRect = win32gui.GetClientRect(Window.hwnd)
|
||||
Window.windowOffset = math.floor(((rect[2] - rect[0]) - clientRect[2]) / 2)
|
||||
Window.titleOffset = ((rect[3] - rect[1]) - clientRect[3]) - Window.windowOffset
|
||||
if G.arguments["--borderless"]:
|
||||
Window.titleOffset = 0
|
||||
Window.hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
||||
rect = win32gui.GetWindowRect(Window.hwnd)
|
||||
clientRect = win32gui.GetClientRect(Window.hwnd)
|
||||
Window.windowOffset = math.floor(((rect[2] - rect[0]) - clientRect[2]) / 2)
|
||||
Window.titleOffset = ((rect[3] - rect[1]) - clientRect[3]) - Window.windowOffset
|
||||
if borderless:
|
||||
Window.titleOffset = 0
|
||||
|
||||
except pywintypes.error:
|
||||
logging.info("Game window not found")
|
||||
quit()
|
||||
|
||||
@staticmethod
|
||||
def Loop():
|
||||
|
Loading…
Reference in New Issue
Block a user