mirror of
https://github.com/fishyboteso/fishyboteso.git
synced 2024-08-30 18:32:13 +00:00
Merge branch 'main' into wip/keyboard-interup
# Conflicts: # fishy/__main__.py
This commit is contained in:
commit
063c1e5481
@ -1,11 +1,6 @@
|
|||||||
import ctypes
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import win32con
|
|
||||||
import win32gui
|
|
||||||
|
|
||||||
import fishy
|
import fishy
|
||||||
from fishy.gui import GUI, update_dialog, check_eula
|
from fishy.gui import GUI, update_dialog, check_eula
|
||||||
from fishy import helper, web
|
from fishy import helper, web
|
||||||
@ -17,21 +12,16 @@ from fishy.helper.active_poll import active
|
|||||||
from fishy.helper.config import config
|
from fishy.helper.config import config
|
||||||
from fishy.helper.hotkey.hotkey_process import hotkey
|
from fishy.helper.hotkey.hotkey_process import hotkey
|
||||||
from fishy.helper.migration import Migration
|
from fishy.helper.migration import Migration
|
||||||
|
from fishy.osservices.os_services import os_services
|
||||||
|
|
||||||
def check_window_name(title):
|
|
||||||
titles = ["Command Prompt", "PowerShell", "Fishy"]
|
|
||||||
for t in titles:
|
|
||||||
if t in title:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
def initialize(window_to_hide):
|
def initialize():
|
||||||
Migration.migrate()
|
Migration.migrate()
|
||||||
|
|
||||||
helper.create_shortcut_first()
|
if not config.get("shortcut_created", False):
|
||||||
|
os_services.create_shortcut(False)
|
||||||
|
config.set("shortcut_created", True)
|
||||||
|
|
||||||
new_session = web.get_session()
|
new_session = web.get_session()
|
||||||
|
|
||||||
@ -39,15 +29,11 @@ def initialize(window_to_hide):
|
|||||||
logging.error("Couldn't create a session, some features might not work")
|
logging.error("Couldn't create a session, some features might not work")
|
||||||
logging.debug(f"created session {new_session}")
|
logging.debug(f"created session {new_session}")
|
||||||
|
|
||||||
try:
|
if os_services.is_admin():
|
||||||
is_admin = os.getuid() == 0
|
|
||||||
except AttributeError:
|
|
||||||
is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
|
|
||||||
if is_admin:
|
|
||||||
logging.info("Running with admin privileges")
|
logging.info("Running with admin privileges")
|
||||||
|
|
||||||
if not config.get("debug", False) and check_window_name(win32gui.GetWindowText(window_to_hide)):
|
if not config.get("debug", False):
|
||||||
win32gui.ShowWindow(window_to_hide, win32con.SW_HIDE)
|
os_services.hide_terminal()
|
||||||
helper.install_thread_excepthook()
|
helper.install_thread_excepthook()
|
||||||
sys.excepthook = helper.unhandled_exception_logging
|
sys.excepthook = helper.unhandled_exception_logging
|
||||||
|
|
||||||
@ -62,9 +48,17 @@ def on_gui_load(gui, splash, logger):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("launching please wait...")
|
print("launching please wait...")
|
||||||
|
|
||||||
|
if not os_services.init():
|
||||||
|
print("platform not supported")
|
||||||
|
return
|
||||||
|
|
||||||
|
config.init()
|
||||||
|
if not check_eula():
|
||||||
|
return
|
||||||
|
|
||||||
bot = EngineEventHandler(lambda: gui)
|
bot = EngineEventHandler(lambda: gui)
|
||||||
gui = GUI(lambda: bot, lambda: on_gui_load(gui, splash, logger))
|
gui = GUI(lambda: bot, lambda: on_gui_load(gui, splash, logger))
|
||||||
window_to_hide = win32gui.GetForegroundWindow()
|
|
||||||
logger = GuiLogger()
|
logger = GuiLogger()
|
||||||
hotkey.init()
|
hotkey.init()
|
||||||
active.init()
|
active.init()
|
||||||
@ -79,7 +73,7 @@ def main():
|
|||||||
splash = Splash().start()
|
splash = Splash().start()
|
||||||
config.start_backup_scheduler()
|
config.start_backup_scheduler()
|
||||||
|
|
||||||
initialize(window_to_hide)
|
initialize()
|
||||||
|
|
||||||
hotkey.start()
|
hotkey.start()
|
||||||
gui.start()
|
gui.start()
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
import logging
|
import logging
|
||||||
import math
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pywintypes
|
|
||||||
import win32api
|
|
||||||
import win32gui
|
|
||||||
from ctypes import windll
|
|
||||||
|
|
||||||
from mss import mss
|
from mss import mss
|
||||||
from mss.base import MSSBase
|
from mss.base import MSSBase
|
||||||
|
|
||||||
from fishy.helper.helper import print_exc
|
from fishy.helper.helper import print_exc
|
||||||
|
from fishy.osservices.os_services import os_services
|
||||||
|
|
||||||
|
|
||||||
class Status(Enum):
|
class Status(Enum):
|
||||||
@ -27,10 +23,11 @@ class WindowServer:
|
|||||||
"""
|
"""
|
||||||
Screen: np.ndarray = None
|
Screen: np.ndarray = None
|
||||||
windowOffset = None
|
windowOffset = None
|
||||||
hwnd = None
|
|
||||||
status = Status.STOPPED
|
status = Status.STOPPED
|
||||||
sct: MSSBase = None
|
sct: MSSBase = None
|
||||||
monitor_top_left = None
|
|
||||||
|
crop = None
|
||||||
|
monitor_id = -1
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
@ -38,22 +35,31 @@ def init():
|
|||||||
Executed once before the main loop,
|
Executed once before the main loop,
|
||||||
Finds the game window, and calculates the offset to remove the title bar
|
Finds the game window, and calculates the offset to remove the title bar
|
||||||
"""
|
"""
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
try:
|
|
||||||
WindowServer.hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
|
||||||
|
|
||||||
monitor_id = windll.user32.MonitorFromWindow(WindowServer.hwnd, 2)
|
|
||||||
WindowServer.monitor_top_left = win32api.GetMonitorInfo(monitor_id)["Monitor"][:2]
|
|
||||||
|
|
||||||
rect = win32gui.GetWindowRect(WindowServer.hwnd)
|
|
||||||
client_rect = win32gui.GetClientRect(WindowServer.hwnd)
|
|
||||||
WindowServer.windowOffset = math.floor(((rect[2] - rect[0]) - client_rect[2]) / 2)
|
|
||||||
WindowServer.status = Status.RUNNING
|
WindowServer.status = Status.RUNNING
|
||||||
WindowServer.sct = mss()
|
WindowServer.sct = mss()
|
||||||
|
|
||||||
except pywintypes.error:
|
WindowServer.crop = os_services.get_game_window_rect()
|
||||||
|
monitor_rect = os_services.get_monitor_rect()
|
||||||
|
|
||||||
|
if monitor_rect is None or WindowServer.crop is None:
|
||||||
logging.error("Game window not found")
|
logging.error("Game window not found")
|
||||||
WindowServer.status = Status.CRASHED
|
WindowServer.status = Status.CRASHED
|
||||||
|
return
|
||||||
|
|
||||||
|
for i, m in enumerate(WindowServer.sct.monitors):
|
||||||
|
if m["top"] == monitor_rect[0] and m["left"] == monitor_rect[1]:
|
||||||
|
WindowServer.monitor_id = i
|
||||||
|
|
||||||
|
|
||||||
|
def get_cropped_screenshot():
|
||||||
|
sct_img = WindowServer.sct.grab(WindowServer.sct.monitors[WindowServer.monitor_id])
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
ss = np.array(sct_img)
|
||||||
|
crop = WindowServer.crop
|
||||||
|
cropped_ss = ss[crop[1]:crop[3], crop[0]:crop[2]]
|
||||||
|
if cropped_ss.size == 0:
|
||||||
|
return None
|
||||||
|
return cropped_ss
|
||||||
|
|
||||||
|
|
||||||
def loop():
|
def loop():
|
||||||
@ -61,27 +67,10 @@ def loop():
|
|||||||
Executed in the start of the main loop
|
Executed in the start of the main loop
|
||||||
finds the game window location and captures it
|
finds the game window location and captures it
|
||||||
"""
|
"""
|
||||||
|
WindowServer.Screen = get_cropped_screenshot()
|
||||||
|
|
||||||
sct_img = WindowServer.sct.grab(WindowServer.sct.monitors[1])
|
if WindowServer.Screen is None:
|
||||||
# noinspection PyTypeChecker
|
logging.error("Couldn't find the game window")
|
||||||
temp_screen = np.array(sct_img)
|
|
||||||
|
|
||||||
rect = win32gui.GetWindowRect(WindowServer.hwnd)
|
|
||||||
client_rect = win32gui.GetClientRect(WindowServer.hwnd)
|
|
||||||
|
|
||||||
fullscreen = sct_img.size.height == (rect[3] - rect[1])
|
|
||||||
title_offset = ((rect[3] - rect[1]) - client_rect[3]) - WindowServer.windowOffset if not fullscreen else 0
|
|
||||||
crop = (
|
|
||||||
rect[0] + WindowServer.windowOffset - WindowServer.monitor_top_left[0],
|
|
||||||
rect[1] + title_offset - WindowServer.monitor_top_left[1],
|
|
||||||
rect[2] - WindowServer.windowOffset - WindowServer.monitor_top_left[0],
|
|
||||||
rect[3] - WindowServer.windowOffset - WindowServer.monitor_top_left[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
WindowServer.Screen = temp_screen[crop[1]:crop[3], crop[0]:crop[2]] if not fullscreen else temp_screen
|
|
||||||
|
|
||||||
if WindowServer.Screen.size == 0:
|
|
||||||
logging.error("Don't minimize or drag game window outside the screen")
|
|
||||||
WindowServer.status = Status.CRASHED
|
WindowServer.status = Status.CRASHED
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ from fishy.engine.fullautofisher.mode.recorder import Recorder
|
|||||||
from fishy.engine.semifisher import fishing_mode
|
from fishy.engine.semifisher import fishing_mode
|
||||||
from fishy.engine.semifisher.fishing_mode import FishingMode
|
from fishy.engine.semifisher.fishing_mode import FishingMode
|
||||||
from fishy.helper.config import config
|
from fishy.helper.config import config
|
||||||
from fishy.helper.helper import wait_until, is_eso_active, sign, print_exc
|
from fishy.helper.helper import wait_until, sign, print_exc
|
||||||
|
from fishy.osservices.os_services import os_services
|
||||||
|
|
||||||
mse = mouse.Controller()
|
mse = mouse.Controller()
|
||||||
kb = keyboard.Controller()
|
kb = keyboard.Controller()
|
||||||
@ -52,9 +53,9 @@ class FullAuto(IEngine):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# block thread until game window becomes active
|
# block thread until game window becomes active
|
||||||
if not is_eso_active():
|
if not os_services.is_eso_active():
|
||||||
logging.info("Waiting for eso window to be active...")
|
logging.info("Waiting for eso window to be active...")
|
||||||
wait_until(lambda: is_eso_active() or not self.start)
|
wait_until(lambda: os_services.is_eso_active() or not self.start)
|
||||||
if self.start:
|
if self.start:
|
||||||
logging.info("starting in 2 secs...")
|
logging.info("starting in 2 secs...")
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
@ -87,8 +88,8 @@ class FullAuto(IEngine):
|
|||||||
def stop_on_inactive(self):
|
def stop_on_inactive(self):
|
||||||
def func():
|
def func():
|
||||||
logging.debug("stop on inactive started")
|
logging.debug("stop on inactive started")
|
||||||
wait_until(lambda: not is_eso_active() or not self.start)
|
wait_until(lambda: not os_services.is_eso_active() or not self.start)
|
||||||
if self.start and not is_eso_active():
|
if self.start and not os_services.is_eso_active():
|
||||||
self.turn_off()
|
self.turn_off()
|
||||||
logging.debug("stop on inactive stopped")
|
logging.debug("stop on inactive stopped")
|
||||||
Thread(target=func).start()
|
Thread(target=func).start()
|
||||||
|
@ -12,10 +12,10 @@ from playsound import playsound
|
|||||||
|
|
||||||
from fishy import web
|
from fishy import web
|
||||||
from fishy.engine.semifisher import fishing_mode
|
from fishy.engine.semifisher import fishing_mode
|
||||||
from fishy.engine.semifisher.fishing_mode import FishingMode, State
|
from fishy.engine.semifisher.fishing_mode import State
|
||||||
from fishy.helper import helper
|
from fishy.helper import helper
|
||||||
from fishy.helper.config import config
|
from fishy.helper.config import config
|
||||||
from fishy.helper.helper import is_eso_active
|
from fishy.osservices.os_services import os_services
|
||||||
|
|
||||||
|
|
||||||
class FishEvent:
|
class FishEvent:
|
||||||
@ -44,7 +44,7 @@ def _fishing_sleep(waittime, lower_limit_ms=16, upper_limit_ms=1375):
|
|||||||
|
|
||||||
def if_eso_is_focused(func):
|
def if_eso_is_focused(func):
|
||||||
def wrapper():
|
def wrapper():
|
||||||
if not is_eso_active():
|
if not os_services.is_eso_active():
|
||||||
logging.warning("ESO window is not focused")
|
logging.warning("ESO window is not focused")
|
||||||
return
|
return
|
||||||
func()
|
func()
|
||||||
|
@ -15,6 +15,7 @@ from ..helper.config import config
|
|||||||
from .discord_login import discord_login
|
from .discord_login import discord_login
|
||||||
from ..helper.hotkey.hotkey_process import hotkey
|
from ..helper.hotkey.hotkey_process import hotkey
|
||||||
from ..helper.hotkey.process import Key
|
from ..helper.hotkey.process import Key
|
||||||
|
from ..osservices.os_services import os_services
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from . import GUI
|
from . import GUI
|
||||||
@ -46,7 +47,7 @@ def _create(gui: 'GUI'):
|
|||||||
gui.login.set(1 if login > 0 else 0)
|
gui.login.set(1 if login > 0 else 0)
|
||||||
state = tk.DISABLED if login == -1 else tk.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_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 Shortcut", command=lambda: os_services.create_shortcut(False))
|
||||||
# filemenu.add_command(label="Create Anti-Ghost Shortcut", command=lambda: helper.create_shortcut(True))
|
# filemenu.add_command(label="Create Anti-Ghost Shortcut", command=lambda: helper.create_shortcut(True))
|
||||||
|
|
||||||
def _toggle_mode():
|
def _toggle_mode():
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from .config import Config
|
from .config import Config
|
||||||
from .helper import (addon_exists, create_shortcut, create_shortcut_first,
|
from .helper import (addon_exists,
|
||||||
get_addonversion, get_savedvarsdir,
|
get_addonversion, get_savedvarsdir,
|
||||||
install_addon, install_thread_excepthook, manifest_file,
|
install_addon, install_thread_excepthook, manifest_file,
|
||||||
not_implemented, open_web, playsound_multiple,
|
not_implemented, open_web, playsound_multiple,
|
||||||
|
@ -10,15 +10,17 @@ from typing import Optional
|
|||||||
|
|
||||||
from event_scheduler import EventScheduler
|
from event_scheduler import EventScheduler
|
||||||
|
|
||||||
|
from fishy.osservices.os_services import os_services
|
||||||
|
|
||||||
|
|
||||||
def filename():
|
def filename():
|
||||||
from fishy.helper.helper import get_documents
|
|
||||||
name = "fishy_config.json"
|
name = "fishy_config.json"
|
||||||
_filename = os.path.join(os.environ["HOMEDRIVE"], os.environ["HOMEPATH"], "Documents", name)
|
_filename = os.path.join(os.environ["HOMEDRIVE"], os.environ["HOMEPATH"], "Documents", name)
|
||||||
if os.path.exists(_filename):
|
if os.path.exists(_filename):
|
||||||
return _filename
|
return _filename
|
||||||
|
|
||||||
return os.path.join(get_documents(), name)
|
# fallback for onedrive documents
|
||||||
|
return os.path.join(os_services.get_documents(), name)
|
||||||
|
|
||||||
|
|
||||||
temp_file = os.path.join(os.environ["TEMP"], "fishy_config.BAK")
|
temp_file = os.path.join(os.environ["TEMP"], "fishy_config.BAK")
|
||||||
|
@ -14,15 +14,13 @@ from uuid import uuid1
|
|||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import winshell
|
|
||||||
from playsound import playsound
|
from playsound import playsound
|
||||||
from win32com.client import Dispatch
|
|
||||||
from win32comext.shell import shell, shellcon
|
|
||||||
from win32gui import GetForegroundWindow, GetWindowText
|
|
||||||
|
|
||||||
import fishy
|
import fishy
|
||||||
from fishy.constants import libgps, lam2, fishyqr, libmapping, libdl, libchatmsg
|
from fishy.constants import libgps, lam2, fishyqr, libmapping, libdl, libchatmsg
|
||||||
from fishy.helper.config import config
|
from fishy.helper.config import config
|
||||||
|
from fishy.osservices.os_services import os_services
|
||||||
|
|
||||||
|
|
||||||
def playsound_multiple(path, count=2):
|
def playsound_multiple(path, count=2):
|
||||||
@ -110,55 +108,14 @@ def manifest_file(rel_path):
|
|||||||
return os.path.join(os.path.dirname(fishy.__file__), rel_path)
|
return os.path.join(os.path.dirname(fishy.__file__), rel_path)
|
||||||
|
|
||||||
|
|
||||||
def create_shortcut_first():
|
|
||||||
from .config import config
|
|
||||||
|
|
||||||
if not config.get("shortcut_created", False):
|
|
||||||
create_shortcut(False)
|
|
||||||
config.set("shortcut_created", True)
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
def create_shortcut(anti_ghosting: bool):
|
|
||||||
"""
|
|
||||||
creates a new shortcut on desktop
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
desktop = winshell.desktop()
|
|
||||||
path = os.path.join(desktop, "Fishybot ESO.lnk")
|
|
||||||
|
|
||||||
_shell = Dispatch('WScript.Shell')
|
|
||||||
shortcut = _shell.CreateShortCut(path)
|
|
||||||
|
|
||||||
if anti_ghosting:
|
|
||||||
shortcut.TargetPath = r"C:\Windows\System32\cmd.exe"
|
|
||||||
python_dir = os.path.join(os.path.dirname(sys.executable), "pythonw.exe")
|
|
||||||
shortcut.Arguments = f"/C start /affinity 1 /low {python_dir} -m fishy"
|
|
||||||
else:
|
|
||||||
shortcut.TargetPath = os.path.join(os.path.dirname(sys.executable), "python.exe")
|
|
||||||
shortcut.Arguments = "-m fishy"
|
|
||||||
|
|
||||||
shortcut.IconLocation = manifest_file("icon.ico")
|
|
||||||
shortcut.save()
|
|
||||||
|
|
||||||
logging.info("Shortcut created")
|
|
||||||
except Exception:
|
|
||||||
print_exc()
|
|
||||||
logging.error("Couldn't create shortcut")
|
|
||||||
|
|
||||||
|
|
||||||
def get_savedvarsdir():
|
def get_savedvarsdir():
|
||||||
# noinspection PyUnresolvedReferences
|
eso_path = os_services.get_eso_config_path()
|
||||||
from win32com.shell import shell, shellcon
|
return os.path.join(eso_path, "live", "SavedVariables")
|
||||||
documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
|
||||||
return os.path.join(documents, "Elder Scrolls Online", "live", "SavedVariables")
|
|
||||||
|
|
||||||
|
|
||||||
def get_addondir():
|
def get_addondir():
|
||||||
# noinspection PyUnresolvedReferences
|
eso_path = os_services.get_eso_config_path()
|
||||||
from win32com.shell import shell, shellcon
|
return os.path.join(eso_path, "live", "Addons")
|
||||||
documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
|
||||||
return os.path.join(documents, "Elder Scrolls Online", "live", "Addons")
|
|
||||||
|
|
||||||
|
|
||||||
def addon_exists(name, url=None, v=None):
|
def addon_exists(name, url=None, v=None):
|
||||||
@ -222,19 +179,10 @@ def remove_addon(name, url=None, v=None):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def get_documents():
|
|
||||||
return shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
|
||||||
|
|
||||||
|
|
||||||
def log_raise(msg):
|
def log_raise(msg):
|
||||||
logging.error(msg)
|
logging.error(msg)
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
|
|
||||||
def is_eso_active():
|
|
||||||
return GetWindowText(GetForegroundWindow()) == "Elder Scrolls Online"
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyProtectedMember,PyUnresolvedReferences
|
# noinspection PyProtectedMember,PyUnresolvedReferences
|
||||||
def _get_id(thread):
|
def _get_id(thread):
|
||||||
# returns id of the respective thread
|
# returns id of the respective thread
|
||||||
|
0
fishy/osservices/__init__.py
Normal file
0
fishy/osservices/__init__.py
Normal file
29
fishy/osservices/linux.py
Normal file
29
fishy/osservices/linux.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
from fishy.osservices.os_services import IOSServices
|
||||||
|
|
||||||
|
|
||||||
|
class Linux(IOSServices):
|
||||||
|
def hide_terminal(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_shortcut(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_documents_path(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_admin(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_eso_config_path(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_eso_active(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_monitor_rect(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_game_window_rect(self) -> Optional[Tuple[int, int, int, int]]:
|
||||||
|
pass
|
100
fishy/osservices/os_services.py
Normal file
100
fishy/osservices/os_services.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import inspect
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Tuple, Optional
|
||||||
|
import platform
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IOSServices(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def hide_terminal(self):
|
||||||
|
"""
|
||||||
|
:return: hides the terminal used to launch fishy
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def create_shortcut(self):
|
||||||
|
"""
|
||||||
|
creates a new shortcut on desktop
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_documents_path(self) -> str:
|
||||||
|
"""
|
||||||
|
:return: documents path to save config file
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def is_admin(self) -> bool:
|
||||||
|
"""
|
||||||
|
:return: true if has elevated rights
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_eso_config_path(self) -> str:
|
||||||
|
"""
|
||||||
|
:return: path location of the ElderScrollsOnline Folder (in documents) which has "live" folder in it
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def is_eso_active(self) -> bool:
|
||||||
|
"""
|
||||||
|
:return: true if eso is the active window
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_monitor_rect(self) -> Optional[Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
:return: [top, left, height, width] of monitor which has game running in it
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_game_window_rect(self) -> Optional[Tuple[int, int, int, int]]:
|
||||||
|
"""
|
||||||
|
:return: location of the game window without any frame
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# todo move this into helper and use for config and similar places
|
||||||
|
# but make sure other fishy stuff is not imported while importing helper
|
||||||
|
# to do that, move everything which uses fishy stuff into a different helper script
|
||||||
|
def singleton_proxy(instance_name):
|
||||||
|
def decorator(root_cls):
|
||||||
|
if not hasattr(root_cls, instance_name):
|
||||||
|
raise AttributeError(f"{instance_name} not found in {root_cls}")
|
||||||
|
|
||||||
|
class SingletonProxy(type):
|
||||||
|
def __getattr__(cls, name):
|
||||||
|
return getattr(getattr(cls, instance_name), name)
|
||||||
|
|
||||||
|
class NewClass(root_cls, metaclass=SingletonProxy):
|
||||||
|
...
|
||||||
|
|
||||||
|
return NewClass
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
@singleton_proxy("_instance")
|
||||||
|
class os_services:
|
||||||
|
_instance: Optional[IOSServices] = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init() -> bool:
|
||||||
|
os_name = platform.system()
|
||||||
|
if os_name == "Windows":
|
||||||
|
from fishy.osservices.windows import Windows
|
||||||
|
os_services._instance = Windows()
|
||||||
|
return True
|
||||||
|
|
||||||
|
# todo uncomment after linux.py is implemented
|
||||||
|
# if os_name == "Linux":
|
||||||
|
# from fishy.osservices.linux import Linux
|
||||||
|
# os_services._instance = Linux()
|
||||||
|
# return True
|
||||||
|
|
||||||
|
return False
|
112
fishy/osservices/windows.py
Normal file
112
fishy/osservices/windows.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import ctypes
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
import pywintypes
|
||||||
|
import win32api
|
||||||
|
import win32con
|
||||||
|
import win32gui
|
||||||
|
import winshell
|
||||||
|
from win32com.client import Dispatch
|
||||||
|
from win32comext.shell import shell, shellcon
|
||||||
|
from win32gui import GetForegroundWindow, GetWindowText
|
||||||
|
|
||||||
|
|
||||||
|
from ctypes import windll
|
||||||
|
|
||||||
|
from fishy.helper import manifest_file
|
||||||
|
from fishy.osservices.os_services import IOSServices
|
||||||
|
|
||||||
|
|
||||||
|
def _check_window_name(title):
|
||||||
|
titles = ["Command Prompt", "PowerShell", "Fishy"]
|
||||||
|
for t in titles:
|
||||||
|
if t in title:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Windows(IOSServices):
|
||||||
|
def is_admin(self) -> bool:
|
||||||
|
try:
|
||||||
|
is_admin = os.getuid() == 0
|
||||||
|
except AttributeError:
|
||||||
|
is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
|
||||||
|
return is_admin
|
||||||
|
|
||||||
|
def is_eso_active(self) -> bool:
|
||||||
|
return GetWindowText(GetForegroundWindow()) == "Elder Scrolls Online"
|
||||||
|
|
||||||
|
# noinspection PyBroadException
|
||||||
|
def create_shortcut(self, anti_ghosting=False):
|
||||||
|
try:
|
||||||
|
desktop = winshell.desktop()
|
||||||
|
path = os.path.join(desktop, "Fishybot ESO.lnk")
|
||||||
|
_shell = Dispatch('WScript.Shell')
|
||||||
|
shortcut = _shell.CreateShortCut(path)
|
||||||
|
|
||||||
|
if anti_ghosting:
|
||||||
|
shortcut.TargetPath = r"C:\Windows\System32\cmd.exe"
|
||||||
|
python_dir = os.path.join(os.path.dirname(sys.executable), "pythonw.exe")
|
||||||
|
shortcut.Arguments = f"/C start /affinity 1 /low {python_dir} -m fishy"
|
||||||
|
else:
|
||||||
|
shortcut.TargetPath = os.path.join(os.path.dirname(sys.executable), "python.exe")
|
||||||
|
shortcut.Arguments = "-m fishy"
|
||||||
|
|
||||||
|
shortcut.IconLocation = manifest_file("icon.ico")
|
||||||
|
shortcut.save()
|
||||||
|
|
||||||
|
logging.info("Shortcut created")
|
||||||
|
except Exception:
|
||||||
|
logging.error("Couldn't create shortcut")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.to_hide = win32gui.GetForegroundWindow()
|
||||||
|
|
||||||
|
def hide_terminal(self):
|
||||||
|
if _check_window_name(win32gui.GetWindowText(self.to_hide)):
|
||||||
|
win32gui.ShowWindow(self.to_hide, win32con.SW_HIDE)
|
||||||
|
|
||||||
|
def get_documents_path(self) -> str:
|
||||||
|
return shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
||||||
|
|
||||||
|
def get_eso_config_path(self) -> str:
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
def get_monitor_rect(self):
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
try:
|
||||||
|
hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
||||||
|
monitor = windll.user32.MonitorFromWindow(hwnd, 2)
|
||||||
|
monitor_info = win32api.GetMonitorInfo(monitor)
|
||||||
|
return monitor_info["Monitor"]
|
||||||
|
except pywintypes.error:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_game_window_rect(self) -> Optional[Tuple[int, int, int, int]]:
|
||||||
|
hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
||||||
|
monitor_rect = self.get_monitor_rect()
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
try:
|
||||||
|
rect = win32gui.GetWindowRect(hwnd)
|
||||||
|
client_rect = win32gui.GetClientRect(hwnd)
|
||||||
|
windowOffset = math.floor(((rect[2] - rect[0]) - client_rect[2]) / 2)
|
||||||
|
fullscreen = monitor_rect[3] == (rect[3] - rect[1])
|
||||||
|
title_offset = ((rect[3] - rect[1]) - client_rect[3]) - windowOffset if not fullscreen else 0
|
||||||
|
|
||||||
|
game_rect = (
|
||||||
|
rect[0] + windowOffset - monitor_rect[0],
|
||||||
|
rect[1] + title_offset - monitor_rect[1],
|
||||||
|
rect[2] - windowOffset - monitor_rect[0],
|
||||||
|
rect[3] - windowOffset - monitor_rect[1]
|
||||||
|
)
|
||||||
|
return game_rect
|
||||||
|
except pywintypes.error:
|
||||||
|
return None
|
@ -1,10 +1,10 @@
|
|||||||
urllib3
|
urllib3
|
||||||
winshell
|
|
||||||
imutils
|
imutils
|
||||||
numpy
|
numpy
|
||||||
opencv_python
|
opencv_python
|
||||||
Pillow
|
Pillow
|
||||||
pypiwin32
|
pypiwin32 ; platform_system=="Windows"
|
||||||
|
winshell ; platform_system=="Windows"
|
||||||
ttkthemes
|
ttkthemes
|
||||||
requests
|
requests
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
|
Loading…
x
Reference in New Issue
Block a user