Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit 2078e50

Browse files
committed
restructure
1 parent 07beac2 commit 2078e50

File tree

8 files changed

+957
-968
lines changed

8 files changed

+957
-968
lines changed

autofish/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
from runner import run
1+
from .gui import App
2+
3+
4+
def run():
5+
App()

autofish/bot.py

+275
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import os
2+
import time
3+
import numpy as np
4+
5+
import cv2
6+
import keyboard
7+
import mouse
8+
import psutil
9+
from PIL import ImageGrab
10+
from .load_data import load_data
11+
12+
13+
# Fishing bot functions
14+
15+
def motions(tip):
16+
""" Collection of moves for fishing """
17+
match tip:
18+
case "Twitching":
19+
mouse.press(button="left")
20+
time.sleep(0.05)
21+
mouse.press(button="right")
22+
time.sleep(0.25)
23+
mouse.release(button="right")
24+
time.sleep(0.65)
25+
case "Stop&Go":
26+
mouse.press(button="left")
27+
time.sleep(1.425)
28+
mouse.release(button="left")
29+
time.sleep(0.45)
30+
case "Lift&Drop":
31+
mouse.press(button="left")
32+
time.sleep(1.75)
33+
mouse.release(button="left")
34+
time.sleep(0.25)
35+
mouse.press(button="right")
36+
time.sleep(0.25)
37+
mouse.release(button="right")
38+
time.sleep(0.25)
39+
case "Straight":
40+
mouse.press(button="left")
41+
time.sleep(1)
42+
case "Straight & Slow":
43+
mouse.press(button="left")
44+
time.sleep(1)
45+
case "Popping":
46+
mouse.press(button="left")
47+
mouse.press(button="right")
48+
time.sleep(0.5)
49+
mouse.release(button="left")
50+
mouse.release(button="right")
51+
time.sleep(1.4)
52+
case "Walking":
53+
mouse.press(button="left")
54+
mouse.press(button="right")
55+
time.sleep(0.25)
56+
mouse.release(button="left")
57+
mouse.release(button="right")
58+
time.sleep(0.85)
59+
case "Float":
60+
pass
61+
case "Bottom":
62+
pass
63+
64+
def checks(tip):
65+
""" Collection of screen vision checks while fishing """
66+
67+
global bot_data
68+
69+
match tip:
70+
case "float-state":
71+
pass
72+
case "fullkeepnet":
73+
screen_load = ImageGrab.grab().load()
74+
for i in range(162, 200):
75+
if ((bot_data["values"]["fullkeepnet_orange"]["low"][0] <= screen_load[88, i][0] <= bot_data["values"]["fullkeepnet_orange"]["high"][0]) and
76+
(bot_data["values"]["fullkeepnet_orange"]["low"][1] <= screen_load[88, i][1] <= bot_data["values"]["fullkeepnet_orange"]["high"][1]) and
77+
(bot_data["values"]["fullkeepnet_orange"]["low"][2] <= screen_load[88, i][2] <= bot_data["values"]["fullkeepnet_orange"]["high"][2])):
78+
return True
79+
return False
80+
case "hookedfish":
81+
screen_load = ImageGrab.grab(bbox=(1631, 794, 1632, 795)).load()
82+
if ((bot_data["values"]["hookedfish_blue"]["low"][0] <= screen_load[0, 0][0] <= bot_data["values"]["hookedfish_blue"]["high"][0]) and
83+
(bot_data["values"]["hookedfish_blue"]["low"][1] <= screen_load[0, 0][1] <= bot_data["values"]["hookedfish_blue"]["high"][1]) and
84+
(bot_data["values"]["hookedfish_blue"]["low"][2] <= screen_load[0, 0][2] <= bot_data["values"]["hookedfish_blue"]["high"][2])):
85+
return True
86+
else:
87+
return False
88+
case "caught_fish":
89+
img = cv2.cvtColor(np.array(ImageGrab.grab()), cv2.COLOR_RGB2GRAY)
90+
91+
disc = cv2.minMaxLoc(cv2.matchTemplate(img, bot_data["images"]["caught_fish"]["discard"], cv2.TM_SQDIFF))
92+
if disc[0] <= 1000000:
93+
mouse.move(disc[2][0], disc[2][1], absolute=True, duration=0)
94+
mouse.click(button="left")
95+
time.sleep(2)
96+
return True
97+
98+
keep = cv2.minMaxLoc(cv2.matchTemplate(img, bot_data["images"]["caught_fish"]["keep"], cv2.TM_SQDIFF))
99+
if keep[0] <= 1000000:
100+
mouse.move(keep[2][0], keep[2][1], absolute=True, duration=0)
101+
mouse.click(button="left")
102+
time.sleep(2)
103+
return True
104+
105+
rel = cv2.minMaxLoc(cv2.matchTemplate(img, bot_data["images"]["caught_fish"]["release"], cv2.TM_SQDIFF))
106+
if rel[0] <= 1000000:
107+
mouse.move(rel[2][0], rel[2][1], absolute=True, duration=0)
108+
mouse.click(button="left")
109+
time.sleep(2)
110+
return True
111+
112+
return False
113+
case "pop_ups":
114+
ret_changes = False
115+
changes = True
116+
while changes:
117+
changes = False
118+
for image in bot_data["images"]["pop_ups"]["list"]:
119+
inf = cv2.minMaxLoc(cv2.matchTemplate(cv2.cvtColor(np.array(ImageGrab.grab()), cv2.COLOR_RGB2GRAY), image, cv2.TM_SQDIFF))
120+
if inf[0] <= 1000000:
121+
mouse.move(inf[2][0], inf[2][1], absolute=True, duration=0)
122+
mouse.click(button="left")
123+
changes = True
124+
ret_changes = True
125+
time.sleep(2)
126+
break
127+
if changes:
128+
continue
129+
# buy
130+
inf = cv2.minMaxLoc(cv2.matchTemplate(cv2.cvtColor(np.array(ImageGrab.grab()), cv2.COLOR_RGB2GRAY), bot_data["images"]["pop_ups"]["buy"][0], cv2.TM_SQDIFF))
131+
if inf[0] <= 1_000_000:
132+
inf2 = cv2.minMaxLoc(cv2.matchTemplate(cv2.cvtColor(np.array(ImageGrab.grab()), cv2.COLOR_RGB2GRAY), bot_data["images"]["pop_ups"]["buy"][1], cv2.TM_SQDIFF))
133+
shape = np.shape(bot_data["images"]["pop_ups"]["buy"][1])
134+
mouse.move(inf2[2][0] + shape[1] // 2, inf2[2][1] + shape[0] // 2, absolute=True, duration=0)
135+
mouse.click(button="left")
136+
changes = True
137+
ret_changes = True
138+
time.sleep(2)
139+
return ret_changes
140+
case "after_reel_in":
141+
while True:
142+
if checks("caught_fish"):
143+
time.sleep(2)
144+
elif checks("pop_ups"):
145+
time.sleep(2)
146+
else:
147+
break
148+
case "lineLen":
149+
cv_img = cv2.cvtColor(np.array(ImageGrab.grab(bbox=(1423, 916, 1666, 1020))), cv2.COLOR_RGB2GRAY)
150+
poz_u_znam = {}
151+
digits_variations = len(bot_data["images"]["digits"]) // 10
152+
for i in range(len(bot_data["images"]["digits"])):
153+
result = cv2.matchTemplate(cv_img, bot_data["images"]["digits"][i], cv2.TM_SQDIFF)
154+
pozicije = np.where(result <= 11500000)
155+
pozicije_x = pozicije[1]
156+
pozicije_y = pozicije[0]
157+
tem = {}
158+
for j in zip(pozicije_y, pozicije_x):
159+
if j[1] in tem.keys():
160+
tem[j[1]].append(result[j])
161+
else:
162+
tem[j[1]] = [result[j]]
163+
for j in tem.keys():
164+
if j in poz_u_znam.keys():
165+
poz_u_znam[j].append((i // digits_variations, min(tem[j])))
166+
else:
167+
poz_u_znam[j] = [(i // digits_variations, min(tem[j]))]
168+
koordinate = list(poz_u_znam.keys())
169+
norm_poz_u_znam = {}
170+
while len(koordinate) != 0:
171+
koord = koordinate[0]
172+
vrijednosti_koord = poz_u_znam[koord]
173+
koordinate.remove(koord)
174+
for i in range(1, 11):
175+
if koord - i in koordinate:
176+
vrijednosti_koord.extend(poz_u_znam[koord - i])
177+
koordinate.remove(koord - i)
178+
if koord + i in koordinate:
179+
vrijednosti_koord.extend(poz_u_znam[koord + i])
180+
koordinate.remove(koord + i)
181+
norm_poz_u_znam[koord] = vrijednosti_koord
182+
redoslijed = list(norm_poz_u_znam.keys())
183+
redoslijed.sort()
184+
broj = ""
185+
for i in redoslijed:
186+
mogucnosti = norm_poz_u_znam[i]
187+
vrijednost = mogucnosti[0][1]
188+
znamenka = mogucnosti[0][0]
189+
for j in mogucnosti:
190+
if j[1] < vrijednost:
191+
vrijednost = j[1]
192+
znamenka = j[0]
193+
broj += str(znamenka)
194+
if broj == "":
195+
return broj
196+
else:
197+
return int(broj)
198+
199+
def warp(auto_time_warp, night):
200+
""" Warps fishing time """
201+
202+
# TODO: warp function
203+
global bot_data
204+
keyboard.press_and_release("t")
205+
time.sleep(3)
206+
if auto_time_warp:
207+
while True:
208+
time.sleep(3)
209+
inf = cv2.minMaxLoc(cv2.matchTemplate(cv2.cvtColor(np.array(ImageGrab.grab()), cv2.COLOR_RGB2GRAY), bot_data["images"]["time_warp"]["next_morning_gray"], cv2.TM_SQDIFF))
210+
if inf[0] <= 1000000:
211+
mouse.move(inf[2][0], inf[2][1], absolute=True, duration=0)
212+
mouse.click(button="left")
213+
time.sleep(5)
214+
break
215+
else:
216+
psutil.Process(os.getpid()).kill()
217+
218+
def action(retrieve, cast_len, num_of_rods, night_toggle, auto_time_warp_toggle, status_mails_toggle, email):
219+
""" Main bot logic (process) """
220+
221+
load_data()
222+
fish_hooked = checks("hookedfish")
223+
while psutil.Process(os.getpid()).parent() is not None:
224+
line_length = checks("lineLen")
225+
226+
# TODO: release fish with fine
227+
228+
if line_length == "": # can't detect line length on screen, wait
229+
time.sleep(2)
230+
checks("after_reel_in")
231+
elif line_length == 0: # line length is zero, check for caught fish, received achievements, ..., cast again
232+
mouse.release(button="left")
233+
mouse.release(button="right")
234+
time.sleep(3)
235+
236+
checks("after_reel_in")
237+
fish_hooked = False
238+
239+
# TODO: check for rod damage, change rod
240+
241+
if checks("fullkeepnet"): # TODO: warp by time of day, not only full keepnet
242+
warp(auto_time_warp_toggle, night_toggle)
243+
if status_mails_toggle:
244+
email.send_gmail(to=email.my_e_mail, subject="Autofish - full keepnet", message="The Autofish bot has filled a keepnet/stringer!", screenshot=True)
245+
else:
246+
# cast
247+
mouse.press(button="left")
248+
time.sleep(0.8575 + ((1.1 * cast_len) / 100))
249+
mouse.release(button="left")
250+
time.sleep(8 + ((4 * cast_len) / 100))
251+
elif line_length <= 5:
252+
# reel in slower if 5 meters or less
253+
mouse.press(button="left")
254+
time.sleep(1)
255+
else:
256+
if not fish_hooked: # if fish was not hooked in current cast, check if it is hooked now
257+
fish_hooked = checks("hookedfish")
258+
259+
if fish_hooked: # if fish was hooked in the current cast, reel in using twitching (it is most effective for reeling in fish)
260+
motions("Twitching")
261+
else:
262+
# TODO: implement float and bottom fishing
263+
match retrieve:
264+
case "Float":
265+
match checks("float-state"):
266+
case "wait":
267+
time.sleep(0.75)
268+
case "bite":
269+
motions("Twitching")
270+
case "empty":
271+
motions("Twitching")
272+
case "Bottom":
273+
pass
274+
case _:
275+
motions(retrieve)

autofish/gmail.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import os
2+
import smtplib
3+
from email import encoders
4+
from email.mime.base import MIMEBase
5+
from email.mime.multipart import MIMEMultipart
6+
from email.mime.text import MIMEText
7+
from io import BytesIO
8+
from random import randint
9+
10+
from PIL import ImageGrab
11+
12+
13+
class Gmail:
14+
def __init__(self, e_mail, key, server="smtp.gmail.com", port=587):
15+
self.my_e_mail = e_mail
16+
self.my_login_key = key
17+
self.my_server = server
18+
self.my_server_port = port
19+
20+
def verify_credentials(self):
21+
with smtplib.SMTP(host=self.my_server, port=self.my_server_port) as smtp:
22+
try:
23+
smtp.ehlo()
24+
smtp.starttls()
25+
smtp.ehlo()
26+
smtp.login(user=self.my_e_mail, password=self.my_login_key)
27+
smtp.quit()
28+
return True
29+
except (smtplib.SMTPException,
30+
smtplib.SMTPServerDisconnected,
31+
smtplib.SMTPResponseException,
32+
smtplib.SMTPSenderRefused,
33+
smtplib.SMTPRecipientsRefused,
34+
smtplib.SMTPDataError,
35+
smtplib.SMTPConnectError,
36+
smtplib.SMTPHeloError,
37+
smtplib.SMTPNotSupportedError,
38+
smtplib.SMTPAuthenticationError):
39+
return False
40+
41+
def send_gmail(self, to: str, subject: str = "", body: str = "", file_paths: list = None, byte_streams: list = None, screenshot: bool = False):
42+
# generating email
43+
e_mail = MIMEMultipart()
44+
e_mail["From"] = self.my_e_mail
45+
e_mail["To"] = to
46+
e_mail["Subject"] = subject
47+
e_mail.attach(MIMEText(body))
48+
49+
file_names = []
50+
if file_paths is not None:
51+
for i in file_paths:
52+
if os.path.exists(i):
53+
basen = os.path.basename(i)
54+
while basen in file_names:
55+
basen = f'{basen.split(".")[0]}{randint(10 ** 10, 10 ** 15)}.{basen.split(".")[1]}'
56+
file_names.append(basen)
57+
file = open(i, "rb")
58+
attac = MIMEBase("application", "octet-stream")
59+
attac.set_payload(file.read())
60+
encoders.encode_base64(attac)
61+
attac.add_header("Content-Disposition", f'attachement; filename="{basen}"')
62+
e_mail.attach(attac)
63+
del file, attac, basen
64+
if byte_streams is not None:
65+
for i in byte_streams:
66+
basen = i[1]
67+
while basen in file_names:
68+
basen = f'{basen.split(".")[0]}{randint(10 ** 10, 10 ** 15)}.{basen.split(".")[1]}'
69+
file_names.append(basen)
70+
attac = MIMEBase("application", "octet-stream")
71+
attac.set_payload(i[0])
72+
encoders.encode_base64(attac)
73+
attac.add_header("Content-Disposition", f'attachement; filename="{basen}"')
74+
e_mail.attach(attac)
75+
del attac, basen
76+
if screenshot:
77+
basen = "screenshot.png"
78+
while basen in file_names:
79+
basen = f"screenshot{randint(10 ** 10, 10 ** 15)}.png"
80+
scrnsht = BytesIO()
81+
ImageGrab.grab().save(scrnsht, format="png")
82+
scrn = MIMEBase("application", "octet-stream")
83+
scrn.set_payload(scrnsht.getvalue())
84+
encoders.encode_base64(scrn)
85+
scrn.add_header("Content-Disposition", f'attachment; filename="{basen}"')
86+
e_mail.attach(scrn)
87+
del scrn, scrnsht, basen
88+
del file_names
89+
90+
# sending email
91+
with smtplib.SMTP(host=self.my_server, port=self.my_server_port) as smtp:
92+
try:
93+
smtp.ehlo()
94+
smtp.starttls()
95+
smtp.ehlo()
96+
smtp.login(user=self.my_e_mail, password=self.my_login_key)
97+
smtp.send_message(msg=e_mail, from_addr=self.my_e_mail, to_addrs=to)
98+
smtp.quit()
99+
return True
100+
except (smtplib.SMTPException,
101+
smtplib.SMTPServerDisconnected,
102+
smtplib.SMTPResponseException,
103+
smtplib.SMTPSenderRefused,
104+
smtplib.SMTPRecipientsRefused,
105+
smtplib.SMTPDataError,
106+
smtplib.SMTPConnectError,
107+
smtplib.SMTPHeloError,
108+
smtplib.SMTPNotSupportedError,
109+
smtplib.SMTPAuthenticationError):
110+
return False

0 commit comments

Comments
 (0)