0.3.3 reworked notifications again (now uses discord chat)

- fixed ui scaling issues
- now uses callables for thread comunication instead of enums (removed comms.py altogether)
- added options to run using test server
This commit is contained in:
DESKTOP-JVKHS7I\Adam 2020-05-19 08:41:58 +05:30
parent b1ee1d1188
commit 3e96fbed2c
18 changed files with 184 additions and 126 deletions

View File

@ -1,6 +1,5 @@
@echo off
rd build dist /s /q
call activate ./venv
python ./setup.py sdist
python ./setup.py bdist_wheel
PAUSE

View File

@ -1,2 +1,2 @@
from fishy.__main__ import main
__version__ = "0.3.2"
__version__ = "0.3.3"

View File

@ -49,18 +49,18 @@ def main():
window_to_hide = win32gui.GetForegroundWindow()
c = Config()
events_buffer = []
if not gui.check_eula(c):
return
gui_window = GUI(c, lambda a, b=None: events_buffer.append((a, b)))
bot = Engine(c, lambda: gui_window)
gui_window = GUI(c, lambda: bot)
gui_window.start()
logging.info(f"Fishybot v{fishy.__version__}")
initialize(c, window_to_hide)
bot = Engine(gui_window, events_buffer, c)
bot.start_event_handler()

View File

@ -1,3 +1,2 @@
from .gui import GUI
from .terms_gui import check_eula
from .comms import GUIFunction, GUIEvent

View File

@ -1,46 +0,0 @@
import threading
from enum import Enum
from tkinter import *
from tkinter import messagebox, filedialog
from .log_config import _write_to_console
import typing
if typing.TYPE_CHECKING:
from . import GUI
class GUIEvent(Enum):
START_BUTTON = 0 # args: ip: str, action_key: str, fullscreen: bool, collect_r: bool
CHECK_PIXELVAL = 1
QUIT = 2
class GUIFunction(Enum):
LOG = 0 # args: str
STARTED = 1 # args: bool
ASK_DIRECTORY = 2 # callback: callable
SHOW_ERROR = 3
SET_NOTIFY = 4
def _clear_function_queue(gui: 'GUI'):
while len(gui._function_queue) > 0:
func = gui._function_queue.pop(0)
if func[0] == GUIFunction.LOG:
_write_to_console(gui, func[1][0])
elif func[0] == GUIFunction.STARTED:
gui._bot_running = func[1][0]
gui._start_button["text"] = "STOP" if gui._bot_running else "START"
elif func[0] == GUIFunction.ASK_DIRECTORY:
messagebox.showinfo("Directory?", func[1][1])
path = filedialog.askdirectory()
if path != '':
threading.Thread(target=func[1][0], args=(path,)).start()
elif func[0] == GUIFunction.SHOW_ERROR:
messagebox.showerror("ERROR", func[1][0])
elif func[0] == GUIFunction.SET_NOTIFY:
gui._notify.set(func[1][0])
if func[1][1]:
gui._notify_check['state'] = NORMAL

23
fishy/gui/funcs.py Normal file
View File

@ -0,0 +1,23 @@
from tkinter import messagebox, NORMAL
# noinspection PyProtectedMember
class GUIFuncs:
def __init__(self, gui):
self.gui = gui
def set_notify(self, flag):
def func():
self.gui._notify_check['state'] = NORMAL
self.gui._notify.set(flag)
self.gui.call_in_thread(func)
def show_error(self, error):
self.gui.call_in_thread(lambda: messagebox.showerror("ERROR", error))
def bot_started(self, started):
def func():
self.gui._bot_running = started
self.gui._start_button["text"] = "STOP" if self.gui._bot_running else "START"
self.gui.call_in_thread(func)

View File

@ -1,25 +1,27 @@
import logging
from typing import Tuple, List, Callable, Optional
from typing import List, Callable
import threading
from fishy.gui.funcs import GUIFuncs
from fishy.tech import Engine
from . import main_gui
from .comms import GUIEvent, GUIFunction
from .log_config import GUIStreamHandler
from fishy.helper import Config
class GUI:
def __init__(self, config: Config, event_trigger: Callable[[GUIEvent, Optional[Tuple]], None]):
def __init__(self, config: Config, get_engine: Callable[[], Engine]):
"""
:param config: used to get and set configuration settings
:param event_trigger: used to communicate with other threads
"""
self.funcs = GUIFuncs(self)
self.get_engine = get_engine
self._config = config
self._start_restart = False
self._destroyed = True
self._log_strings = []
self._function_queue: List[Tuple[GUIFunction, Tuple]] = []
self._event_trigger = event_trigger
self._function_queue: List[Callable] = []
self._bot_running = False
# UI items
@ -37,11 +39,20 @@ class GUI:
new_console = GUIStreamHandler(self)
root_logger.addHandler(new_console)
@property
def engine(self):
return self.get_engine().funcs
def create(self):
main_gui._create(self)
def start(self):
self._thread.start()
def call(self, gui_func, args):
self._function_queue.append((gui_func, args))
def _clear_function_queue(self):
while len(self._function_queue) > 0:
func = self._function_queue.pop(0)
func()
def call_in_thread(self, func: Callable):
self._function_queue.append(func)

View File

@ -12,9 +12,8 @@ class GUIStreamHandler(StreamHandler):
self.gui = gui
def emit(self, record):
from .comms import GUIFunction
msg = self.format(record)
self.gui.call(GUIFunction.LOG, (msg,))
self.gui.call_in_thread(lambda: _write_to_console(self.gui, msg))
def _write_to_console(root: 'GUI', msg):

View File

@ -7,7 +7,6 @@ from ttkthemes import ThemedTk
from fishy import helper, web
from .comms import GUIEvent, GUIFunction, _clear_function_queue
from .notification import _give_notification_link
import typing
@ -16,7 +15,7 @@ if typing.TYPE_CHECKING:
def _apply_theme(gui: 'GUI'):
dark = gui._config.get("dark", True)
dark = gui._config.get("dark_mode", True)
gui._root["theme"] = "equilux" if dark else "breeze"
gui._console["background"] = "#707070" if dark else "#ffffff"
gui._console["fg"] = "#ffffff" if dark else "#000000"
@ -25,7 +24,6 @@ def _apply_theme(gui: 'GUI'):
def _create(gui: 'GUI'):
gui._root = ThemedTk(theme="equilux", background=True)
gui._root.title("Fishybot for Elder Scrolls Online")
gui._root.geometry('650x550')
gui._root.iconbitmap(helper.manifest_file('icon.ico'))
@ -48,7 +46,7 @@ def _create(gui: 'GUI'):
debug_menu = Menu(menubar, tearoff=0)
debug_menu.add_command(label="Check PixelVal",
command=lambda: gui._event_trigger(GUIEvent.CHECK_PIXELVAL, ()))
command=lambda: gui.engine.check_pixel_val())
debug_var = IntVar()
debug_var.set(int(gui._config.get('debug', False)))
@ -93,7 +91,8 @@ def _create(gui: 'GUI'):
def update_notify_check():
is_subbed = web.is_subbed(gui._config.get('uid'))
gui.call(GUIFunction.SET_NOTIFY, (int(is_subbed[0]), is_subbed[1]))
if is_subbed[1]:
gui.funcs.set_notify(is_subbed[0])
threading.Thread(target=update_notify_check).start()
@ -121,10 +120,9 @@ def _create(gui: 'GUI'):
gui._start_button = Button(gui._root, text="STOP" if gui._bot_running else "START", width=25)
def start_button_callback():
args = (action_key_entry.get(),
gui.engine.start_button_pressed(action_key_entry.get(),
borderless.instate(['selected']),
collect_r.instate(['selected']))
gui._event_trigger(GUIEvent.START_BUTTON, args)
gui._config.set("action_key", action_key_entry.get(), False)
gui._config.set("borderless", borderless.instate(['selected']), False)
@ -147,13 +145,13 @@ def _create(gui: 'GUI'):
while True:
gui._root.update()
_clear_function_queue(gui)
gui._clear_function_queue()
if gui._start_restart:
gui._root.destroy()
gui._root.quit()
gui._start_restart = False
gui.create()
if gui._destroyed:
gui._event_trigger(GUIEvent.QUIT, ())
gui.engine.quit()
break
time.sleep(0.01)

View File

@ -1,9 +1,8 @@
import os
import tempfile
import time
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import *
import pyqrcode
from tkhtmlview import HTMLLabel
from fishy import web
import typing
@ -12,6 +11,7 @@ if typing.TYPE_CHECKING:
from . import GUI
# noinspection PyProtectedMember
def _give_notification_link(gui: 'GUI'):
if web.is_subbed(gui._config.get("uid"))[0]:
web.unsub(gui._config.get("uid"))
@ -25,37 +25,49 @@ def _give_notification_link(gui: 'GUI'):
top_running[0] = False
def check():
if web.sub(gui._config.get("uid"), discord_name.get()):
if web.is_subbed(gui._config.get("uid"), False)[0]:
gui._notify.set(1)
web.send_notification(gui._config.get("uid"), "Sending a test notification :D")
messagebox.showinfo("Note!", "Notification configured successfully!")
quit_top()
else:
messagebox.showerror("Error", "Subscription wasn't successful")
print("got to {}".format(web.get_notification_page(gui._config.get("uid"))))
qrcode = pyqrcode.create(web.get_notification_page(gui._config.get("uid")))
t = os.path.join(tempfile.gettempdir(), "fishyqr.png")
qrcode.png(t, scale=8)
top_running = [True]
top = Toplevel(background=gui._root["background"])
top.minsize(width=500, height=500)
top.minsize(width=300, height=300)
top.title("Notification Setup")
Label(top, text="Step 1.").pack(pady=(5, 5))
Label(top, text="Scan the QR Code on your Phone and press \"Enable Notification\"").pack(pady=(5, 5))
canvas = Canvas(top, width=qrcode.get_png_size(8), height=qrcode.get_png_size(8))
canvas.pack(pady=(5, 5))
Label(top, text="Step 2.").pack(pady=(5, 5))
Button(top, text="Check", command=check).pack(pady=(5, 5))
html_label = HTMLLabel(top,
html=f'<div style="color: {gui._console["fg"]}; text-align: center">'
f'<p><span style="font-size:20px">Step 1.</span><br/>'
f'Join <a href="https://discord.definex.in/">Discord server</a></p>'
f'<p><span style="font-size:20px">Step 2.</span><br/>'
f'Enter username (ex. Fishy#1234)'
f'</div>', background=gui._root["background"])
image = PhotoImage(file=t)
canvas.create_image(0, 0, anchor=NW, image=image)
html_label.pack(pady=(20, 5))
html_label.fit_height()
discord_name = Entry(top, justify=CENTER, font="Calibri 15")
discord_name.pack(padx=(15, 15), expand=True, fill=BOTH)
html_label = HTMLLabel(top,
html=f'<div style="color: {gui._console["fg"]}; text-align: center">'
f'<p><span style="font-size:20px">Step 3.</span><br/>'
f'Install Discord App on your phone</p>'
f'<p><span style="font-size:20px">Step 4.</span><br/></p>'
f'</div>', background=gui._root["background"])
html_label.pack(pady=(5, 5))
html_label.fit_height()
Button(top, text="REGISTER", command=check).pack(pady=(5, 20))
top.protocol("WM_DELETE_WINDOW", quit_top)
top.grab_set()
while top_running[0]:
top.update()
time.sleep(0.01)
top.grab_release()

View File

@ -1 +1,2 @@
from .engine import Engine
from .funcs import EngineFuncs

View File

@ -1,4 +1,6 @@
import time
import typing
from collections import Callable
from threading import Thread
import cv2
@ -6,34 +8,44 @@ import logging
import pywintypes
from fishy.gui import GUIFunction, GUIEvent
from fishy.tech.funcs import EngineFuncs
from . import fishing_event
from .fishing_event import HookEvent, StickEvent, LookEvent, IdleEvent
from .fishing_mode import FishingMode
from .pixel_loc import PixelLoc
from .window import Window
if typing.TYPE_CHECKING:
from fishy.gui import GUI
def _wait_and_check(gui):
time.sleep(10)
if not fishing_event._FishingStarted:
gui.call(GUIFunction.SHOW_ERROR, ("Doesn't look like fishing has started\n\n"
gui.show_error("Doesn't look like fishing has started\n\n"
"Make sure ProvisionsChalutier addon is visible clearly on top "
"left corner of the screen, either,\n"
"1) Outdated addons are disabled\n"
"2) Other addons are overlapping ProvisionsChalutier\n"
"3) Post processing (re shader) is on\n\n"
"If fixing those doesnt work, try running the bot as admin",))
"If fixing those doesnt work, try running the bot as admin")
class Engine:
def __init__(self, gui_ref, gui_event_buffer, config):
self.gui_events = gui_event_buffer
def __init__(self, config, gui_ref: 'Callable[[], GUI]'):
self.funcs = EngineFuncs(self)
self.get_gui = gui_ref
self.start = False
self.fishPixWindow = None
self.fishy_thread = None
self.gui = gui_ref
self.config = config
self.event_handler_running = True
self.gui_events = []
@property
def gui(self):
return self.get_gui().funcs
def start_fishing(self, action_key: str, borderless: bool, collect_r: bool):
"""
@ -59,7 +71,7 @@ class Engine:
self.fishPixWindow = Window(color=cv2.COLOR_RGB2HSV)
# check for game window and stuff
self.gui.call(GUIFunction.STARTED, (True,))
self.gui.bot_started(True)
logging.info("Starting the bot engine, look at the fishing hole to start fishing")
Thread(target=_wait_and_check, args=(self.gui,)).start()
while self.start:
@ -73,26 +85,13 @@ class Engine:
# Services to be ran in the end of the main loop
Window.loop_end()
logging.info("Fishing engine stopped")
self.gui.call(GUIFunction.STARTED, (False,))
self.gui.bot_started(False)
def start_event_handler(self):
while True:
while self.event_handler_running:
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
event()
def _show_pixel_vals(self):
def show():
@ -106,3 +105,6 @@ class Engine:
logging.debug("Will display pixel values for 10 seconds")
time.sleep(5)
Thread(target=show, args=()).start()

33
fishy/tech/funcs.py Normal file
View File

@ -0,0 +1,33 @@
import logging
from threading import Thread
# noinspection PyProtectedMember
class EngineFuncs:
def __init__(self, engine):
self.engine = engine
def start_button_pressed(self, *params):
def func():
self.engine.start = not self.engine.start
if self.engine.start:
self.engine.fishy_thread = Thread(target=self.engine.start_fishing, args=(*params,))
self.engine.fishy_thread.start()
self.engine.gui_events.append(func)
def check_pixel_val(self):
def func():
if self.engine.start:
self.engine._show_pixel_vals()
else:
logging.debug("Start the engine first before running this command")
self.engine.gui_events.append(func)
def quit(self):
def func():
self.engine.start = False
self.engine.event_handler_running = False
self.engine.gui_events.append(func)

View File

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

View File

@ -1,6 +1,11 @@
import sys
domain = "http://127.0.0.1:5000" if "--local-server" in sys.argv else "https://fishyeso.herokuapp.com"
if "--local-server" in sys.argv:
domain = "http://127.0.0.1:5000"
elif "--test-server" in sys.argv:
domain = "https://fishyeso-test.herokuapp.com"
else:
domain = "https://fishyeso.herokuapp.com"
user = domain + "/api/user"
notify = domain + "/api/notify"

View File

@ -41,8 +41,20 @@ def send_hole_deplete(uid, fish_caught, hole_time, fish_times):
requests.post(urls.hole_depleted, json=body)
@fallback(False)
def sub(uid, name):
body = {"uid": uid, "discord_name": name}
response = requests.post(urls.subscription, json=body)
return response.json()["success"]
@fallback((False, False))
def is_subbed(uid, lazy=True):
"""
:param uid:
:param lazy:
:return: Tuple[is_subbed, success]
"""
global _is_subbed
if lazy and _is_subbed is not None:

View File

@ -1,3 +1,4 @@
urllib3
winshell
imutils
numpy
@ -9,5 +10,5 @@ pyautogui
requests
beautifulsoup4
whatsmyip
pyqrcode
pypng
tkhtmlview

9
test.ps1 Normal file
View File

@ -0,0 +1,9 @@
cd temp\test
Remove-Item venv -Recurse
& conda create --prefix venv python=3.7 -y
conda activate ./venv
cd ../../dist
pip install ((dir).Name | grep whl)
python -m fishy
cd ..
conda deactivate