Compare commits

..

90 Commits
0.5.12 ... main

Author SHA1 Message Date
Adam Saudagar
aedd048a34
removed a line from readme 2024-03-28 14:26:07 +01:00
Adam Saudagar
d262885afa #185 re did the auto switching screenshot lib with the fix 2024-03-14 23:58:51 +05:30
Adam Saudagar
773f05ebae revert: auto switching screenshot lib from #185
need more thought as it is causing fishy to no longer function
2024-03-14 23:32:59 +05:30
Adam Saudagar
0efa8138da hotfix 0.5.24 testing grab once before providing the ss lib 2024-03-13 15:11:34 +05:30
Adam Saudagar
106eca4980
version update 0.5.23 2024-03-12 12:56:24 +05:30
Adam Saudagar
8a9d621086
Merge pull request #185 from Femi-lawal/main
chore: make PyAutoGUI default
2024-03-12 08:25:06 +01:00
Femi Lawal
a16474f613 handled None in screenshot.create() 2024-03-11 09:10:09 +01:00
Femi Lawal
270abc5167 remove error thrown when no suitable library found 2024-03-03 18:40:27 +01:00
Femi Lawal
f6f6bfad70 make PyAutoGUI default 2024-02-12 21:44:44 +01:00
Adam Saudagar
820bdfdd06
Merge pull request #183 from fishyboteso/dependabot/github_actions/dot-github/workflows/tj-actions/changed-files-41
Bump tj-actions/changed-files from 35 to 41 in /.github/workflows
2024-01-07 12:56:32 +05:30
dependabot[bot]
cc36abc605
Bump tj-actions/changed-files from 35 to 41 in /.github/workflows
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 35 to 41.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v35...v41)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-02 16:48:47 +00:00
Adam Saudagar
0fdb285aea #180 hotfix proper fallback for version check 2023-12-15 20:33:46 +05:30
Adam Saudagar
04e30c2f1c version update 0.5.21 2023-12-15 20:28:43 +05:30
Adam Saudagar
afb82d0562 #180 checks for version using github version file instead of pypi
also removed beautiful soup dependency
2023-12-15 20:23:41 +05:30
Adam Saudagar
e80f0fabdc
Merge pull request #182 from fishyboteso/wip/forwardkey-config
#95 created config for forward key
2023-12-15 18:50:44 +05:30
Adam Saudagar
a59909de9d #95 added default config for when forward_key isn't set but used 2023-12-15 18:37:16 +05:30
Shayaan Shaikh
d29b801657 #95 created config for forward key 2023-12-11 21:31:59 +05:30
Shayaan Shaikh
43651b81fd #135 if test server arg is used so it will use a different conf file is used 2023-10-10 21:28:41 +05:30
Adam Saudagar
c67a40a7d6
Merge pull request #178 from fishyboteso/wip/beep_bug
beep sound when sound notification abled and no beep sound when disabled
2023-10-01 00:41:25 +05:30
Shayaan Shaikh
91a97af5d9 #161 beep sound when sound notification abled and no beep sound when disabled 2023-09-30 22:12:19 +05:30
Semjon Wilke
0b17e0dd88 update add on version 2023-08-02 00:40:54 +02:00
Adam Saudagar
8b21b722f2 #167 hotfix forgot to add timeout to register too, as its one of the first web calls too 2023-07-21 18:50:59 +05:30
Adam Saudagar
cbb37e8f0b #167 hotfix start fishy in offline mode if backend doesn't responds 2023-07-21 18:41:46 +05:30
Adam Saudagar
dd404741fe #168 hotfix fixes 415 due to incorrect body type in header 2023-07-06 21:27:21 +05:30
Adam Saudagar
28b7cfeb8c
Merge pull request #163 from BDadmehr0/main
Update main_gui.py | Save scr & Save y or n
2023-06-14 13:58:18 +05:30
Dadmehr
cab56da6e4
Update main_gui.py 2023-06-08 18:51:15 +03:30
Dadmehr
ad77ac85b9
Update main_gui.py
This code modifies the toggle_show_grab function to show a warning message when "Save Screenshots" is enabled. When "Save Screenshots" is disabled, it shows a confirmation message asking the user if they want to delete the saved screenshots. Depending on the user's choice, you can add the necessary code to delete the screenshots or perform any other desired action.
2023-06-06 19:42:49 +03:30
Semjon Wilke
5fe4c235ac update addons 2023-05-31 22:10:47 +02:00
Adam Saudagar
ecf5b3524c #156 added logs 2023-03-09 16:30:15 +05:30
Adam Saudagar
cc61caf12d #156 scaling down full window screenshot 2023-03-09 15:38:50 +05:30
Adam Saudagar
0c3b5da26b #156 renamed label to match its function 2023-03-09 15:38:21 +05:30
Adam Saudagar
3354de4772 [hotfix] incorrect function call causing exception fixed 2023-03-08 15:43:19 +05:30
Adam Saudagar
9942d0533f version update 0.5.16 2023-03-07 15:47:53 +05:30
Adam Saudagar
4add028ff6 playsound excpetion fixed 2023-03-07 15:47:03 +05:30
Adam Saudagar
70af635025
Merge pull request #155 from fishyboteso/wip/screenshot_lib_selector
screenshot library selected
2023-03-07 15:46:20 +05:30
Adam Saudagar
19a5fe9b7f removed some useless code 2023-03-07 15:46:06 +05:30
Adam Saudagar
5141ae9c2d new fishyqr version 2023-03-07 15:34:45 +05:30
Adam Saudagar
df4deda1f2 added debug option to save capture 2023-03-07 15:00:16 +05:30
Adam Saudagar
2ca4107595 removed some unused code 2023-03-07 14:59:00 +05:30
Adam Saudagar
7bf4567395 corrected multi monitor calculations for pyautogui 2023-03-07 14:58:35 +05:30
Adam Saudagar
e1257aeda0 fixed logic for multimonitor setup 2023-03-07 13:40:22 +05:30
Adam Saudagar
f520004c11 #154 look_for_hole disabled by default 2023-03-07 01:48:59 +05:30
Adam Saudagar
8634bac19c d3dshot install logic implemented 2023-03-07 01:48:13 +05:30
Adam Saudagar
199fed6682 gui implemented 2023-03-07 01:47:55 +05:30
Adam Saudagar
5dc7c09cb7 created interface for sslib and choose one using config 2023-03-07 00:37:26 +05:30
Adam Saudagar
788e78b9bb moved singleton_proxy to helper.depless 2023-03-07 00:22:24 +05:30
Adam Saudagar
5fb58d9998 improved code a bit 2023-03-07 00:11:19 +05:30
Adam Saudagar
bc491c8cb0
Merge pull request #153 from fishyboteso/wip/keyboard-interup
handling keyboard interupt and exiting main thread safely
2023-03-07 00:08:25 +05:30
Adam Saudagar
063c1e5481 Merge branch 'main' into wip/keyboard-interup
# Conflicts:
#	fishy/__main__.py
2023-03-07 00:07:36 +05:30
Adam Saudagar
2118e10d5d using splash object instead of using finish function
using gui method instead of directly accessing private members
2023-03-07 00:03:24 +05:30
Adam Saudagar
4dec07d27e
Merge pull request #151 from fishyboteso/wip/cross_platform
making fishy cross platform
2023-03-06 23:33:26 +05:30
Adam Saudagar
47c0ce7413 removed Linux temprorily until linux.py is implemented 2023-03-06 23:32:39 +05:30
Adam Saudagar
901ce6c346 handled case when game is not running 2023-03-06 23:18:09 +05:30
Adam Saudagar
a5499475f6 generalized ClassInstance even more to be used as a decorator 2023-02-22 00:56:22 +05:30
Adam Saudagar
4f90df9079 generalized class instance 2023-02-21 23:22:11 +05:30
Adam Saudagar
c5d1cb67cf decoupled os_calls from fishy to use os_services instead 2023-02-21 23:05:44 +05:30
Adam Saudagar
0de6b54777 implemented methods for windows 2023-02-21 23:04:58 +05:30
Adam Saudagar
6000e9022e created interface and proxy for os services 2023-02-21 23:04:24 +05:30
Adam Saudagar
455976a018 install windows lib if on windows 2023-02-21 22:59:43 +05:30
Adam Saudagar
4e55c88629 handling keyboard interupt and exiting main thread safely
kindof related to #104
2023-02-15 12:38:47 +05:30
Adam Saudagar
b6e543a9e3 version update 0.5.15 2023-02-12 03:45:22 +05:30
Adam Saudagar
3567f062b0 #128 added few checks before image leaves WindowClient which was causing exception 2023-02-12 03:44:40 +05:30
Adam Saudagar
fb54ca4826 fixed crash in 3.11 on first launch 2023-02-12 03:00:43 +05:30
Adam Saudagar
ebbce458cf replaced d3dshot with mss 2023-02-12 02:50:43 +05:30
Adam Saudagar
cd32b8926d Revert "#131 fixed opencv version to reduce install time"
This reverts commit f93ea04d429bd44a0bb29e48367bdc5e7d9d3082.
2023-02-12 02:49:05 +05:30
Adam Saudagar
0827bd9f3b
Merge pull request #146 from fishyboteso/feature/fishyQR_hide_on_scene_change
update FishyQR with hide on scene change
2023-02-12 01:46:04 +05:30
Semjon Wilke
339abba4c4
update FishyQR version 2023-02-11 18:40:56 +01:00
Adam Saudagar
ca06141386 use version.txt to publish 2023-02-11 01:51:14 +05:30
Adam Saudagar
0fef4aa22c moved version to version.txt 2023-02-10 11:12:10 +05:30
Adam Saudagar
cc926bf5fd
added auto publish github action 2023-02-10 10:21:04 +05:30
Adam Saudagar
f93ea04d42 #131 fixed opencv version to reduce install time 2023-02-10 09:54:20 +05:30
Adam Saudagar
5acc7863bc #131 replaced dependencies to work with latest python version 2023-02-06 23:10:29 +05:30
Adam Saudagar
7331bc7824 updated urls for new server 2023-02-05 19:17:00 +05:30
Adam Saudagar
fe6cd012f5 removed useless pre preprocess step 2023-02-05 19:16:18 +05:30
Adam Saudagar
20c920adc9
Merge pull request #141 from fishyboteso/feature/update_numpy
Feature/update numpy
2022-10-01 15:31:29 +05:30
SemjonWilke
e0204ad205 update version to 0.5.14 2022-09-27 23:09:19 +02:00
SemjonWilke
74e96e4439 fix splash screen on first boot error 2022-09-27 23:03:41 +02:00
SemjonWilke
f925997731 update addons 2022-09-27 23:03:41 +02:00
SemjonWilke
70e4fad167 update numpy 2022-09-27 21:09:32 +02:00
Adam Saudagar
9e4c17f035 changed version 2022-06-30 22:51:54 +05:30
Adam Saudagar
dfc3c14c2c
Merge pull request #125 from fishyboteso/improvement/resilient-qr 2022-06-30 22:28:42 +05:30
Adam Saudagar
131d6bbf3c
Merge pull request #123 from fishyboteso/fixup/fullscreen_position 2022-06-30 22:20:22 +05:30
Shamuwel Ansari
ca771811b7 corrected function name 2022-06-30 20:41:53 +05:30
Adam Saudagar
016e378fdd refactored qr detection code to use window instead of directly cropped image, retrying qr reading instead of failing directly 2022-06-30 20:15:21 +05:30
Semjon
62a531c381 fix fullscreen fishy restart 2022-06-17 09:56:51 +02:00
Adam Saudagar
17a03c7142
Merge pull request #122 from fishyboteso/fixup/popup_icon
fix popup default configuration
2022-06-13 14:01:08 +05:30
Semjon
a5af567e14 update canvas of recorder_frame 2022-06-13 10:18:52 +02:00
Semjon
621d8d3549 update canvas of update_dialog 2022-06-13 10:17:15 +02:00
Semjon
a8ff0c5bc8 fix popup resizing 2022-06-13 10:11:04 +02:00
Semjon
f1f565c628 allways use icon.ico in popups 2022-06-13 09:40:50 +02:00
42 changed files with 878 additions and 443 deletions

46
.github/workflows/python-publish.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Upload Python Package
on:
push:
branches:
- main
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.changed-files-specific.outputs.any_changed }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # OR "2" -> To retrieve the preceding commit.
- name: Get changed files in the docs folder
id: changed-files-specific
uses: tj-actions/changed-files@v41
with:
files: fishy/version.txt # Alternatively using: `docs/**` or `docs`
deploy:
runs-on: ubuntu-latest
needs: check
if: ${{ needs.check.outputs.changed == 'true' }}
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}

View File

@ -1,6 +1,7 @@
include LICENSE
include README.md
include requirements.txt
include fishy/version.txt
include fishy/icon.ico
include fishy/fishybot_logo.png
include fishy/sound.mp3

View File

@ -1,7 +1,7 @@
# Fishybot ESO 🎣
Auto fishing bot for Elder Scrolls Online. The Bot automatically fishes until the fishing hole disappears. It also sends notification via discord when it stops fishing. We also have a leaderboard for the amount of fishes you caught. Become the master fisher and swim in perfect roes 😉
Botting does violate ESO's terms of service, so technically you could get banned. But this bot doesn't read or write memory from ESO so they won't know you are using a bot. **This software doesn't come with any Liability or Warranty, I am not responsible if you do get banned.**
Botting does violate ESO's terms of service, so you could get banned. **This software doesn't come with any Liability or Warranty, I am not responsible if you do get banned.**
- Check out the [Showcase Video](https://www.youtube.com/watch?v=THQ66lG4ImU).
- [How to Install ?](https://github.com/fishyboteso/fishyboteso/wiki/Installation)

View File

@ -1,4 +1,5 @@
from fishy import constants
import os
from pathlib import Path
# this prevents importing from package while setup
@ -7,4 +8,4 @@ def main():
actual_main()
__version__ = constants.version
__version__ = (Path(os.path.dirname(__file__)) / "version.txt").read_text()

View File

@ -1,36 +1,27 @@
import ctypes
import logging
import os
import sys
import win32con
import win32gui
import fishy
from fishy.gui import GUI, splash, update_dialog, check_eula
from fishy.gui import GUI, update_dialog, check_eula
from fishy import helper, web
from fishy.engine.common.event_handler import EngineEventHandler
from fishy.gui.log_config import GuiLogger
from fishy.gui.splash import Splash
from fishy.helper import hotkey
from fishy.helper.active_poll import active
from fishy.helper.config import config
from fishy.helper.hotkey.hotkey_process import hotkey
from fishy.helper.migration import Migration
def check_window_name(title):
titles = ["Command Prompt", "PowerShell", "Fishy"]
for t in titles:
if t in title:
return True
return False
from fishy.osservices.os_services import os_services
# noinspection PyBroadException
def initialize(window_to_hide):
def initialize():
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()
@ -38,58 +29,66 @@ def initialize(window_to_hide):
logging.error("Couldn't create a session, some features might not work")
logging.debug(f"created session {new_session}")
try:
is_admin = os.getuid() == 0
except AttributeError:
is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
if is_admin:
if os_services.is_admin():
logging.info("Running with admin privileges")
if not config.get("debug", False) and check_window_name(win32gui.GetWindowText(window_to_hide)):
win32gui.ShowWindow(window_to_hide, win32con.SW_HIDE)
if not config.get("debug", False):
os_services.hide_terminal()
helper.install_thread_excepthook()
sys.excepthook = helper.unhandled_exception_logging
helper.install_required_addons()
def on_gui_load(gui, splash, logger):
splash.finish()
update_dialog.check_update(gui)
logger.connect(gui)
def main():
print("launching please wait...")
if not os_services.init():
print("platform not supported")
return
config.init()
if not check_eula():
return
finish_splash = splash.start()
logger = GuiLogger()
config.start_backup_scheduler()
active.init()
hotkey.init()
def on_gui_load():
finish_splash()
update_dialog.check_update(gui)
logger.connect(gui)
window_to_hide = win32gui.GetForegroundWindow()
splash = Splash()
bot = EngineEventHandler(lambda: gui)
gui = GUI(lambda: bot, on_gui_load)
gui = GUI(lambda: bot, lambda: on_gui_load(gui, splash, logger))
logger = GuiLogger()
hotkey.init()
active.init()
hotkey.start()
try:
config.init()
if not check_eula():
return
logging.info(f"Fishybot v{fishy.__version__}")
initialize(window_to_hide)
logging.info(f"Fishybot v{fishy.__version__}")
gui.start()
active.start()
splash.start()
config.start_backup_scheduler()
bot.start_event_handler() # main thread loop
initialize()
hotkey.stop()
active.stop()
config.stop()
bot.stop()
hotkey.start()
gui.start()
active.start()
bot.start_event_handler() # main thread loop
except KeyboardInterrupt:
print("caught KeyboardInterrupt, Stopping main thread")
finally:
gui.stop()
hotkey.stop()
active.stop()
config.stop()
bot.stop()
if __name__ == "__main__":

View File

@ -1,11 +1,17 @@
apiversion = 2
current_version_url = "https://raw.githubusercontent.com/fishyboteso/fishyboteso/main/fishy/version.txt"
# removed since 0.5.3
chalutier = ("Chalutier", "https://www.esoui.com/downloads/dl2934/Chalutier_1.1.4.zip", 114)
chalutier = ("Chalutier", "https://cdn.esoui.com/downloads/file2934/Chalutier_1.3.zip", 130)
# addons used
lam2 = ("LibAddonMenu-2.0", "https://www.esoui.com/downloads/dl7/LibAddonMenu-2.0r32.zip", 32)
fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/releases/download/v1.2.0/FishyQR.zip", 120)
libgps = ("LibGPS", "https://cdn.esoui.com/downloads/file601/LibGPS_3_0_3.zip", 30)
lam2 = ("LibAddonMenu-2.0", "https://cdn.esoui.com/downloads/file7/LibAddonMenu-2.0r34.zip", 34)
fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/releases/download/v1.8/FishyQR-1.8.zip", 180)
fishyfsm = ("FishingStateMachine", "https://github.com/fishyboteso/FishingStateMachine/releases/download/fsm_v1.1/FishingStateMachine-1.1.zip", 110)
libgps = ("LibGPS", "https://cdn.esoui.com/downloads/file601/LibGPS_v3.3.0.zip", 69)
libmapping = ("LibMapPing", "https://cdn.esoui.com/downloads/file1302/LibMapPing_2_0_0.zip", 1236)
libdl = ("LibDebugLogger", "https://cdn.esoui.com/downloads/file2275/LibDebugLogger_2_5_1.zip", 263)
libchatmsg = ("LibChatMessage", "https://cdn.esoui.com/downloads/file2382/LibChatMessage_1_2_0.zip", 105)
version = "0.5.12"
d3dshot_git = "git+https://github.com/fauskanger/D3DShot.git#egg=D3DShot"

View File

@ -1 +1,2 @@
from fishy.engine.semifisher.engine import SemiFisherEngine
from fishy.engine.fullautofisher.engine import FullAuto

View File

@ -65,7 +65,7 @@ class IEngine:
# noinspection PyBroadException
def _crash_safe(self):
logging.debug(f"starting {self.name} engine thread")
self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name=f"{self.name} debug")
self.window = WindowClient()
self.gui.bot_started(True)
try:
self.run()

View File

@ -3,20 +3,37 @@ import re
import cv2
import numpy as np
from fishy.engine.common.window import WindowClient
detector = cv2.QRCodeDetector()
def image_pre_process(img):
scale_percent = 100 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
return img
# noinspection PyBroadException
def get_values(window: WindowClient):
values = None
for _ in range(6):
img = window.processed_image()
if img is None:
logging.debug("Couldn't capture window.")
continue
if not window.crop:
window.crop = _get_qr_location(img)
if not window.crop:
logging.debug("FishyQR not found.")
continue
values = _get_values_from_image(img)
if not values:
window.crop = None
logging.debug("Values not able to read.")
continue
break
return values
def get_qr_location(image):
def _get_qr_location(image):
"""
code from https://stackoverflow.com/a/45770227/4512396
"""
@ -29,16 +46,15 @@ def get_qr_location(image):
return [int(x) for x in [p[0][0], p[0][1], p[1][0], p[2][1]]]
# noinspection PyBroadException
def get_values_from_image(img):
def _get_values_from_image(img):
h, w = img.shape
points = np.array([[(0, 0), (w, 0), (w, h), (0, h)]])
code = detector.decode(img, points)[0]
return parse_qr_code(code)
return _parse_qr_code(code)
# this needs to be updated each time qr code format is changed
def parse_qr_code(code):
def _parse_qr_code(code):
if not code:
return None
match = re.match(r'^(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+),(\d+)$', code)

View File

@ -0,0 +1,122 @@
import logging
import subprocess
import traceback
from abc import ABC, abstractmethod
from functools import partial
from typing import Optional
import numpy as np
from numpy import ndarray
from fishy import constants
from fishy.helper.config import config
from fishy.osservices.os_services import os_services
class IScreenShot(ABC):
@abstractmethod
def setup(self) -> bool:
...
@abstractmethod
def grab(self) -> ndarray:
...
def get_monitor_id(monitors_iterator, get_top_left) -> Optional[int]:
monitor_rect = os_services.get_monitor_rect()
if monitor_rect is None:
logging.error("Game window not found")
return None
for i, m in enumerate(monitors_iterator):
top, left = get_top_left(m)
if top == monitor_rect[1] and left == monitor_rect[0]:
return i
return None
class MSS(IScreenShot):
def __init__(self):
from mss import mss
self.monitor_id = None
self.sct = mss()
def setup(self) -> bool:
self.monitor_id = get_monitor_id(self.sct.monitors, lambda m: (m["top"], m["left"]))
return self.monitor_id is not None
# noinspection PyTypeChecker
def grab(self) -> ndarray:
sct_img = self.sct.grab(self.sct.monitors[self.monitor_id])
return np.array(sct_img)
class PyAutoGUI(IScreenShot):
def __init__(self):
self.monitor_rect = None
def setup(self) -> bool:
from PIL import ImageGrab
ImageGrab.grab = partial(ImageGrab.grab, all_screens=True)
self.monitor_rect = os_services.get_monitor_rect()
return True
def grab(self) -> ndarray:
import pyautogui
image = pyautogui.screenshot()
img = np.array(image)
crop = self.monitor_rect
img = img[crop[1]:crop[3], crop[0]:crop[2]]
return img
class D3DShot(IScreenShot):
# noinspection PyPackageRequirements
def __init__(self):
try:
import d3dshot
except ImportError:
logging.info("Installing d3dshot please wait...")
subprocess.call(["python", "-m", "pip", "install", constants.d3dshot_git])
import d3dshot
self.d3 = d3dshot.create(capture_output="numpy")
def setup(self) -> bool:
monitor_id = get_monitor_id(self.d3.displays, lambda m: (m.position["top"], m.position["left"]))
if monitor_id is None:
return False
self.d3.display = self.d3.displays[monitor_id]
return True
def grab(self) -> ndarray:
return self.d3.screenshot()
LIBS = [PyAutoGUI, MSS, D3DShot]
# noinspection PyBroadException
def create() -> Optional[IScreenShot]:
# Initialize a variable to hold the preferred library index
preferred_lib_index = config.get("sslib", 0)
# Create a list of library indices to try, starting with the preferred one
lib_indices = [preferred_lib_index] + [i for i in range(len(LIBS)) if i != preferred_lib_index]
for index in lib_indices:
lib = LIBS[index]
try:
lib_instance = lib()
if lib_instance.setup():
# testing grab once
ss = lib_instance.grab()
if ss.shape:
logging.debug(f"Using {lib.__name__} as the screenshot library.")
return lib_instance
except Exception:
logging.warning(f"Setup failed for {lib.__name__} with error: {traceback.format_exc()}. Trying next library...")
return None

View File

@ -1,4 +1,5 @@
import logging
import uuid
from typing import List
import cv2
@ -7,26 +8,52 @@ import imutils
from fishy.engine.common import window_server
from fishy.engine.common.window_server import Status, WindowServer
from fishy.helper import helper
from fishy.helper.config import config
class WindowClient:
clients: List['WindowClient'] = []
def __init__(self, crop=None, color=None, scale=None, show_name=None):
def __init__(self):
"""
create a window instance with these pre process
:param crop: [x1,y1,x2,y2] array defining the boundaries to crop
:param color: color to use example cv2.COLOR_RGB2HSV
:param scale: scaling the window
"""
self.color = color
self.crop = crop
self.scale = scale
self.show_name = show_name
self.crop = None
self.scale = None
self.show_name = f"window client {len(WindowClient.clients)}"
if len(WindowClient.clients) == 0:
window_server.start()
WindowClient.clients.append(self)
if len(WindowClient.clients) > 0 and WindowServer.status != Status.RUNNING:
window_server.start()
@staticmethod
def running():
return WindowServer.status == Status.RUNNING
def processed_image(self, func=None):
"""
processes the image using the function provided
:param func: function to process image
:return: processed image
"""
if WindowServer.status == Status.CRASHED:
return None
img = self._get_capture()
if img is None:
return None
if func:
img = func(img)
if config.get("show_grab", 0):
self._show(img)
return img
def destroy(self):
if self in WindowClient.clients:
@ -34,11 +61,7 @@ class WindowClient:
if len(WindowClient.clients) == 0:
window_server.stop()
@staticmethod
def running():
return WindowServer.status == Status.RUNNING
def get_capture(self):
def _get_capture(self):
"""
copies the recorded screen and then pre processes its
:return: game window image
@ -56,8 +79,7 @@ class WindowClient:
if temp_img is None or temp_img.size == 0:
return None
if self.color is not None:
temp_img = cv2.cvtColor(temp_img, self.color)
temp_img = cv2.cvtColor(temp_img, cv2.COLOR_RGB2GRAY)
if self.crop is not None:
temp_img = temp_img[self.crop[1]:self.crop[3], self.crop[0]:self.crop[2]]
@ -65,51 +87,18 @@ class WindowClient:
if self.scale is not None:
temp_img = cv2.resize(temp_img, (self.scale[0], self.scale[1]), interpolation=cv2.INTER_AREA)
# need ot check again after crop/resize
if temp_img.size == 0:
return None
return temp_img
def processed_image(self, func=None):
"""
processes the image using the function provided
:param func: function to process image
:return: processed image
"""
if WindowServer.status == Status.CRASHED:
return None
img = self.get_capture()
if img is None:
return None
if func is None:
return img
return func(img)
def show(self, to_show, resize=None, func=None):
# noinspection PyUnresolvedReferences
def _show(self, img):
"""
Displays the processed image for debugging purposes
:param to_show: false to destroy the window
:param resize: scale the image to make small images more visible
:param func: function to process the image
"""
if WindowServer.status == Status.CRASHED:
return
if not self.show_name:
logging.warning("You need to assign a name first")
return
if not to_show:
cv2.destroyWindow(self.show_name)
return
img = self.processed_image(func)
if img is None:
return
if resize is not None:
img = imutils.resize(img, width=resize)
cv2.imshow(self.show_name, img)
cv2.waitKey(25)
helper.save_img(self.show_name, img, True)

View File

@ -1,15 +1,16 @@
import logging
import math
from enum import Enum
from threading import Thread
import d3dshot
import pywintypes
import win32api
import win32gui
from ctypes import windll
import cv2
import numpy as np
from mss.base import MSSBase
from fishy.engine.common import screenshot
from fishy.helper import helper
from fishy.helper.config import config
from fishy.helper.helper import print_exc
from fishy.osservices.os_services import os_services
class Status(Enum):
@ -22,12 +23,11 @@ class WindowServer:
"""
Records the game window, and allows to create instance to process it
"""
Screen = None
Screen: np.ndarray = None
windowOffset = None
hwnd = None
status = Status.STOPPED
d3: d3dshot.D3DShot = d3dshot.create(capture_output="numpy")
monitor_top_left = None
sslib = None
crop = None
def init():
@ -35,23 +35,39 @@ def init():
Executed once before the main loop,
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.d3.display = next((m for m in WindowServer.d3.displays if m.hmonitor == monitor_id), None)
except pywintypes.error:
logging.error("Game window not found")
WindowServer.sslib = screenshot.create()
# Check if the screenshot library was successfully created
if WindowServer.sslib is None:
logging.error("Failed to create screenshot library instance")
WindowServer.status = Status.CRASHED
return
crop = os_services.get_game_window_rect()
if crop is None or not WindowServer.sslib.setup():
logging.error("Game window not found by window_server")
WindowServer.status = Status.CRASHED
return
WindowServer.crop = crop
WindowServer.status = Status.RUNNING
def get_cropped_screenshot():
ss = WindowServer.sslib.grab()
if config.get("show_grab", 0):
helper.save_img("full screen", ss)
crop = WindowServer.crop
cropped_ss = ss[crop[1]:crop[3], crop[0]:crop[2]]
if cropped_ss.size == 0:
return None
if config.get("show_grab", 0):
helper.save_img("Game window", cropped_ss)
return cropped_ss
def loop():
@ -59,25 +75,10 @@ def loop():
Executed in the start of the main loop
finds the game window location and captures it
"""
WindowServer.Screen = get_cropped_screenshot()
temp_screen = WindowServer.d3.screenshot()
rect = win32gui.GetWindowRect(WindowServer.hwnd)
client_rect = win32gui.GetClientRect(WindowServer.hwnd)
fullscreen = WindowServer.d3.display.resolution[1] == (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")
if WindowServer.Screen is None:
logging.error("Couldn't find the game window")
WindowServer.status = Status.CRASHED

View File

@ -3,6 +3,7 @@ import math
import time
from threading import Thread
from fishy.engine.common import qr_detection
from pynput import keyboard, mouse
from fishy.engine import SemiFisherEngine
@ -12,12 +13,11 @@ 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.common.qr_detection import (get_qr_location,
get_values_from_image, image_pre_process)
from fishy.engine.semifisher import fishing_mode
from fishy.engine.semifisher.fishing_mode import FishingMode
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()
kb = keyboard.Controller()
@ -53,14 +53,20 @@ class FullAuto(IEngine):
return
# 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...")
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:
logging.info("starting in 2 secs...")
time.sleep(2)
if not self._pre_run_checks():
if not (type(self.mode) is Calibrator) and not self.calibrator.all_calibrated():
logging.error("you need to calibrate first")
return
if not qr_detection.get_values(self.window):
logging.error("FishyQR not found, if its not hidden, try to drag it around, "
"or increase/decrease its size and try again\nStopping engine...")
return
if config.get("tabout_stop", 1):
@ -73,33 +79,11 @@ class FullAuto(IEngine):
logging.error("exception occurred while running full auto mode")
print_exc()
def _pre_run_checks(self):
if self.window.get_capture() is None:
logging.error("Game window not found")
return False
self.window.crop = get_qr_location(self.window.get_capture())
if self.window.crop is None:
logging.error("FishyQR not found, try to drag it around and try again")
return False
if not (type(self.mode) is Calibrator) and not self.calibrator.all_calibrated():
logging.error("you need to calibrate first")
return False
return True
def start_show(self):
def func():
while self.start and WindowClient.running():
self.window.show(self.show_crop, func=image_pre_process)
Thread(target=func).start()
def stop_on_inactive(self):
def func():
logging.debug("stop on inactive started")
wait_until(lambda: not is_eso_active() or not self.start)
if self.start and not is_eso_active():
wait_until(lambda: not os_services.is_eso_active() or not self.start)
if self.start and not os_services.is_eso_active():
self.turn_off()
logging.debug("stop on inactive stopped")
Thread(target=func).start()
@ -111,8 +95,7 @@ class FullAuto(IEngine):
todo find a better way of handling None: switch from start bool to state which knows
todo its waiting for qr which doesn't block the engine when commanded to close
"""
img = self.window.processed_image(func=image_pre_process)
values = get_values_from_image(img)
values = qr_detection.get_values(self.window)
return values[:3] if values else None
def move_to(self, target) -> bool:
@ -137,9 +120,12 @@ class FullAuto(IEngine):
walking_time = dist / self.calibrator.move_factor
logging.debug(f"walking for {walking_time}")
kb.press('w')
forward_key = config.get("forward_key", 'w')
kb.press(forward_key)
time.sleep(walking_time)
kb.release('w')
kb.release(forward_key)
logging.debug("done")
# todo: maybe check if it reached the destination before returning true?
return True
@ -177,7 +163,7 @@ class FullAuto(IEngine):
_hole_found_flag = FishingMode.CurrentMode in valid_states
# if vertical movement is disabled
if not config.get("look_for_hole", 1):
if not config.get("look_for_hole", 0):
return _hole_found_flag
t = 0

View File

@ -20,26 +20,6 @@ kb = keyboard.Controller()
offset = 0
def get_crop_coords(window):
img = window.get_capture()
img = cv2.inRange(img, 0, 1)
cnt, h = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
"""
code from https://stackoverflow.com/a/45770227/4512396
"""
for i in range(len(cnt)):
area = cv2.contourArea(cnt[i])
if 5000 < area < 100000:
mask = np.zeros_like(img)
cv2.drawContours(mask, cnt, i, 255, -1)
x, y, w, h = cv2.boundingRect(cnt[i])
return x, y + offset, x + w, y + h - offset
return None
def _update_factor(key, value):
full_auto_factors = config.get("full_auto_factors", {})
full_auto_factors[key] = value
@ -83,9 +63,11 @@ class Calibrator(IMode):
x1, y1, rot1 = coords
kb.press('w')
forward_key = config.get("forward_key", 'w')
kb.press(forward_key)
time.sleep(walking_time)
kb.release('w')
kb.release(forward_key)
time.sleep(0.5)
coords = self.engine.get_coords()

View File

@ -99,7 +99,7 @@ class Recorder(IMode):
def _open_save_popup(self):
top = PopUp(empty_function, self.engine.get_gui()._root, background=self.engine.get_gui()._root["background"])
controls_frame = ttk.Frame(top)
recorder_frame = ttk.Frame(top)
top.title("Save Recording?")
button = [-1]
@ -109,14 +109,15 @@ class Recorder(IMode):
top.quit_top()
selected_text = f"\n\nSelected: {os.path.basename(config.get('full_auto_rec_file'))}" if config.get('edit_recorder_mode') else ""
ttk.Label(controls_frame, text=f"Do you want to save the recording?{selected_text}").grid(row=0, column=0, columnspan=3, pady=(0, 5))
ttk.Label(recorder_frame, text=f"Do you want to save the recording?{selected_text}").grid(row=0, column=0, columnspan=3, pady=(0, 5))
_overwrite = tk.NORMAL if config.get("edit_recorder_mode") else tk.DISABLED
ttk.Button(controls_frame, text="Overwrite", command=lambda: button_pressed(0), state=_overwrite).grid(row=1, column=0, pady=(5, 0))
ttk.Button(controls_frame, text="Save As", command=lambda: button_pressed(1)).grid(row=1, column=1)
ttk.Button(controls_frame, text="Cancel", command=lambda: button_pressed(2)).grid(row=1, column=2)
ttk.Button(recorder_frame, text="Overwrite", command=lambda: button_pressed(0), state=_overwrite).grid(row=1, column=0, pady=(5, 0))
ttk.Button(recorder_frame, text="Save As", command=lambda: button_pressed(1)).grid(row=1, column=1)
ttk.Button(recorder_frame, text="Cancel", command=lambda: button_pressed(2)).grid(row=1, column=2)
controls_frame.pack(padx=(5, 5), pady=(5, 5))
recorder_frame.pack(padx=(5, 5), pady=(5, 5))
recorder_frame.update()
top.start()
return button[0]

View File

@ -4,10 +4,11 @@ import typing
from threading import Thread
from typing import Callable, Optional
from fishy.engine.common import qr_detection
from fishy.engine.semifisher.fishing_mode import FishingMode
from fishy.engine.common.IEngine import IEngine
from fishy.engine.common.qr_detection import get_qr_location, get_values_from_image, image_pre_process
from fishy.engine.common.window import WindowClient
from fishy.engine.semifisher import fishing_event, fishing_mode
from fishy.engine.semifisher.fishing_event import FishEvent
@ -35,15 +36,6 @@ class SemiFisherEngine(IEngine):
Thread(target=self._wait_and_check).start()
time.sleep(0.2)
capture = self.window.get_capture()
if capture is None:
logging.error("couldn't get game capture")
return
self.window.crop = get_qr_location(capture)
if not self.window.crop:
logging.error("FishyQR not found, try to drag it around and try again")
return
fishing_event.init()
# noinspection PyBroadException
@ -59,15 +51,9 @@ class SemiFisherEngine(IEngine):
def _engine_loop(self):
skip_count = 0
while self.state == 1 and WindowClient.running():
capture = self.window.processed_image(func=image_pre_process)
# if window server crashed
if capture is None:
logging.error("Couldn't capture window stopping engine")
return
# crop qr and get the values from it
self.values = get_values_from_image(capture)
self.values = qr_detection.get_values(self.window)
# if fishyqr fails to get read multiple times, stop the bot
if not self.values:
if skip_count >= 5:

View File

@ -12,10 +12,10 @@ from playsound import playsound
from fishy import web
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.config import config
from fishy.helper.helper import is_eso_active
from fishy.osservices.os_services import os_services
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 wrapper():
if not is_eso_active():
if not os_services.is_eso_active():
logging.warning("ESO window is not focused")
return
func()

View File

@ -13,11 +13,20 @@ from fishy.helper.config import config
from fishy.helper.popup import PopUp
def start_fullfisher_config(gui: 'GUI'):
top = PopUp(helper.empty_function, gui._root, background=gui._root["background"])
def del_entry_key(event):
event.widget.delete(0, "end")
event.widget.insert(0, str(event.char))
def start_fullfisher_config(gui: 'GUI' ):
def save():
gui.config.set("forward_key", forward_key_entry.get())
top = PopUp(save, gui._root, background=gui._root["background"])
controls_frame = ttk.Frame(top)
top.title("Config")
def file_name():
file = config.get("full_auto_rec_file", None)
if file is None:
@ -48,7 +57,7 @@ def start_fullfisher_config(gui: 'GUI'):
mode_var = tk.IntVar(value=config.get("full_auto_mode", 0))
edit_var = tk.IntVar(value=config.get("edit_recorder_mode", 0))
tabout_var = tk.IntVar(value=config.get("tabout_stop", 1))
look_for_hole = tk.IntVar(value=config.get("look_for_hole", 1))
look_for_hole = tk.IntVar(value=config.get("look_for_hole", 0))
row = 0
ttk.Label(controls_frame, text="Calibration: ").grid(row=row, column=0, pady=(5, 0))
@ -63,6 +72,14 @@ def start_fullfisher_config(gui: 'GUI'):
row += 1
ttk.Label(controls_frame, text="Forward key:").grid(row=row, column=0)
forward_key_entry = ttk.Entry(controls_frame, justify=tk.CENTER)
forward_key_entry.grid(row=row, column=1)
forward_key_entry.insert(0, config.get("forward_key", "w"))
forward_key_entry.bind("<KeyRelease>", del_entry_key)
row += 1
ttk.Label(controls_frame, text="Edit Mode: ").grid(row=row, column=0)
edit_state = tk.NORMAL if config.get("full_auto_mode", 0) == FullAutoMode.Recorder.value else tk.DISABLED
edit_cb = ttk.Checkbutton(controls_frame, variable=edit_var, state=edit_state, command=lambda: config.set("edit_recorder_mode", edit_var.get()))
@ -90,6 +107,8 @@ def start_fullfisher_config(gui: 'GUI'):
ttk.Label(controls_frame, text="Use semi-fisher config for rest").grid(row=row, column=0, columnspan=2, pady=(20, 0))
controls_frame.pack(padx=(5, 5), pady=(5, 10))
controls_frame.update()
top.start()
@ -109,9 +128,7 @@ def start_semifisher_config(gui: 'GUI'):
if web.sub():
gui._notify.set(1)
def del_entry_key(event):
event.widget.delete(0, "end")
event.widget.insert(0, str(event.char))
top = PopUp(save, gui._root, background=gui._root["background"])
controls_frame = ttk.Frame(top)
@ -119,7 +136,7 @@ def start_semifisher_config(gui: 'GUI'):
ttk.Label(controls_frame, text="Notification:").grid(row=0, column=0)
gui._notify = tk.IntVar(0)
gui._notify = tk.IntVar()
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'] = tk.DISABLED
@ -149,6 +166,8 @@ def start_semifisher_config(gui: 'GUI'):
jitter.grid(row=5, column=1)
controls_frame.pack(padx=(5, 5), pady=(5, 5))
controls_frame.update()
top.start()
@ -156,4 +175,5 @@ if __name__ == '__main__':
from fishy.gui import GUI
gui = GUI(lambda: IEngineHandler())
gui.call_in_thread(lambda: start_semifisher_config(gui))
gui.call_in_thread(lambda: start_fullfisher_config(gui))
gui.create()

View File

@ -69,6 +69,9 @@ class GUI:
def create(self):
main_gui._create(self)
def stop(self):
self._destroyed = True
def start(self):
self._thread.start()

View File

@ -2,19 +2,24 @@ import logging
import time
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
import typing
from functools import partial
import os
from fishy.gui import update_dialog
from ttkthemes import ThemedTk
from fishy import helper
from fishy.helper import helper
from fishy.web import web
from ..constants import fishyqr
from ..engine.common import screenshot
from ..helper.config import config
from .discord_login import discord_login
from ..helper.hotkey.hotkey_process import hotkey
from ..helper.hotkey.process import Key
from ..osservices.os_services import os_services
if typing.TYPE_CHECKING:
from . import GUI
@ -46,7 +51,7 @@ def _create(gui: 'GUI'):
gui.login.set(1 if login > 0 else 0)
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 Shortcut", command=lambda: os_services.create_shortcut(False))
# filemenu.add_command(label="Create Anti-Ghost Shortcut", command=lambda: helper.create_shortcut(True))
def _toggle_mode():
@ -61,6 +66,7 @@ def _create(gui: 'GUI'):
def update():
config.delete("dont_ask_update")
update_dialog.check_update(gui, True)
filemenu.add_command(label="Update", command=update)
def installer():
@ -70,6 +76,7 @@ def _create(gui: 'GUI'):
else:
helper.install_required_addons(True)
filemenu.entryconfigure(4, label="Remove FishyQR")
chaEntry = "Remove FishyQR" if helper.addon_exists(fishyqr[0]) else "Install FishyQR"
filemenu.add_command(label=chaEntry, command=installer)
menubar.add_cascade(label="Options", menu=filemenu)
@ -78,6 +85,47 @@ def _create(gui: 'GUI'):
debug_menu.add_command(label="Check QR Value",
command=lambda: gui.engine.check_qr_val())
def toggle_show_grab():
new_val = 1 - config.get("show_grab", 0)
show_grab_var.set(new_val)
config.set("show_grab", new_val)
if new_val:
logging.info(f"Screenshots taken by fishy will be saved in {helper.save_img_path()}")
messagebox.showwarning("Warning", "Screenshots taken by Fishy will be saved in Documents.")
logging.info(f"Screenshots taken by Fishy will be saved in {helper.save_img_path()}")
else:
delete_screenshots = messagebox.askyesno("Confirmation", "Do you want to delete the saved screenshots?")
if delete_screenshots:
# Delete the saved screenshots
folder_path = helper.save_img_path()
try:
os.rmdir(folder_path) # Deletes the folder
logging.info("Saved screenshots folder has been deleted.")
except OSError as e:
logging.error(f"Error occurred while deleting the folder: {e}")
else:
logging.info("Saved screenshots will be preserved.")
show_grab_var = tk.IntVar()
show_grab_var.set(config.get("show_grab", 0))
debug_menu.add_checkbutton(label="Save Screenshots", variable=show_grab_var, command=lambda: toggle_show_grab(), onvalue=1)
if config.get("show_grab", 0):
logging.info(f"Save Screenshots is On, images will be saved in {helper.save_img_path()}")
def select_sslib(selected_i):
config.set("sslib", selected_i)
sslib_var.set(selected_i)
sslib = tk.Menu(debug_menu, tearoff=False)
sslib_var = tk.IntVar()
sslib_var.set(config.get("sslib", 0))
for i, lib in enumerate(screenshot.LIBS):
sslib.add_checkbutton(label=lib.__name__, variable=sslib_var,
command=partial(select_sslib, i), onvalue=i)
debug_menu.add_cascade(label="Screenshot Lib", menu=sslib)
debug_var = tk.IntVar()
debug_var.set(int(config.get('debug', False)))
@ -89,7 +137,8 @@ def _create(gui: 'GUI'):
menubar.add_cascade(label="Debug", menu=debug_menu)
help_menu = tk.Menu(menubar, tearoff=0)
help_menu.add_command(label="Need Help?", command=lambda: helper.open_web("https://github.com/fishyboteso/fishyboteso/wiki"))
help_menu.add_command(label="Need Help?",
command=lambda: helper.open_web("https://github.com/fishyboteso/fishyboteso/wiki"))
help_menu.add_command(label="Donate", command=lambda: helper.open_web("https://paypal.me/AdamSaudagar"))
menubar.add_cascade(label="Help", menu=help_menu)
@ -125,9 +174,13 @@ def _create(gui: 'GUI'):
_apply_theme(gui)
gui._root.update()
gui._root.minsize(gui._root.winfo_width() + 10, gui._root.winfo_height() + 10)
if config.get("win_loc") is not None:
gui._root.geometry(config.get("win_loc"))
gui._root.geometry(config.get("win_loc").split(":")[-1])
if config.get("win_loc").split(":")[0] == "zoomed":
gui._root.update()
gui._root.state("zoomed")
hotkey.hook(Key.F9, gui.funcs.start_engine)
@ -137,7 +190,13 @@ def _create(gui: 'GUI'):
if not tk.messagebox.askyesno(title="Quit?", message="Bot engine running. Quit Anyway?"):
return
config.set("win_loc", gui._root.geometry())
if gui._root.state() == "zoomed":
# setting it to normal first is done to keep user-changed geometry values
gui._root.state("normal")
config.set("win_loc", "zoomed" + ":" + gui._root.geometry())
else:
config.set("win_loc", gui._root.state() + ":" + gui._root.geometry())
gui._destroyed = True
gui._root.protocol("WM_DELETE_WINDOW", set_destroy)

View File

@ -10,54 +10,53 @@ from fishy.helper import helper
from fishy.helper.config import config
def show(win_loc, q):
logging.debug("started splash process")
dim = (300, 200)
top = tk.Tk()
class Splash:
def __init__(self):
self.q = Queue()
self.process = Process(name=Splash.__name__, target=self.show, args=(config.get("win_loc"), self.q,))
top.overrideredirect(True)
top.lift()
top.attributes('-topmost', True)
def finish(self):
self.q.put("stop")
top.title("Loading...")
top.resizable(False, False)
top.iconbitmap(helper.manifest_file('icon.ico'))
def start(self):
self.process.start()
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=tk.NW, image=top.image)
def show(self, win_loc, q):
logging.debug("started splash process")
dim = (300, 200)
top = tk.Tk()
# Position splash at the center of the main window
top.overrideredirect(True)
top.lift()
top.attributes('-topmost', True)
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.title("Loading...")
top.resizable(False, False)
top.iconbitmap(helper.manifest_file('icon.ico'))
def waiting():
q.get()
time.sleep(0.2)
running[0] = False
Thread(target=waiting).start()
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=tk.NW, image=top.image)
running = [True]
while running[0]:
top.update()
time.sleep(0.1)
# Position splash at the center of the main window
top.destroy()
logging.debug("ended splash process")
default_loc = (str(top.winfo_reqwidth()) + "+" + str(top.winfo_reqheight()) + "+" + "0" + "0")
loc = (win_loc or default_loc).split(":")[-1].split("+")[1:]
top.geometry("{}x{}+{}+{}".format(dim[0], dim[1], int(loc[0]) + int(dim[0] / 2), int(loc[1]) + int(dim[1] / 2)))
def waiting():
q.get()
time.sleep(0.2)
running[0] = False
def create_finish(q):
def finish():
q.put("stop")
Thread(target=waiting).start()
return finish
running = [True]
while running[0]:
top.update()
time.sleep(0.1)
def start():
q = Queue()
Process(target=show, args=(config.get("win_loc"), q,)).start()
return create_finish(q)
top.destroy()
logging.debug("ended splash process")

View File

@ -40,7 +40,7 @@ def _run_terms_window():
root.image = ImageTk.PhotoImage(root.image)
canvas.create_image(0, 0, anchor=tk.NW, image=root.image)
check_value = tk.IntVar(0)
check_value = tk.IntVar()
g1 = ttk.Frame(f)
ttk.Checkbutton(g1, command=disable_enable_button, variable=check_value).pack(side=tk.LEFT)

View File

@ -18,7 +18,6 @@ def _show(gui, currentversion, newversion, returns):
top = PopUp(helper.empty_function, gui._root)
top.title("A wild fishy update appeared!")
top.iconbitmap(helper.manifest_file('icon.ico'))
dialogLabel = tk.Label(top, text="There is a new fishy update available (" +
currentversion + "->" + newversion + "). Do you want to update now?")
@ -38,6 +37,7 @@ def _show(gui, currentversion, newversion, returns):
width=buttonWidth, compound="c")
dialogBtnYes.grid(row=2, column=1, padx=5, pady=5)
dialogBtnYes.focus_set()
dialogBtnYes.update()
top.protocol('WM_DELETE_WINDOW', _clickNo)
top.start()

View File

@ -1,5 +1,5 @@
from .config import Config
from .helper import (addon_exists, create_shortcut, create_shortcut_first,
from .helper import (addon_exists,
get_addonversion, get_savedvarsdir,
install_addon, install_thread_excepthook, manifest_file,
not_implemented, open_web, playsound_multiple,

View File

@ -14,12 +14,12 @@ class active:
return
active._scheduler = EventScheduler()
active._scheduler.start()
logging.debug("active scheduler initialized")
@staticmethod
def start():
web.ping()
active._scheduler.start()
active._scheduler.enter_recurring(60, 1, web.ping)
logging.debug("active scheduler started")

View File

@ -6,14 +6,8 @@ import logging
import re
import subprocess
import sys
import urllib.request
from os import execl
from bs4 import BeautifulSoup
def _hr_version(v):
return '.'.join([str(x) for x in v])
from fishy.web import web
def _normalize_version(v):
@ -38,46 +32,16 @@ def _normalize_version(v):
return rv
def _get_highest_version(_index, _pkg):
"""
Crawls web for latest version name then returns latest version
:param _index: website to check
:param _pkg: package name
:return: latest version normalized
"""
url = "{}/{}/".format(_index, _pkg)
html = urllib.request.urlopen(url)
if html.getcode() != 200:
raise Exception # not found
soup = BeautifulSoup(html.read(), "html.parser")
_versions = []
for link in soup.find_all('a'):
text = link.get_text()
try:
version = re.search(_pkg + r'-(.*)\.tar\.gz', text).group(1)
_versions.append(_normalize_version(version))
except AttributeError:
pass
if len(_versions) == 0:
raise Exception # no version
return max(_versions)
def _get_current_version():
"""
Gets the current version of the package installed
:return: version normalized
"""
import fishy
return _normalize_version(fishy.__version__)
index = "https://pypi.python.org/simple"
pkg = "fishy"
return fishy.__version__
def versions():
return _hr_version(_get_current_version()), _hr_version(_get_highest_version(index, pkg))
return _get_current_version(), web.get_highest_version()
def upgrade_avail():
@ -85,14 +49,18 @@ def upgrade_avail():
Checks if update is available
:return: boolean
"""
return _get_current_version() < _get_highest_version(index, pkg)
highest_version_normalized = _normalize_version(web.get_highest_version())
current_version_normalized = _normalize_version(_get_current_version())
return current_version_normalized < highest_version_normalized
def update_now(version):
"""
public function,
compares current version with the latest version (from web),
if current version is older, then it updates and restarts the script
calling this function updates fishy,
should be the last thing to be executed as this function will restart fishy
the flaw is handed by `EngineEventHandler.update_flag` which is the last thing to be stopped
"""
logging.info(f"Updating to v{version}, Please Wait...")
subprocess.call(["python", '-m', 'pip', 'install', '--upgrade', 'fishy', '--user'])

View File

@ -7,18 +7,28 @@ import logging
import os
# path to save the configuration file
from typing import Optional
import sys
from event_scheduler import EventScheduler
from fishy.osservices.os_services import os_services
def filename():
from fishy.helper.helper import get_documents
name = "fishy_config.json"
if "--test-server" in sys.argv:
name = "fishy_config_test.json"
else:
name = "fishy_config.json"
_filename = os.path.join(os.environ["HOMEDRIVE"], os.environ["HOMEPATH"], "Documents", name)
if os.path.exists(_filename):
return _filename
return os.path.join(get_documents(), name)
# fallback for OneDrive documents
return os.path.join(os_services.get_documents_path(), name)
temp_file = os.path.join(os.environ["TEMP"], "fishy_config.BAK")

18
fishy/helper/depless.py Normal file
View File

@ -0,0 +1,18 @@
"""
no imports from fishy itself here, or anything which depends on fishy
"""
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

View File

@ -7,22 +7,21 @@ import threading
import time
import traceback
import webbrowser
from datetime import datetime
from hashlib import md5
from io import BytesIO
from threading import Thread
from uuid import uuid1
from zipfile import ZipFile
import cv2
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
from fishy.constants import libgps, lam2, fishyqr
from fishy.constants import libgps, lam2, fishyqr, fishyfsm, libmapping, libdl, libchatmsg
from fishy.helper.config import config
from fishy.osservices.os_services import os_services
def playsound_multiple(path, count=2):
@ -110,55 +109,14 @@ def manifest_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():
# 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")
eso_path = os_services.get_eso_config_path()
return os.path.join(eso_path, "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")
eso_path = os_services.get_eso_config_path()
return os.path.join(eso_path, "live", "Addons")
def addon_exists(name, url=None, v=None):
@ -180,7 +138,7 @@ def get_addonversion(name, url=None, v=None):
def install_required_addons(force=False):
addons_req = [libgps, lam2, fishyqr]
addons_req = [libgps, lam2, fishyqr, fishyfsm, libmapping, libdl, libchatmsg]
addon_version = config.get("addon_version", {})
installed = False
for addon in addons_req:
@ -222,19 +180,11 @@ def remove_addon(name, url=None, v=None):
return 0
def get_documents():
return shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
def log_raise(msg):
logging.error(msg)
raise Exception(msg)
def is_eso_active():
return GetWindowText(GetForegroundWindow()) == "Elder Scrolls Online"
# noinspection PyProtectedMember,PyUnresolvedReferences
def _get_id(thread):
# returns id of the respective thread
@ -252,8 +202,26 @@ def kill_thread(thread):
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')
def print_exc():
logging.error(traceback.format_exc())
traceback.print_exc()
def save_img_path():
return os.path.join(os_services.get_documents_path(), "fishy_debug", "imgs")
def save_img(show_name, img, half=False):
img_path = os.path.join(save_img_path(), show_name)
if not os.path.exists(img_path):
os.makedirs(img_path)
if half:
img = cv2.resize(img, (0, 0), fx=0.5, fy=0.5)
t = time.strftime("%Y.%m.%d.%H.%M.%S")
cv2.imwrite(
os.path.join(img_path, f"{t}.jpg"),
img)

View File

@ -7,6 +7,7 @@ from typing import Dict, Optional, Callable
from playsound import playsound
from fishy import helper
from fishy.helper.config import config
from fishy.helper.hotkey import process
from fishy.helper.hotkey.process import Key
@ -57,13 +58,16 @@ class HotKey:
while True:
key = self.outq.get()
if key == "stop":
break
if key in Key:
callback = self._hotkeys[key]
if callback:
playsound(helper.manifest_file("beep.wav"), False)
if config.get("sound_notification", False):
playsound(helper.manifest_file("beep.wav"), False)
callback()
elif key == "stop":
break
time.sleep(0.1)

View File

@ -1,8 +1,8 @@
import logging
import fishy
from fishy.helper.auto_update import _normalize_version
from fishy.constants import version
from .config import config
@ -14,14 +14,14 @@ class Migration:
@staticmethod
def migrate():
prev_version = _normalize_version(config.get("prev_version", "0.0.0"))
current_version = _normalize_version(version)
current_version = _normalize_version(fishy.__version__)
if current_version > prev_version:
for v, f in migration_code:
if prev_version < _normalize_version(v) <= current_version:
logging.info(f"running migration for {v}")
f()
config.set("prev_version", version)
config.set("prev_version", fishy.__version__)

View File

@ -1,5 +1,6 @@
import time
from tkinter import Toplevel
from fishy import helper
def center(win):
@ -20,6 +21,7 @@ class PopUp(Toplevel):
self.running = True
self.quit_callback = quit_callback
self.protocol("WM_DELETE_WINDOW", self.quit_top)
self.iconbitmap(helper.manifest_file('icon.ico'))
def quit_top(self):
self.quit_callback()
@ -27,6 +29,7 @@ class PopUp(Toplevel):
self.running = False
def start(self):
self.minsize(self.winfo_width(), self.winfo_height())
self.grab_set()
center(self)
while self.running:

View File

29
fishy/osservices/linux.py Normal file
View 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

View File

@ -0,0 +1,81 @@
import inspect
import logging
import re
import sys
from abc import ABC, abstractmethod
from typing import Tuple, Optional
import platform
from fishy.helper.depless import singleton_proxy
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
"""
@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
View 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
fishy/version.txt Normal file
View File

@ -0,0 +1 @@
0.5.26

View File

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

View File

@ -2,8 +2,11 @@ import logging
import traceback
from functools import wraps
from fishy.web import web
def uses_session(f):
"""directly returns none if it couldn't get session, instead of running the function"""
@wraps(f)
def wrapper(*args, **kwargs):
from .web import get_session
@ -21,6 +24,9 @@ def fallback(default):
# noinspection PyBroadException
@wraps(f)
def wrapper(*args, **kwargs):
if not web.is_online():
return default
try:
return f(*args, **kwargs)
except Exception:

View File

@ -3,9 +3,9 @@ import sys
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"
domain = "https://fishyeso-test.definex.in"
else:
domain = "https://fishyeso.herokuapp.com"
domain = "https://fishyeso.definex.in"
user = domain + "/api/user"
notify = domain + "/api/notify"

View File

@ -1,6 +1,7 @@
import logging
import requests
from fishy import constants
from whatsmyip.ip import get_ip
from whatsmyip.providers import GoogleDnsProvider
@ -10,6 +11,11 @@ from . import urls
from .decorators import fallback, uses_session
_session_id = None
_online = True
def is_online():
return _online
@fallback(-1)
@ -18,7 +24,7 @@ def is_logged_in():
return -1
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.get(urls.discord, params=body)
response = requests.get(urls.discord, json=body)
logged_in = response.json()["discord_login"]
return 1 if logged_in else 0
@ -44,10 +50,10 @@ def logout():
@fallback(None)
def register_user():
def _register_user():
ip = get_ip(GoogleDnsProvider)
body = {"ip": ip, "apiversion": apiversion}
response = requests.post(urls.user, json=body)
response = requests.post(urls.user, json=body, timeout=10)
result = response.json()
return result["uid"]
@ -93,7 +99,7 @@ def is_subbed():
return False, False
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.get(urls.subscription, params=body)
response = requests.get(urls.subscription, json=body)
if response.status_code != 200:
return False, False
@ -111,7 +117,12 @@ def unsub():
def get_session(lazy=True):
global _session_id
"""
this doesn't have @fallback as this doesn't actually make any web calls directly
this web call needs to be the first thing to be called, as it sets the online status
todo maybe shift this to web.init() or something to signify that
"""
global _session_id, _online
# lazy loading logic
if lazy and _session_id is not None:
@ -122,22 +133,22 @@ def get_session(lazy=True):
# then create session
if uid:
_session_id, online = _create_new_session(uid)
_session_id, _online = _create_new_session(uid)
# if not, create new id then try creating session again
else:
uid = register_user()
uid = _register_user()
config.set("uid", uid, True)
logging.debug(f"New User, generated new uid: {uid}")
if uid:
_session_id, online = _create_new_session(uid)
_session_id, _online = _create_new_session(uid)
else:
online = False
_online = False
# when the user is already registered but session is not created as uid is not found
if online and not _session_id:
# when the user is already registered but session is not created as uid is not found by the server
if _online and not _session_id:
logging.error("user not found, generating new uid.. contact dev if you don't want to loose data")
new_uid = register_user()
_session_id, online = _create_new_session(new_uid)
new_uid = _register_user()
_session_id, _online = _create_new_session(new_uid)
config.set("uid", new_uid, True)
config.set("old_uid", uid, True)
@ -147,7 +158,7 @@ def get_session(lazy=True):
@fallback((None, False))
def _create_new_session(uid):
body = {"uid": uid, "apiversion": apiversion}
response = requests.post(urls.session, params=body)
response = requests.post(urls.session, json=body, timeout=10)
if response.status_code == 405:
return None, True
@ -158,7 +169,7 @@ def _create_new_session(uid):
@fallback(False)
def has_beta():
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.get(urls.beta, params=body)
response = requests.get(urls.beta, json=body)
result = response.json()
if not result["success"]:
@ -170,5 +181,11 @@ def has_beta():
@fallback(None)
def ping():
body = {"uid": config.get("uid"), "apiversion": apiversion}
response = requests.post(urls.ping, params=body)
response = requests.post(urls.ping, json=body)
logging.debug(f"ping response: {response.json()}")
@fallback("0.5.21")
def get_highest_version():
response = requests.get(constants.current_version_url)
return response.content.decode()

View File

@ -1,17 +1,17 @@
urllib3
winshell
imutils
numpy!=1.19.4
opencv_python==4.5.2.54
numpy
opencv_python
Pillow
pypiwin32
pypiwin32 ; platform_system=="Windows"
winshell ; platform_system=="Windows"
ttkthemes
requests
beautifulsoup4
whatsmyip
pynput
keyboard
playsound==1.2.2
event-scheduler
mouse
d3dshot
pyautogui
mss