2021-05-02 15:39:59 +00:00
|
|
|
import ctypes
|
2020-04-18 11:32:14 +00:00
|
|
|
import logging
|
2020-04-19 11:52:42 +00:00
|
|
|
import os
|
2021-03-24 16:58:35 +00:00
|
|
|
import shutil
|
2020-04-15 11:27:26 +00:00
|
|
|
import sys
|
2020-04-19 11:52:42 +00:00
|
|
|
import threading
|
2020-10-15 01:26:31 +00:00
|
|
|
import time
|
2020-04-19 11:52:42 +00:00
|
|
|
import traceback
|
2020-04-18 11:32:14 +00:00
|
|
|
import webbrowser
|
2021-05-09 07:05:51 +00:00
|
|
|
from hashlib import md5
|
2021-03-24 16:58:35 +00:00
|
|
|
from io import BytesIO
|
2020-04-18 11:32:14 +00:00
|
|
|
from threading import Thread
|
2020-04-19 11:52:42 +00:00
|
|
|
from uuid import uuid1
|
2021-05-09 07:05:51 +00:00
|
|
|
from zipfile import ZipFile
|
2020-04-19 11:52:42 +00:00
|
|
|
|
2021-05-09 07:05:51 +00:00
|
|
|
import requests
|
|
|
|
import winshell
|
|
|
|
from playsound import playsound
|
2020-05-05 15:11:00 +00:00
|
|
|
from win32com.client import Dispatch
|
2020-11-30 21:04:33 +00:00
|
|
|
from win32comext.shell import shell, shellcon
|
2021-05-02 18:05:06 +00:00
|
|
|
from win32gui import GetForegroundWindow, GetWindowText
|
2020-05-05 15:11:00 +00:00
|
|
|
|
2020-04-19 11:52:42 +00:00
|
|
|
import fishy
|
2020-05-14 02:03:13 +00:00
|
|
|
from fishy import web
|
2021-11-21 11:00:41 +00:00
|
|
|
from fishy.constants import libgps, lam2, fishyqr
|
|
|
|
from fishy.helper.config import config
|
2020-05-14 02:03:13 +00:00
|
|
|
|
2020-04-20 19:46:12 +00:00
|
|
|
|
2021-04-15 18:27:08 +00:00
|
|
|
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():
|
2021-05-09 09:09:26 +00:00
|
|
|
for i in range(count - 1):
|
2021-04-15 18:27:08 +00:00
|
|
|
playsound(path, True)
|
|
|
|
playsound(path, False)
|
|
|
|
|
|
|
|
Thread(target=_ps_m).start()
|
|
|
|
|
|
|
|
|
2020-05-23 22:14:09 +00:00
|
|
|
def not_implemented():
|
|
|
|
logging.error("Not Implemented")
|
|
|
|
|
|
|
|
|
2020-06-25 18:43:53 +00:00
|
|
|
def empty_function():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-10-15 01:26:31 +00:00
|
|
|
def wait_until(func):
|
|
|
|
while not func():
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
|
|
|
2020-11-07 16:40:57 +00:00
|
|
|
def sign(x):
|
|
|
|
return -1 if x < 0 else 1
|
|
|
|
|
|
|
|
|
2020-05-12 06:13:42 +00:00
|
|
|
def open_web(website):
|
2020-04-15 11:27:26 +00:00
|
|
|
"""
|
2020-05-12 06:13:42 +00:00
|
|
|
Opens a website on browser,
|
|
|
|
uses multi-threading so that current thread doesnt get blocked
|
|
|
|
:param website: url
|
2020-04-15 11:27:26 +00:00
|
|
|
"""
|
2020-04-18 11:32:14 +00:00
|
|
|
logging.debug("opening web, please wait...")
|
|
|
|
Thread(target=lambda: webbrowser.open(website, new=2)).start()
|
2020-04-19 11:52:42 +00:00
|
|
|
|
|
|
|
|
2020-10-17 19:06:07 +00:00
|
|
|
def initialize_uid():
|
2020-11-30 21:04:33 +00:00
|
|
|
from .config import config
|
|
|
|
|
2020-05-14 02:03:13 +00:00
|
|
|
if config.get("uid") is not None:
|
|
|
|
return
|
|
|
|
|
2021-03-28 06:02:39 +00:00
|
|
|
new_uid = web.register_user()
|
|
|
|
if new_uid is not None:
|
2020-05-14 02:03:13 +00:00
|
|
|
config.set("uid", new_uid)
|
|
|
|
else:
|
|
|
|
logging.error("Couldn't register uid, some features might not work")
|
|
|
|
|
|
|
|
|
|
|
|
def _create_new_uid():
|
2020-05-12 06:13:42 +00:00
|
|
|
"""
|
|
|
|
Creates a unique id for user
|
|
|
|
"""
|
2020-04-27 21:19:30 +00:00
|
|
|
return md5(str(uuid1()).encode()).hexdigest()
|
2020-04-19 11:52:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
def install_thread_excepthook():
|
|
|
|
"""
|
|
|
|
Workaround for sys.excepthook thread bug
|
|
|
|
https://bugs.python.org/issue1230540
|
|
|
|
(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
|
|
|
|
Call once from __main__ before creating any threads.
|
|
|
|
If using psyco, call psycho.cannotcompile(threading.Thread.run)
|
|
|
|
since this replaces a new-style class method.
|
|
|
|
"""
|
|
|
|
run_old = threading.Thread.run
|
|
|
|
|
2020-05-14 02:03:13 +00:00
|
|
|
# noinspection PyBroadException
|
2020-04-19 11:52:42 +00:00
|
|
|
def run(*args, **kwargs):
|
|
|
|
try:
|
|
|
|
run_old(*args, **kwargs)
|
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
|
|
raise
|
2020-05-14 02:03:13 +00:00
|
|
|
except Exception:
|
2020-04-19 11:52:42 +00:00
|
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
|
|
|
|
threading.Thread.run = run
|
|
|
|
|
|
|
|
|
|
|
|
def unhandled_exception_logging(*exc_info):
|
|
|
|
text = "".join(traceback.format_exception(*exc_info))
|
|
|
|
logging.error("Unhandled exception: %s", text)
|
|
|
|
|
|
|
|
|
2020-05-12 06:13:42 +00:00
|
|
|
def manifest_file(rel_path):
|
|
|
|
"""
|
|
|
|
returns a file from the manifest files,
|
|
|
|
used to get the files which are installed along with the scripts
|
|
|
|
:param rel_path: relative path from `__init__.py`
|
|
|
|
:return: abs path of the file
|
|
|
|
"""
|
2020-04-19 11:52:42 +00:00
|
|
|
return os.path.join(os.path.dirname(fishy.__file__), rel_path)
|
|
|
|
|
|
|
|
|
2020-10-17 19:06:07 +00:00
|
|
|
def create_shortcut_first():
|
2020-11-30 21:04:33 +00:00
|
|
|
from .config import config
|
|
|
|
|
2020-10-17 19:06:07 +00:00
|
|
|
if not config.get("shortcut_created", False):
|
2020-10-13 18:23:27 +00:00
|
|
|
create_shortcut(False)
|
2020-10-17 19:06:07 +00:00
|
|
|
config.set("shortcut_created", True)
|
2020-05-14 02:03:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
# noinspection PyBroadException
|
2020-10-13 13:49:39 +00:00
|
|
|
def create_shortcut(anti_ghosting: bool):
|
2020-05-12 06:13:42 +00:00
|
|
|
"""
|
|
|
|
creates a new shortcut on desktop
|
|
|
|
"""
|
2020-04-20 19:46:12 +00:00
|
|
|
try:
|
2020-05-12 06:13:42 +00:00
|
|
|
desktop = winshell.desktop()
|
|
|
|
path = os.path.join(desktop, "Fishybot ESO.lnk")
|
2020-04-20 19:46:12 +00:00
|
|
|
|
2020-05-12 06:13:42 +00:00
|
|
|
shell = Dispatch('WScript.Shell')
|
|
|
|
shortcut = shell.CreateShortCut(path)
|
2020-10-13 13:49:39 +00:00
|
|
|
|
|
|
|
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"
|
|
|
|
|
2020-05-12 06:13:42 +00:00
|
|
|
shortcut.IconLocation = manifest_file("icon.ico")
|
|
|
|
shortcut.save()
|
2020-05-05 15:11:00 +00:00
|
|
|
|
2020-05-12 06:13:42 +00:00
|
|
|
logging.info("Shortcut created")
|
2020-05-14 02:03:13 +00:00
|
|
|
except Exception:
|
2020-10-13 13:49:39 +00:00
|
|
|
traceback.print_exc()
|
2020-05-12 06:13:42 +00:00
|
|
|
logging.error("Couldn't create shortcut")
|
2020-04-19 11:52:42 +00:00
|
|
|
|
|
|
|
|
2021-04-09 18:50:53 +00:00
|
|
|
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")
|
|
|
|
|
|
|
|
|
2021-03-24 16:58:35 +00:00
|
|
|
def get_addondir():
|
|
|
|
# noinspection PyUnresolvedReferences
|
|
|
|
from win32com.shell import shell, shellcon
|
|
|
|
documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
2021-04-09 18:50:53 +00:00
|
|
|
return os.path.join(documents, "Elder Scrolls Online", "live", "Addons")
|
2021-03-24 16:58:35 +00:00
|
|
|
|
|
|
|
|
2021-03-26 09:34:01 +00:00
|
|
|
def addon_exists(name, url=None, v=None):
|
2021-03-24 16:58:35 +00:00
|
|
|
return os.path.exists(os.path.join(get_addondir(), name))
|
|
|
|
|
|
|
|
|
2021-03-26 09:34:01 +00:00
|
|
|
def get_addonversion(name, url=None, v=None):
|
|
|
|
if addon_exists(name):
|
|
|
|
txt = name + ".txt"
|
|
|
|
try:
|
|
|
|
with open(os.path.join(get_addondir(), name, txt)) as f:
|
|
|
|
for line in f:
|
|
|
|
if "AddOnVersion" in line:
|
|
|
|
return int(line.split(' ')[2])
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
2021-11-21 11:00:41 +00:00
|
|
|
def install_required_addons(force=False):
|
|
|
|
addons_req = [libgps, lam2, fishyqr]
|
|
|
|
addon_version = config.get("addon_version", {})
|
|
|
|
installed = False
|
|
|
|
for addon in addons_req:
|
|
|
|
if force or (addon_exists(*addon) and
|
|
|
|
(addon[0] not in addon_version or (
|
|
|
|
addon[0] in addon_version and addon_version[addon[0]] < addon[2]))):
|
|
|
|
remove_addon(*addon)
|
|
|
|
install_addon(*addon)
|
|
|
|
addon_version[addon[0]] = addon[2]
|
|
|
|
installed = True
|
|
|
|
config.set("addon_version", addon_version)
|
|
|
|
if installed:
|
|
|
|
logging.info("Please make sure to enable \"Allow outdated addons\" in ESO")
|
|
|
|
|
|
|
|
|
2020-05-14 02:03:13 +00:00
|
|
|
# noinspection PyBroadException
|
2021-03-26 09:34:01 +00:00
|
|
|
def install_addon(name, url, v=None):
|
2020-04-20 19:46:12 +00:00
|
|
|
try:
|
2021-03-24 16:58:35 +00:00
|
|
|
r = requests.get(url, stream=True)
|
|
|
|
z = ZipFile(BytesIO(r.content))
|
|
|
|
z.extractall(path=get_addondir())
|
2021-11-21 11:00:41 +00:00
|
|
|
logging.info("Add-On " + name + " installed successfully!")
|
2021-03-26 09:35:00 +00:00
|
|
|
return 0
|
2021-05-09 09:48:35 +00:00
|
|
|
except Exception:
|
2021-05-09 09:09:26 +00:00
|
|
|
logging.error("Could not install Add-On " + name + ", try doing it manually")
|
2021-11-21 11:00:41 +00:00
|
|
|
traceback.print_exc()
|
2021-03-26 09:35:00 +00:00
|
|
|
return 1
|
2021-03-24 16:58:35 +00:00
|
|
|
|
|
|
|
|
2021-03-26 09:34:01 +00:00
|
|
|
def remove_addon(name, url=None, v=None):
|
2021-03-24 16:58:35 +00:00
|
|
|
try:
|
|
|
|
shutil.rmtree(os.path.join(get_addondir(), name))
|
2021-05-09 09:09:26 +00:00
|
|
|
logging.info("Add-On " + name + " removed!")
|
2021-03-24 16:58:35 +00:00
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
2021-05-09 09:48:35 +00:00
|
|
|
except PermissionError:
|
2021-05-09 09:09:26 +00:00
|
|
|
logging.error("Fishy has no permission to remove " + name + " Add-On")
|
2021-03-26 09:35:00 +00:00
|
|
|
return 1
|
|
|
|
return 0
|
2020-05-05 15:11:00 +00:00
|
|
|
|
|
|
|
|
2020-11-30 21:04:33 +00:00
|
|
|
def get_documents():
|
|
|
|
return shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
|
|
|
|
|
|
|
|
2020-05-05 15:11:00 +00:00
|
|
|
def restart():
|
|
|
|
os.execl(sys.executable, *([sys.executable] + sys.argv))
|
2021-02-14 18:10:11 +00:00
|
|
|
|
|
|
|
|
2021-05-02 15:39:59 +00:00
|
|
|
def log_raise(msg):
|
|
|
|
logging.error(msg)
|
|
|
|
raise Exception(msg)
|
|
|
|
|
|
|
|
|
2021-02-14 18:10:11 +00:00
|
|
|
def update():
|
|
|
|
from .config import config
|
|
|
|
|
|
|
|
config.delete("dont_ask_update")
|
|
|
|
restart()
|
2021-05-02 15:39:59 +00:00
|
|
|
|
|
|
|
|
2021-05-02 18:05:06 +00:00
|
|
|
def is_eso_active():
|
|
|
|
return GetWindowText(GetForegroundWindow()) == "Elder Scrolls Online"
|
|
|
|
|
|
|
|
|
2021-05-02 15:39:59 +00:00
|
|
|
# noinspection PyProtectedMember,PyUnresolvedReferences
|
|
|
|
def _get_id(thread):
|
|
|
|
# returns id of the respective thread
|
|
|
|
if hasattr(thread, '_thread_id'):
|
|
|
|
return thread._thread_id
|
|
|
|
for id, thread in threading._active.items():
|
|
|
|
if thread is thread:
|
|
|
|
return id
|
|
|
|
|
|
|
|
|
|
|
|
def kill_thread(thread):
|
|
|
|
thread_id = _get_id(thread)
|
|
|
|
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
|
|
|
|
ctypes.py_object(SystemExit))
|
|
|
|
if res > 1:
|
|
|
|
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
|
|
|
|
print('Exception raise failure')
|