Merge remote-tracking branch 'origin/master' into feature/fullauto

# Conflicts:
#	fishy/engine/fullautofisher/controls.py
#	fishy/engine/fullautofisher/engine.py
#	fishy/engine/fullautofisher/mode/calibrator.py
#	fishy/engine/fullautofisher/mode/recorder.py
#	fishy/engine/fullautofisher/player.py
#	fishy/engine/semifisher/fishing_event.py
#	fishy/gui/config_top.py
This commit is contained in:
Adam Saudagar 2021-05-12 20:44:55 +05:30
commit 8f7b6b71c4
30 changed files with 378 additions and 285 deletions

View File

@ -3,16 +3,17 @@ import logging
import os
import sys
import traceback
import win32con
import win32gui
import fishy
from fishy import web, helper, gui
from fishy import gui, helper, web
from fishy.constants import chalutier, lam2
from fishy.engine.common.event_handler import EngineEventHandler
from fishy.gui import GUI, splash, update_dialog
from fishy.helper import hotkey
from fishy.helper.config import config
from fishy.constants import chalutier, lam2
def check_window_name(title):
@ -42,8 +43,8 @@ def initialize(window_to_hide):
try:
if helper.upgrade_avail() and not config.get("dont_ask_update", False):
cv,hv = helper.versions()
update_now, dont_ask_update = update_dialog.start(cv,hv)
cv, hv = helper.versions()
update_now, dont_ask_update = update_dialog.start(cv, hv)
if dont_ask_update:
config.set("dont_ask_update", dont_ask_update)
else:

View File

@ -1,5 +1,5 @@
apiversion = 2
chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/619b4ab0b8ff91746afda855542e886d27b7a794/Chalutier_1.1.2.zip", 112)
chalutier = ("Chalutier", "https://www.esoui.com/downloads/dl2934/Chalutier_1.1.4.zip", 114)
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)

View File

@ -5,7 +5,7 @@ import cv2
import imutils
from fishy.engine.common import window_server
from fishy.engine.common.window_server import WindowServer, Status
from fishy.engine.common.window_server import Status, WindowServer
from fishy.helper import helper

View File

@ -1,16 +1,14 @@
import logging
import math
from enum import Enum
from threading import Thread
import cv2
import math
import numpy as np
import pywintypes
import win32gui
from win32api import GetSystemMetrics
import numpy as np
from PIL import ImageGrab
from win32api import GetSystemMetrics
from fishy.helper.config import config
@ -60,8 +58,6 @@ def loop():
temp_screen = np.array(ImageGrab.grab(bbox=bbox))
temp_screen = cv2.cvtColor(temp_screen, cv2.COLOR_BGR2RGB)
rect = win32gui.GetWindowRect(WindowServer.hwnd)
crop = (
rect[0] + WindowServer.windowOffset, rect[1] + WindowServer.titleOffset, rect[2] - WindowServer.windowOffset,

View File

@ -1,29 +1,29 @@
import math
import logging
import math
import time
import traceback
from threading import Thread
import cv2
import logging
import time
from pynput import keyboard, mouse
from fishy.constants import libgps, fishyqr, lam2
from fishy.constants import fishyqr, lam2, libgps
from fishy.engine import SemiFisherEngine
from fishy.engine.common.IEngine import IEngine
from fishy.engine.common.window import WindowClient
from fishy.engine.fullautofisher.mode.calibrator import Calibrator
from fishy.engine.fullautofisher.mode.imode import FullAutoMode
from fishy.engine.fullautofisher.mode.player import Player
from fishy.engine.fullautofisher.mode.recorder import Recorder
from fishy.engine.fullautofisher.qr_detection import get_values_from_image, get_qr_location
from fishy.engine.fullautofisher.qr_detection import (get_qr_location,
get_values_from_image)
from fishy.engine.semifisher import fishing_event, fishing_mode
from fishy.engine.semifisher.fishing_mode import FishingMode
from fishy.engine import SemiFisherEngine
from fishy.engine.common.window import WindowClient
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.helper import sign, log_raise, wait_until, is_eso_active
from fishy.helper import helper, hotkey
from fishy.helper.config import config
from fishy.helper.helper import log_raise, wait_until, is_eso_active
from fishy.helper.helper import sign
mse = mouse.Controller()
kb = keyboard.Controller()
@ -42,7 +42,6 @@ class FullAuto(IEngine):
rotate_by = 30
def __init__(self, gui_ref):
from fishy.engine.fullautofisher.mode.calibrator import Calibrator
from fishy.engine.fullautofisher.test import Test
super().__init__(gui_ref)

View File

@ -1,16 +1,17 @@
import math
import logging
import math
import time
import typing
import cv2
import numpy as np
import typing
if typing.TYPE_CHECKING:
from fishy.engine.fullautofisher.engine import FullAuto
from pynput import keyboard, mouse
from fishy.engine.fullautofisher.mode.imode import IMode
from pynput import keyboard, mouse
from fishy.helper.config import config
mse = mouse.Controller()
@ -125,4 +126,3 @@ class Calibrator(IMode):
self._rotate_calibrate()
config.set("calibrate", False)
logging.info("calibration done")

View File

@ -2,20 +2,17 @@ import logging
import os
import pickle
import time
from pprint import pprint
import typing
from tkinter.filedialog import asksaveasfile
import typing
from fishy.helper.config import config
from playsound import playsound
from fishy import helper
from fishy.helper.config import config
if typing.TYPE_CHECKING:
from fishy.engine.fullautofisher.engine import FullAuto
from fishy.engine.fullautofisher.mode.imode import IMode
from fishy.helper.hotkey import Key
from fishy.helper.hotkey_process import HotKey
@ -71,4 +68,3 @@ class Recorder(IMode):
config.set("full_auto_rec_file", file.name)
logging.info(f"saved {os.path.basename(file.name)} recording, and loaded it in player")
file.close()

View File

@ -3,12 +3,11 @@ import os
from datetime import datetime
import cv2
from fishy.helper.helper import get_documents
import numpy as np
from pyzbar.pyzbar import decode
from fishy.helper.helper import get_documents
def get_qr_location(og_img):
"""

View File

@ -1,20 +1,19 @@
import logging
import time
import typing
from threading import Thread
from typing import Callable
from typing import Optional
from typing import Callable, Optional
import cv2
import logging
from fishy.engine.semifisher.fishing_event import FishEvent
from fishy.engine.common.window import WindowClient
from fishy.engine.semifisher.fishing_mode import FishingMode
from playsound import playsound
from fishy.engine.common.IEngine import IEngine
from fishy.engine.semifisher import fishing_mode, fishing_event
from fishy.engine.common.window import WindowClient
from fishy.engine.semifisher import fishing_event, fishing_mode
from fishy.engine.semifisher.fishing_event import FishEvent
from fishy.engine.semifisher.fishing_mode import Colors, FishingMode
from fishy.engine.semifisher.pixel_loc import PixelLoc
from fishy.helper import helper
from fishy.helper.luaparser import sv_color_extract
if typing.TYPE_CHECKING:
from fishy.gui import GUI
@ -31,11 +30,13 @@ class SemiFisherEngine(IEngine):
code explained in comments in detail
"""
fishing_event.init()
self.fishPixWindow = WindowClient(color=cv2.COLOR_RGB2HSV)
self.fishPixWindow = WindowClient()
# check for game window and stuff
self.gui.bot_started(True)
sv_color_extract(Colors)
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()
@ -51,6 +52,7 @@ class SemiFisherEngine(IEngine):
self.fishPixWindow.crop = PixelLoc.val
fishing_mode.loop(capture[0][0])
time.sleep(0.1)
logging.info("Fishing engine stopped")
self.gui.bot_started(False)
@ -60,7 +62,8 @@ class SemiFisherEngine(IEngine):
def _wait_and_check(self):
time.sleep(10)
if not FishEvent.FishingStarted and self.start:
logging.warning("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 \n"
"Check out #read-me-first on our discord channel to troubleshoot the issue")
def show_pixel_vals(self):
def show():
@ -80,6 +83,9 @@ class SemiFisherEngine(IEngine):
if self.start:
self.thread = Thread(target=self.run)
self.thread.start()
playsound(helper.manifest_file("beep.wav"), False)
else:
helper.playsound_multiple(helper.manifest_file("beep.wav"))
if __name__ == '__main__':
@ -87,4 +93,3 @@ if __name__ == '__main__':
# noinspection PyTypeChecker
fisher = SemiFisherEngine(None)
fisher.toggle_start()

View File

@ -4,21 +4,17 @@ Defines different fishing modes (states) which acts as state for state machine
also implements callbacks which is called when states are changed
"""
import logging
import random
import time
from fishy.engine.semifisher import fishing_mode
import keyboard
from playsound import playsound
from fishy import web
from fishy.engine.semifisher.fishing_mode import State, FishingMode
from fishy.engine.semifisher import fishing_mode
from fishy.engine.semifisher.fishing_mode import FishingMode, State
from fishy.helper import helper
import keyboard
from win32gui import GetWindowText, GetForegroundWindow
from fishy.helper.config import config
import random
from fishy.helper.helper import is_eso_active
@ -34,15 +30,15 @@ class FishEvent:
# initialize these
action_key = 'e'
collect_key = 'r'
collect_key = 'r'
sound = False
def _fishing_sleep(waittime, lower_limit_ms = 16, upper_limit_ms = 2500):
def _fishing_sleep(waittime, lower_limit_ms=16, upper_limit_ms=2500):
reaction = 0.0
if FishEvent.jitter and upper_limit_ms > lower_limit_ms:
reaction = float( random.randrange(lower_limit_ms, upper_limit_ms) )/1000.0
max_wait_t = waittime+reaction if waittime+reaction <= 2.5 else 2.5
reaction = float(random.randrange(lower_limit_ms, upper_limit_ms)) / 1000.0
max_wait_t = waittime + reaction if waittime + reaction <= 2.5 else 2.5
time.sleep(max_wait_t)
@ -57,7 +53,7 @@ def if_eso_is_focused(func):
def _sound_and_send_fishy_data():
if FishEvent.fishCaught > 0:
web.send_hole_deplete(FishEvent.fishCaught, time.time() - FishEvent.hole_start_time, FishEvent.fish_times)
web.send_fish_caught(FishEvent.fishCaught, time.time() - FishEvent.hole_start_time, FishEvent.fish_times)
FishEvent.fishCaught = 0
if FishEvent.sound:
@ -89,22 +85,25 @@ def subscribe():
def fisher_callback(event: State):
callbacks_map = {
State.IDLE: on_idle,
State.LOOKAWAY: on_lookaway,
State.LOOKAWAY: on_idle,
State.LOOKING: on_looking,
State.DEPLETED: on_depleted,
State.NOBAIT: on_nobait,
State.NOBAIT: lambda: on_user_interact("You need to equip bait!"),
State.FISHING: on_fishing,
State.REELIN: on_reelin,
State.LOOT: on_loot,
State.INVFULL: on_invfull,
State.FIGHT: on_fight,
State.DEAD: on_dead
State.INVFULL: lambda: on_user_interact("Inventory is full!"),
State.FIGHT: lambda: on_user_interact("Character is FIGHTING!"),
State.DEAD: lambda: on_user_interact("Character died!")
}
try:
callbacks_map[event]()
FishEvent.previousState = event
except KeyError as ex:
pass
except KeyError:
logging.error("KeyError: State " + str(event) + " is not known.")
except TypeError:
logging.error("TypeError when reading state: " + str(event))
def on_idle():
@ -118,10 +117,6 @@ def on_depleted():
_sound_and_send_fishy_data()
def on_lookaway():
return
@if_eso_is_focused
def on_looking():
"""
@ -131,11 +126,13 @@ def on_looking():
keyboard.press_and_release(FishEvent.action_key)
def on_nobait():
msg = "No bait equipped!"
def on_user_interact(msg):
logging.info(msg)
web.send_notification(msg)
if FishEvent.sound:
playsound(helper.manifest_file("sound.mp3"), False)
def on_fishing():
FishEvent.stickInitTime = time.time()
@ -169,21 +166,3 @@ def on_loot():
_fishing_sleep(0)
keyboard.press_and_release(FishEvent.collect_key)
_fishing_sleep(0)
def on_invfull():
msg = "Inventory full!"
logging.info(msg)
web.send_notification(msg)
def on_fight():
msg = "FIGHTING!"
logging.info(msg)
web.send_notification(msg)
def on_dead():
msg = "Character is dead!"
logging.info(msg)
web.send_notification(msg)

View File

@ -1,21 +1,36 @@
import time
from enum import Enum
subscribers = []
class State(Enum):
IDLE = [ 0, 0, 255]
LOOKAWAY = [150, 255, 76]
LOOKING = [100, 255, 101]
DEPLETED = [ 30, 255, 76]
NOBAIT = [ 96, 255, 255]
FISHING = [ 18, 165, 213]
REELIN = [ 60, 255, 204]
LOOT = [ 0, 255, 204]
INVFULL = [ 0, 255, 51]
FIGHT = [120, 255, 204]
DEAD = [ 0, 0, 51]
IDLE = 0
LOOKAWAY = 1
LOOKING = 2
DEPLETED = 3
NOBAIT = 5
FISHING = 6
REELIN = 7
LOOT = 8
INVFULL = 9
FIGHT = 14
DEAD = 15
Colors = {
State.IDLE : [255, 255, 255],
State.LOOKAWAY : [ 76, 0, 76],
State.LOOKING : [101, 69, 0],
State.DEPLETED : [ 0, 76, 76],
State.NOBAIT : [255, 204, 0],
State.FISHING : [ 75, 156, 213],
State.REELIN : [ 0, 204, 0],
State.LOOT : [ 0, 0, 204],
State.INVFULL : [ 0, 0, 51],
State.FIGHT : [204, 0, 0],
State.DEAD : [ 51, 51, 51]
}
def _notify(event):
for subscriber in subscribers:
@ -27,16 +42,16 @@ class FishingMode:
PrevMode = State.IDLE
def loop(hsv):
def loop(rgb):
"""
Executed in the start of the main loop in fishy.py
Changes modes, calls mode events (callbacks) when mode is changed
:param hsv: hsv read by the bot
:param rgb: rgb read by the bot
"""
FishingMode.CurrentMode = State.IDLE
for s in State:
if all(hsv == s.value):
if all(rgb == Colors[s]):
FishingMode.CurrentMode = s
if FishingMode.CurrentMode != FishingMode.PrevMode:

View File

@ -1,5 +1,7 @@
import logging
import os
import tkinter as tk
import tkinter.ttk as ttk
import typing
from tkinter.filedialog import askopenfilename
@ -8,10 +10,7 @@ from fishy.engine.fullautofisher.mode.imode import FullAutoMode
from fishy.helper import helper
from fishy import web
from tkinter import *
from tkinter.ttk import *
from fishy.helper import helper
from fishy.helper.config import config
from fishy.helper.popup import PopUp
@ -21,7 +20,7 @@ if typing.TYPE_CHECKING:
def start_fullfisher_config(gui: 'GUI'):
top = PopUp(helper.empty_function, gui._root, background=gui._root["background"])
controls_frame = Frame(top)
controls_frame = ttk.Frame(top)
top.title("Config")
def file_name():
@ -48,19 +47,19 @@ def start_fullfisher_config(gui: 'GUI'):
def mode_command():
config.set("full_auto_mode", mode_var.get())
file_name_label = StringVar(value=file_name())
mode_var = IntVar(value=config.get("full_auto_mode", 0))
file_name_label = tk.StringVar(value=file_name())
mode_var = tk.IntVar(value=config.get("full_auto_mode", 0))
Button(controls_frame, text="Calibrate", command=start_calibrate).grid(row=0, column=0, columnspan=2, pady=(5, 5))
tk.Button(controls_frame, text="Calibrate", command=start_calibrate).grid(row=0, column=0, columnspan=2, pady=(5, 5))
Label(controls_frame, text="Mode: ").grid(row=1, column=0, pady=(5, 0))
Radiobutton(controls_frame, text="Player", variable=mode_var, value=FullAutoMode.Player.value, command=mode_command).grid(row=1, column=1, sticky="w")
Radiobutton(controls_frame, text="Recorder", variable=mode_var, value=FullAutoMode.Recorder.value, command=mode_command).grid(row=2, column=1, sticky="w", pady=(0, 5))
tk.Label(controls_frame, text="Mode: ").grid(row=1, column=0, pady=(5, 0))
tk.Radiobutton(controls_frame, text="Player", variable=mode_var, value=FullAutoMode.Player.value, command=mode_command).grid(row=1, column=1, sticky="w")
tk.Radiobutton(controls_frame, text="Recorder", variable=mode_var, value=FullAutoMode.Recorder.value, command=mode_command).grid(row=2, column=1, sticky="w", pady=(0, 5))
Button(controls_frame, text="Select fishy file", command=select_file).grid(row=3, column=0, columnspan=2, pady=(5, 0))
Label(controls_frame, textvariable=file_name_label).grid(row=4, column=0, columnspan=2, pady=(0, 5))
tk.Button(controls_frame, text="Select fishy file", command=select_file).grid(row=3, column=0, columnspan=2, pady=(5, 0))
tk.Label(controls_frame, textvariable=file_name_label).grid(row=4, column=0, columnspan=2, pady=(0, 5))
Label(controls_frame, text="Use semi-fisher config for rest").grid(row=5, column=0, columnspan=2, pady=(15, 5))
tk.Label(controls_frame, text="Use semi-fisher config for rest").grid(row=5, column=0, columnspan=2, pady=(15, 5))
controls_frame.pack(padx=(5, 5), pady=(5, 5))
top.start()
@ -84,46 +83,46 @@ def start_semifisher_config(gui: 'GUI'):
gui._notify.set(1)
def del_entry_key(event):
event.widget.delete(0,"end")
event.widget.delete(0, "end")
event.widget.insert(0, str(event.char))
top = PopUp(save, gui._root, background=gui._root["background"])
controls_frame = Frame(top)
controls_frame = ttk.Frame(top)
top.title("Config")
Label(controls_frame, text="Notification:").grid(row=0, column=0)
ttk.Label(controls_frame, text="Notification:").grid(row=0, column=0)
gui._notify = IntVar(0)
gui._notify_check = Checkbutton(controls_frame, command=toggle_sub, variable=gui._notify)
gui._notify = tk.IntVar(0)
gui._notify_check = ttk.Checkbutton(controls_frame, command=toggle_sub, variable=gui._notify)
gui._notify_check.grid(row=0, column=1)
gui._notify_check['state'] = DISABLED
gui._notify_check['state'] = tk.DISABLED
is_subbed = web.is_subbed()
if is_subbed[1]:
gui._notify_check['state'] = NORMAL
gui._notify_check['state'] = tk.NORMAL
gui._notify.set(is_subbed[0])
Label(controls_frame, text="Fullscreen: ").grid(row=1, column=0, pady=(5, 5))
borderless = Checkbutton(controls_frame, var=BooleanVar(value=config.get("borderless")))
ttk.Label(controls_frame, text="Fullscreen: ").grid(row=1, column=0, pady=(5, 5))
borderless = ttk.Checkbutton(controls_frame, var=tk.BooleanVar(value=config.get("borderless")))
borderless.grid(row=1, column=1)
Label(controls_frame, text="Action Key:").grid(row=2, column=0)
action_key_entry = Entry(controls_frame, justify=CENTER)
ttk.Label(controls_frame, text="Action Key:").grid(row=2, column=0)
action_key_entry = ttk.Entry(controls_frame, justify=tk.CENTER)
action_key_entry.grid(row=2, column=1)
action_key_entry.insert(0, config.get("action_key", "e"))
action_key_entry.bind("<KeyRelease>", del_entry_key)
Label(controls_frame, text="Looting Key:").grid(row=4, column=0, pady=(5, 5))
collect_key_entry = Entry(controls_frame, justify=CENTER)
ttk.Label(controls_frame, text="Looting Key:").grid(row=4, column=0, pady=(5, 5))
collect_key_entry = ttk.Entry(controls_frame, justify=tk.CENTER)
collect_key_entry.grid(row=4, column=1, pady=(5, 5))
collect_key_entry.insert(0, config.get("collect_key", "r"))
collect_key_entry.bind("<KeyRelease>", del_entry_key)
Label(controls_frame, text="Sound Notification: ").grid(row=5, column=0, pady=(5, 5))
sound = Checkbutton(controls_frame, var=BooleanVar(value=config.get("sound_notification")))
ttk.Label(controls_frame, text="Sound Notification: ").grid(row=5, column=0, pady=(5, 5))
sound = ttk.Checkbutton(controls_frame, var=tk.BooleanVar(value=config.get("sound_notification")))
sound.grid(row=5, column=1)
Label(controls_frame, text="Human-Like Delay: ").grid(row=6, column=0, pady=(5, 5))
jitter = Checkbutton(controls_frame, var=BooleanVar(value=config.get("jitter")))
ttk.Label(controls_frame, text="Human-Like Delay: ").grid(row=6, column=0, pady=(5, 5))
jitter = ttk.Checkbutton(controls_frame, var=tk.BooleanVar(value=config.get("jitter")))
jitter.grid(row=6, column=1)
controls_frame.pack(padx=(5, 5), pady=(5, 5))

View File

@ -1,15 +1,11 @@
import time
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import *
import tkinter as tk
import tkinter.ttk as ttk
import typing
from fishy.helper import helper
from fishy.libs.tkhtmlview import HTMLLabel
from fishy.web import web
from fishy.libs.tkhtmlview import HTMLLabel
from ..helper.config import config
if typing.TYPE_CHECKING:
@ -34,14 +30,14 @@ def discord_login(gui: 'GUI'):
code = int(login_code.get()) if login_code.get().isdigit() else 0
if web.login(config.get("uid"), code):
gui.login.set(1)
messagebox.showinfo("Note!", "Logged in successfuly!")
tk.messagebox.showinfo("Note!", "Login successful!")
quit_top()
else:
messagebox.showerror("Error", "Logged wasn't successful")
tk.messagebox.showerror("Error", "Login was not successful!")
top_running = [True]
top = Toplevel(background=gui._root["background"])
top = tk.Toplevel(background=gui._root["background"])
top.minsize(width=300, height=300)
top.title("Notification Setup")
@ -58,8 +54,8 @@ def discord_login(gui: 'GUI'):
html_label.pack(pady=(20, 5))
html_label.fit_height()
login_code = Entry(top, justify=CENTER, font="Calibri 15")
login_code.pack(padx=(15, 15), expand=True, fill=BOTH)
login_code = ttk.Entry(top, justify=tk.CENTER, font="Calibri 15")
login_code.pack(padx=(15, 15), expand=True, fill=tk.BOTH)
html_label = HTMLLabel(top,
html=f'<div style="color: {gui._console["fg"]}; text-align: center">'
@ -69,7 +65,7 @@ def discord_login(gui: 'GUI'):
html_label.pack(pady=(5, 5))
html_label.fit_height()
Button(top, text="REGISTER", command=check).pack(pady=(5, 20))
ttk.Button(top, text="REGISTER", command=check).pack(pady=(5, 20))
top.protocol("WM_DELETE_WINDOW", quit_top)
top.grab_set()

View File

@ -1,6 +1,5 @@
from tkinter import messagebox
import typing
from tkinter import messagebox
from fishy.helper.config import config
@ -16,7 +15,7 @@ class GUIFuncsMock:
...
def bot_started(self, started):
...
...
def quit(self):
...

View File

@ -1,20 +1,21 @@
import logging
import uuid
from tkinter import OptionMenu, Button, IntVar
from typing import List, Callable, Optional, Dict, Any
import queue
import threading
import tkinter as tk
import uuid
from typing import Any, Callable, Dict, Optional
from fishy.web import web
from ttkthemes import ThemedTk
from fishy.engine.common.event_handler import EngineEventHandler, IEngineHandler
from fishy.gui import config_top
from fishy.gui.funcs import GUIFuncs
from . import main_gui
from .log_config import GUIStreamHandler
from fishy.web import web
from ..helper.config import config
from ..helper.helper import wait_until
from . import main_gui
from .log_config import GUIStreamHandler
class GUI:
@ -35,8 +36,8 @@ class GUI:
self._console = None
self._start_button = None
self._notify_check = None
self._engine_select: Optional[OptionMenu] = None
self._config_button: Optional[Button] = None
self._engine_select: Optional[tk.OptionMenu] = None
self._config_button: Optional[tk.Button] = None
self._engine_var = None
self._thread = threading.Thread(target=self.create, args=())

View File

@ -1,6 +1,5 @@
from logging import StreamHandler
import typing
from logging import StreamHandler
if typing.TYPE_CHECKING:
from . import GUI

View File

@ -1,20 +1,19 @@
import logging
import time
from tkinter import *
from tkinter.ttk import *
import tkinter as tk
import tkinter.ttk as ttk
import typing
from fishy.web import web
from ttkthemes import ThemedTk
from fishy import helper
import typing
from fishy.helper import hotkey
from .discord_login import discord_login
from fishy.web import web
from ..constants import chalutier, lam2
from ..helper.config import config
from ..helper.hotkey import Key
from ..constants import chalutier, lam2
from .discord_login import discord_login
if typing.TYPE_CHECKING:
from . import GUI
@ -36,14 +35,14 @@ def _create(gui: 'GUI'):
gui._root.iconbitmap(helper.manifest_file('icon.ico'))
# region menu
menubar = Menu(gui._root)
menubar = tk.Menu(gui._root)
filemenu = Menu(menubar, tearoff=0)
filemenu = tk.Menu(menubar, tearoff=0)
login = web.is_logged_in()
gui.login = IntVar()
gui.login = tk.IntVar()
gui.login.set(1 if login > 0 else 0)
state = DISABLED if login == -1 else ACTIVE
state = tk.DISABLED if login == -1 else tk.ACTIVE
filemenu.add_checkbutton(label="Login", command=lambda: discord_login(gui), variable=gui.login, state=state)
filemenu.add_command(label="Create Shortcut", command=lambda: helper.create_shortcut(False))
# filemenu.add_command(label="Create Anti-Ghost Shortcut", command=lambda: helper.create_shortcut(True))
@ -52,7 +51,7 @@ def _create(gui: 'GUI'):
config.set("dark_mode", not config.get("dark_mode", True))
gui._start_restart = True
dark_mode_var = IntVar()
dark_mode_var = tk.IntVar()
dark_mode_var.set(int(config.get('dark_mode', True)))
filemenu.add_checkbutton(label="Dark Mode", command=_toggle_mode,
variable=dark_mode_var)
@ -72,11 +71,11 @@ def _create(gui: 'GUI'):
filemenu.add_command(label=chaEntry, command=installer)
menubar.add_cascade(label="Options", menu=filemenu)
debug_menu = Menu(menubar, tearoff=0)
debug_menu = tk.Menu(menubar, tearoff=0)
debug_menu.add_command(label="Check PixelVal",
command=lambda: gui.engine.check_pixel_val())
debug_var = IntVar()
debug_var = tk.IntVar()
debug_var.set(int(config.get('debug', False)))
def keep_console():
@ -87,7 +86,7 @@ def _create(gui: 'GUI'):
debug_menu.add_command(label="Restart", command=helper.restart)
menubar.add_cascade(label="Debug", menu=debug_menu)
help_menu = Menu(menubar, tearoff=0)
help_menu = tk.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", command=lambda: helper.open_web("https://paypal.me/AdamSaudagar"))
menubar.add_cascade(label="Help", menu=help_menu)
@ -96,29 +95,29 @@ def _create(gui: 'GUI'):
# endregion
# region console
gui._console = Text(gui._root, state='disabled', wrap='none', background="#707070", fg="#ffffff")
gui._console.pack(fill=BOTH, expand=True, pady=(15, 15), padx=(10, 10))
gui._console.mark_set("sentinel", INSERT)
gui._console.config(state=DISABLED)
gui._console = tk.Text(gui._root, state='disabled', wrap='none', background="#707070", fg="#ffffff")
gui._console.pack(fill=tk.BOTH, expand=True, pady=(15, 15), padx=(10, 10))
gui._console.mark_set("sentinel", tk.INSERT)
gui._console.config(state=tk.DISABLED)
# endregion
# region controls
start_frame = Frame(gui._root)
start_frame = ttk.Frame(gui._root)
gui._engine_var = StringVar(start_frame)
gui._engine_var = tk.StringVar(start_frame)
labels = list(engines.keys())
last_started = config.get("last_started", labels[0])
gui._engine_select = OptionMenu(start_frame, gui._engine_var, last_started, *labels)
gui._engine_select.pack(side=LEFT)
gui._engine_select = ttk.OptionMenu(start_frame, gui._engine_var, last_started, *labels)
gui._engine_select.pack(side=tk.LEFT)
gui._config_button = Button(start_frame, text="", width=0, command=lambda: engines[gui._engine_var.get()][0]())
gui._config_button.pack(side=RIGHT)
gui._config_button = ttk.Button(start_frame, text="", width=0, command=lambda: engines[gui._engine_var.get()][0]())
gui._config_button.pack(side=tk.RIGHT)
gui._start_button = Button(start_frame, text=gui._get_start_stop_text(), width=25,
command=gui.funcs.start_engine)
gui._start_button.pack(side=RIGHT)
gui._start_button = ttk.Button(start_frame, text=gui._get_start_stop_text(), width=25,
command=gui.funcs.start_engine)
gui._start_button.pack(side=tk.RIGHT)
start_frame.pack(padx=(10, 10), pady=(5, 15), fill=X)
start_frame.pack(padx=(10, 10), pady=(5, 15), fill=tk.X)
# endregion
_apply_theme(gui)
@ -132,7 +131,7 @@ def _create(gui: 'GUI'):
# noinspection PyProtectedMember
def set_destroy():
if gui._bot_running:
if not messagebox.askyesno(title="Quit?", message="Bot engine running. Quit Anyway?"):
if not tk.messagebox.askyesno(title="Quit?", message="Bot engine running. Quit Anyway?"):
return
config.set("win_loc", gui._root.geometry())

View File

@ -1,15 +1,16 @@
import time
import tkinter as tk
from multiprocessing import Process
from tkinter import *
from PIL import Image, ImageTk
from fishy.helper.config import config
from fishy.helper import helper
from fishy.helper.config import config
def show(win_loc):
dim=(300,200)
top = Tk()
dim = (300, 200)
top = tk.Tk()
top.overrideredirect(True)
top.lift()
@ -18,17 +19,17 @@ def show(win_loc):
top.resizable(False, False)
top.iconbitmap(helper.manifest_file('icon.ico'))
canvas = Canvas(top, width=dim[0], height=dim[1], bg='white')
canvas = tk.Canvas(top, width=dim[0], height=dim[1], bg='white')
canvas.pack()
top.image = Image.open(helper.manifest_file('fishybot_logo.png')).resize(dim)
top.image = ImageTk.PhotoImage(top.image)
canvas.create_image(0, 0, anchor=NW, image=top.image)
canvas.create_image(0, 0, anchor=tk.NW, image=top.image)
# Position splash at the center of the main window
default_loc = (str(top.winfo_reqwidth())+"+"+str(top.winfo_reqheight())+"+"+"0"+"0")
default_loc = (str(top.winfo_reqwidth()) + "+" + str(top.winfo_reqheight()) + "+" + "0" + "0")
loc = (win_loc or default_loc).split("+")[1:]
top.geometry("{}x{}+{}+{}".format(dim[0], dim[1], int(loc[0])+int(dim[0]/2), int(loc[1])+int(dim[1]/2)))
top.geometry("{}x{}+{}+{}".format(dim[0], dim[1], int(loc[0]) + int(dim[0] / 2), int(loc[1]) + int(dim[1] / 2)))
top.update()
time.sleep(3)

View File

@ -1,14 +1,14 @@
import webbrowser
from tkinter import *
from tkinter.ttk import *
import re
import tkinter as tk
import tkinter.ttk as ttk
import webbrowser
from PIL import Image, ImageTk
from fishy import helper, web
from fishy.helper.config import config
hyperlinkPattern = re.compile(r'\[(?P<title>.*?)\]\((?P<address>.*?)\)')
hyperlinkPattern = re.compile(r'\[(?P<title>.*?)\]\((?P<address>.*?)\)')
def check_eula():
@ -25,42 +25,42 @@ def _run_terms_window():
root.destroy()
def disable_enable_button():
accept_button.config(state=NORMAL if check_value.get() else DISABLED)
accept_button.config(state=tk.NORMAL if check_value.get() else tk.DISABLED)
root = Tk()
root = tk.Tk()
message = f'I agree to the [Terms of Service and Privacy Policy]({web.get_terms_page()})'
root.title("EULA")
root.resizable(False, False)
root.iconbitmap(helper.manifest_file('icon.ico'))
f = Frame(root)
canvas = Canvas(f, width=300, height=200)
f = ttk.Frame(root)
canvas = tk.Canvas(f, width=300, height=200)
canvas.pack()
root.image = Image.open(helper.manifest_file('fishybot_logo.png')).resize((300, 200))
root.image = ImageTk.PhotoImage(root.image)
canvas.create_image(0, 0, anchor=NW, image=root.image)
canvas.create_image(0, 0, anchor=tk.NW, image=root.image)
check_value = IntVar(0)
check_value = tk.IntVar(0)
g1 = Frame(f)
Checkbutton(g1, command=disable_enable_button, variable=check_value).pack(side=LEFT)
text = Text(g1, width=len(hyperlinkPattern.sub(r'\g<title>', message)),
height=1, borderwidth=0, highlightthickness=0)
g1 = ttk.Frame(f)
ttk.Checkbutton(g1, command=disable_enable_button, variable=check_value).pack(side=tk.LEFT)
text = tk.Text(g1, width=len(hyperlinkPattern.sub(r'\g<title>', message)),
height=1, borderwidth=0, highlightthickness=0)
text["background"] = root["background"]
_format_hyper_link(text, message)
text.config(state=DISABLED)
text.pack(side=LEFT)
text.config(state=tk.DISABLED)
text.pack(side=tk.LEFT)
g1.pack()
f.pack(padx=(10, 10), pady=(20, 20))
g2 = Frame(f)
accept_button = Button(g2, text="Accept",
command=accept)
g2 = ttk.Frame(f)
accept_button = ttk.Button(g2, text="Accept",
command=accept)
accept_button.grid(row=0, column=0)
Button(g2, text="Deny",
command=lambda: root.destroy()).grid(row=0, column=1)
ttk.Button(g2, text="Deny",
command=lambda: root.destroy()).grid(row=0, column=1)
g2.pack(pady=(5, 0))
disable_enable_button()

View File

@ -1,35 +1,38 @@
from multiprocessing import Process, Manager
from tkinter import *
import time
import tkinter as tk
from multiprocessing import Manager, Process
from fishy import helper
def show(currentversion, newversion, returns):
top = Tk()
top = tk.Tk()
top.title("A wild fishy update appeared!")
top.iconbitmap(helper.manifest_file('icon.ico'))
dialogLabel = Label(top, text="There is a new fishy update available ("+currentversion+"->"+newversion+"). Do you want to update now?")
dialogLabel = tk.Label(top, text="There is a new fishy update available (" +
currentversion + "->" + newversion + "). Do you want to update now?")
dialogLabel.grid(row=0, columnspan=2, padx=5, pady=5)
cbVar = IntVar()
dialogCheckbutton = Checkbutton(top, text="don't ask again", variable=cbVar)
cbVar = tk.IntVar()
dialogCheckbutton = tk.Checkbutton(top, text="don't ask again", variable=cbVar)
dialogCheckbutton.grid(row=1, columnspan=2, padx=5, pady=0)
top.update()
buttonWidth = int(dialogLabel.winfo_width()/2)-20
buttonWidth = int(dialogLabel.winfo_width() / 2) - 20
def _clickYes():
returns[0],returns[1]=True, False
returns[0], returns[1] = True, False
top.destroy()
def _clickNo():
returns[0],returns[1]=False, bool(cbVar.get())
returns[0], returns[1] = False, bool(cbVar.get())
top.destroy()
pixelVirtual = PhotoImage(width=1, height=1) # trick to use buttonWidth as pixels, not #symbols
dialogBtnNo = Button(top, text="No " + str(chr(10005)), fg='red4', command=_clickNo, image=pixelVirtual, width=buttonWidth, compound="c")
pixelVirtual = tk.PhotoImage(width=1, height=1) # trick to use buttonWidth as pixels, not #symbols
dialogBtnNo = tk.Button(top, text="No " + str(chr(10005)), fg='red4', command=_clickNo, image=pixelVirtual,
width=buttonWidth, compound="c")
dialogBtnNo.grid(row=2, column=0, padx=5, pady=5)
dialogBtnYes = Button(top, text="Yes " + str(chr(10003)), fg='green', command=_clickYes, image=pixelVirtual, width=buttonWidth, compound="c")
dialogBtnYes = tk.Button(top, text="Yes " + str(chr(10003)), fg='green', command=_clickYes, image=pixelVirtual,
width=buttonWidth, compound="c")
dialogBtnYes.grid(row=2, column=1, padx=5, pady=5)
dialogBtnYes.focus_set()

View File

@ -1,4 +1,9 @@
from .auto_update import auto_upgrade, upgrade_avail, versions
from .config import Config
from .helper import open_web, initialize_uid, install_thread_excepthook, unhandled_exception_logging, manifest_file, \
create_shortcut_first, addon_exists, get_addonversion, install_addon, remove_addon, restart, create_shortcut, not_implemented, update
from .helper import (addon_exists, create_shortcut, create_shortcut_first,
get_addonversion, get_savedvarsdir, initialize_uid,
install_addon, install_thread_excepthook, manifest_file,
not_implemented, open_web, playsound_multiple,
remove_addon, restart, unhandled_exception_logging,
update)
from .luaparser import sv_color_extract

View File

@ -11,9 +11,9 @@ from os import execl
from bs4 import BeautifulSoup
def _hr_version(v):
return '.'.join([str(x) for x in v])
#return str(v[0])+"."+str(v[1])+"."+str(v[2])
def _normalize_version(v):
@ -75,6 +75,7 @@ def _get_current_version():
index = "https://pypi.python.org/simple"
pkg = "fishy"
def versions():
return _hr_version(_get_current_version()), _hr_version(_get_highest_version(index, pkg))

View File

@ -3,11 +3,10 @@ config.py
Saves configuration in file as json file
"""
import json
import logging
import os
# path to save the configuration file
from typing import Optional
from event_scheduler import EventScheduler

View File

@ -7,24 +7,36 @@ import threading
import time
import traceback
import webbrowser
import requests
from hashlib import md5
from io import BytesIO
from threading import Thread
from uuid import uuid1
from zipfile import ZipFile
from uuid import uuid1
from hashlib import md5
import requests
import winshell
from playsound import playsound
from win32com.client import Dispatch
from win32comext.shell import shell, shellcon
from win32gui import GetForegroundWindow, GetWindowText
import fishy
import winshell
from fishy import web
def playsound_multiple(path, count=2):
if count < 1:
logging.debug("Please don't make me beep 0 times or less.")
return
def _ps_m():
for i in range(count - 1):
playsound(path, True)
playsound(path, False)
Thread(target=_ps_m).start()
def not_implemented():
logging.error("Not Implemented")
@ -147,11 +159,18 @@ def create_shortcut(anti_ghosting: bool):
logging.error("Couldn't create shortcut")
def get_savedvarsdir():
# noinspection PyUnresolvedReferences
from win32com.shell import shell, shellcon
documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
return os.path.join(documents, "Elder Scrolls Online", "live", "SavedVariables")
def get_addondir():
# noinspection PyUnresolvedReferences
from win32com.shell import shell, shellcon
documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
return os.path.join(documents, "Elder Scrolls Online", "live", "Addons")
return os.path.join(documents, "Elder Scrolls Online", "live", "Addons")
def addon_exists(name, url=None, v=None):
@ -177,21 +196,22 @@ def install_addon(name, url, v=None):
r = requests.get(url, stream=True)
z = ZipFile(BytesIO(r.content))
z.extractall(path=get_addondir())
logging.info("Add-On "+name+" installed successfully!\nPlease make sure to enable \"Allow outdated addons\" in ESO")
logging.info("Add-On " + name +
" installed successfully!\nPlease make sure to enable \"Allow outdated addons\" in ESO")
return 0
except Exception as ex:
logging.error("Could not install Add-On "+name+", try doing it manually")
except Exception:
logging.error("Could not install Add-On " + name + ", try doing it manually")
return 1
def remove_addon(name, url=None, v=None):
try:
shutil.rmtree(os.path.join(get_addondir(), name))
logging.info("Add-On "+name+" removed!")
logging.info("Add-On " + name + " removed!")
except FileNotFoundError:
pass
except PermissionError as ex:
logging.error("Fishy has no permission to remove "+name+" Add-On")
except PermissionError:
logging.error("Fishy has no permission to remove " + name + " Add-On")
return 1
return 0

View File

@ -1,11 +1,8 @@
from enum import Enum
from threading import Thread
from typing import Dict, Callable, Optional
from typing import Callable, Dict, Optional
import keyboard
from playsound import playsound
from fishy.helper import helper
class Key(Enum):
@ -27,7 +24,6 @@ def _get_callback(k):
if not _hotkeys[k]:
return
playsound(helper.manifest_file("beep.wav"), False)
Thread(target=_hotkeys[k]).start()
return callback

View File

@ -1,8 +1,8 @@
import time
from multiprocessing import Process, Queue
from threading import Thread
import mouse
from multiprocessing import Process, Queue
def event_triggered(queue, e):

81
fishy/helper/luaparser.py Normal file
View File

@ -0,0 +1,81 @@
import logging
import os
from math import floor
from .helper import get_savedvarsdir
def _sv_parser(path):
try:
with open(path, "r") as f:
lua = f.read()
"""
bring lua saved-var file into a useable format:
- one line per expression (add \n where needed)
- remove all redundant characters
- make lowercase, split into list of expressions
- remove empty expressions
EXPRESSIONS: A) List-Start "name=", B) Variable assignment "name=val", C) List End "}"
"""
subs = ((",", "\n"), ("{", "{\n"), ("}", "}\n"),
("{", ""), (",", ""), ("[", ""), ("]", ""), ('"', ""), (" ", ""))
for old, new in subs:
lua = lua.replace(old, new)
lua = lua.lower().split("\n")
lua = [expression for expression in lua if expression]
"""
the lua saved-var file is parsed to a tree of dicts
each line represents either one node in the tree or the end of a subtree
the last symbol of each line decides the type of the node (branch vertex or leaf)
"""
stack = []
root = (dict(), "root")
stack.append(root)
for line in lua:
if line == "":
break
if line[-1] == '=': # subtree start
t = dict()
tname = line.split("=")[0]
stack.append((t, tname))
elif line[-1] == '}': # subtree end
t = stack.pop()
tp = stack.pop()
tp[0][t[1]] = t[0]
stack.append(tp)
else: # new element in tree
name, val = line.split("=")
t = stack.pop()
t[0][name] = val
stack.append(t)
return root[0]
except Exception as ex:
logging.error("Error: '" + str(ex) + "' occured, while parsing ESO variables.")
return None
def sv_color_extract(Colors):
root = _sv_parser(os.path.join(get_savedvarsdir(), "Chalutier.lua"))
if root is None:
return Colors
for i in range(4):
name, root = root.popitem()
colors = []
for i in root["colors"]:
"""
ingame representation of colors range from 0 to 1 in float
these values are scaled by 255
"""
rgb = [
floor(float(root["colors"][i]["r"]) * 255),
floor(float(root["colors"][i]["g"]) * 255),
floor(float(root["colors"][i]["b"]) * 255)
]
colors.append(rgb)
for i, c in enumerate(Colors):
Colors[c] = colors[i]
return Colors

View File

@ -1,2 +1,3 @@
from .urls import get_notification_page, get_terms_page
from .web import register_user, send_notification, send_hole_deplete, is_subbed, unsub, get_session, sub
from .web import (get_session, is_subbed, register_user, send_fish_caught,
send_notification, sub, unsub)

View File

@ -3,10 +3,11 @@ from whatsmyip.ip import get_ip
from whatsmyip.providers import GoogleDnsProvider
from fishy import helper
from ..constants import apiversion
from ..helper.config import config
from . import urls
from .decorators import fallback, uses_session
from ..helper.config import config
from ..constants import apiversion
_session_id = None
@ -16,7 +17,7 @@ def is_logged_in():
if config.get("uid") is None:
return -1
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.get(urls.discord, params=body)
logged_in = response.json()["discord_login"]
return 1 if logged_in else 0
@ -24,7 +25,7 @@ def is_logged_in():
@fallback(False)
def login(uid, login_code):
body = {"uid": uid, "login_code": login_code, "apiversion":apiversion}
body = {"uid": uid, "login_code": login_code, "apiversion": apiversion}
reponse = requests.post(urls.discord, json=body)
result = reponse.json()
@ -36,7 +37,7 @@ def login(uid, login_code):
@fallback(False)
def logout():
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
reponse = requests.delete(urls.discord, json=body)
result = reponse.json()
return result["success"]
@ -56,13 +57,13 @@ def send_notification(message):
if not is_subbed()[0]:
return False
body = {"uid": config.get("uid"), "message": message, "apiversion":apiversion}
body = {"uid": config.get("uid"), "message": message, "apiversion": apiversion}
requests.post(urls.notify, json=body)
@uses_session
@fallback(None)
def send_hole_deplete(fish_caught, hole_time, fish_times):
def send_fish_caught(fish_caught, hole_time, fish_times):
hole_data = {
"fish_caught": fish_caught,
"hole_time": hole_time,
@ -70,13 +71,13 @@ def send_hole_deplete(fish_caught, hole_time, fish_times):
"session": get_session()
}
body = {"uid": config.get("uid"), "hole_data": hole_data, "apiversion":apiversion}
body = {"uid": config.get("uid"), "hole_data": hole_data, "apiversion": apiversion}
requests.post(urls.hole_depleted, json=body)
@fallback(False)
def sub():
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.post(urls.subscription, json=body)
result = response.json()
return result["success"]
@ -93,7 +94,7 @@ def is_subbed():
if config.get("uid") is None:
return False, False
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.get(urls.subscription, params=body)
if response.status_code != 200:
@ -105,7 +106,7 @@ def is_subbed():
@fallback(None)
def unsub():
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.delete(urls.subscription, json=body)
result = response.json()
return result["success"]
@ -118,7 +119,7 @@ def get_session(lazy=True):
if lazy and _session_id is not None:
return _session_id
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.post(urls.session, params=body)
if response.status_code == 405:
@ -132,7 +133,7 @@ def get_session(lazy=True):
@fallback(False)
def has_beta():
body = {"uid": config.get("uid"), "apiversion":apiversion}
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.get(urls.beta, params=body)
result = response.json()

View File

@ -4,14 +4,16 @@ https://packaging.python.org/guides/distributing-packages-using-setuptools/
https://github.com/pypa/sampleproject
"""
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
from os import path
# io.open is needed for projects that support Python 2.7
# It ensures open() defaults to text mode with universal newlines,
# and accepts an argument to specify the text encoding
# Python 3 only projects can skip this import
from io import open
from os import path
# Always prefer setuptools over distutils
from setuptools import find_packages, setup
from fishy import __version__
here = path.abspath(path.dirname(__file__))
@ -211,4 +213,4 @@ setup(
},
include_package_data=True
)
)