/
stak.py
222 lines (177 loc) · 6.89 KB
/
stak.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# Simple TradeWars2002 Automatic Keypresser (STAK)
#
# Allows user to define and playback keypresses.
# This is mostly to faciliate TradeWars 2002 port trading
# and other simple repetitive tasks.
#
# Uses: pyautogui
#
# Other Notes: I considered using some platform specific libraries
# like pywin32 and/or pywinauto to better handle Windows manipulation
# like using the actual Windows handle to give/check focus when
# running macros, but decided to stick with using only the
# cross-compatible pyautogui library for now, so it should work on
# any platform that can install Python 3 and pyautogui.
import sys
import os
import pyautogui
from time import sleep
VERSION="1.1"
macros = {}
macro_list = []
TYPESPEED=0.05
DELAY_CHAR="`"
DELAY=1
FAILSAFE_DISTANCE=10
FAILSAFE=True
def do_main_menu():
global DELAY_CHAR, DELAY, TYPESPEED, FAILSAFE, FAILSAFE_DISTANCE
selection = None
while True:
print()
print("==== MAIN MENU====")
print("l) List macros")
print("c) Create macro")
print("r) Run macro")
print("m) Run macro multiple times")
print("d) Delete macro")
print()
print("dc) Set macro delay char (Currently: {0})".format(DELAY_CHAR))
print("dv) Set macro delay char value (Currently: {0} seconds)".format(DELAY))
print("td) Set inter-character typing delay (Currently {0} seconds)".format(TYPESPEED))
print()
print("f) Toggle FAILSAFE (aborts macro on mouse movement): Currently {0}".format(FAILSAFE))
print()
print("q) Quit")
print()
selection = input("Selection: ").lower()
if selection == "c":
create_macro()
elif selection == "q" or selection == "Q":
sys.exit(0)
elif selection == "dc":
DELAY_CHAR=str(input("Enter character to represent delay in macro string: "))
elif selection == "dv":
DELAY=float(input("Enter value of delay character, in seconds (float value allowed): "))
elif selection == "td":
TYPESPEED=float(input("Enter delay between keystrokes, in seconds (float value allowed): "))
elif selection == "f":
FAILSAFE = not FAILSAFE
elif len(macro_list) == 0:
print("No macros defined.")
elif selection == "l":
list_macros()
elif selection == "c":
create_macro()
elif selection == "r":
run_macro()
elif selection == "m":
times = input("Times to run macro: ")
run_macro(repeat=int(times))
elif selection == "d":
delete_macro()
else:
print("Unknown selection '{0}'".format(selection))
def list_macros():
print()
print("---- Macros ----")
print()
i = 1
for macro in macro_list:
print("{0}. {1} = {2}".format(str(i),macro,macros[macro]['seq']))
i = i + 1
print()
def create_macro():
print()
print("---- Creating New Macro ----")
print()
input("Step 1: While keeping this window active, move the mouse to the target window to run the macro on, and press ENTER.")
mouse_x, mouse_y = pyautogui.position()
print("Clicked at {0},{1}".format(mouse_x, mouse_y))
print()
macro_seq = input("Step 2: Enter the key sequence you want to transmit to the target screen (may use \\n for ENTER): ")
print()
macro_name = input("Enter name of macro: ")
if macro_name in macros:
overwrite = input("{0} already exists. Overwrite? y/n ".format(macro_name))
if overwrite != 'y':
print("Cancelled.")
return
macros[macro_name] = {'x': mouse_x, 'y': mouse_y, 'seq': macro_seq}
if macro_name not in macro_list:
macro_list.append(macro_name)
print("Macro {0} defined.".format(macro_name))
def run_macro(repeat=1):
print()
print("---- Run Macro ({0} times) ----".format(repeat))
print()
i = 1
mapping={}
for macro in macro_list:
print("{0}. {1}".format(str(i),macro))
mapping[str(i)] = macros[macro]
i = i + 1
print()
print("q. Quit")
print()
selection = input("Select macro to run: ")
if selection == "q":
return
if not selection in mapping:
print("Invalid selection.")
return
selected_macro = mapping[selection]
print()
try:
pyautogui.moveTo(selected_macro['x'],selected_macro['y'])
pyautogui.click()
i=0
while i < repeat:
#pyautogui.typewrite(selected_macro['seq'].encode('utf-8').decode('unicode_escape'), interval=TYPESPEED)
seq_list = selected_macro['seq'].split(DELAY_CHAR)
for index in range(len(seq_list)):
# FAILSAFE: Make sure mouse hasn't moved a significant amount (and potentially lost focus to target window),
# when sending macro sequences.
mouse_x, mouse_y = pyautogui.position()
diffx = abs(mouse_x - selected_macro['x'])
diffy = abs(mouse_y - selected_macro['y'])
if FAILSAFE and ((diffx > FAILSAFE_DISTANCE) or (diffy > FAILSAFE_DISTANCE)):
print("*** FAILSAFE TRIGGERED (mouse moved off x,y): Aborting macro")
return
pyautogui.typewrite(seq_list[index].encode('utf-8').decode('unicode_escape'),interval=TYPESPEED)
#for key in seq_list[index].encode('utf-8').decode('unicode_escape'):
# pyautogui.press(key)
# Don't delay after last subsequence is sent
if index < len(seq_list) - 1:
sleep(DELAY)
i = i + 1
print("Times run: {0} (moving mouse to upper left screen corner aborts)".format(i))
except pyautogui.FailSafeException:
print("*** FAILSAFE TRIGGERED (screen corner hit): Aborting macro")
return
def delete_macro():
print()
print("---- DELETE Macro ----")
print()
i = 1
mapping={}
for macro in macro_list:
print("{0}. {1}".format(str(i),macro))
mapping[str(i)] = macro
i = i + 1
print()
print("q. Quit")
print()
selection = input("Select macro to DELETE: ")
if selection == "q":
return
if not selection in mapping:
print("Invalid selection.")
return
del macros[mapping[selection]]
macro_list.remove(mapping[selection])
print("Deleted macro {0}".format(mapping[selection]))
if __name__ == "__main__":
print("***** Simple Tradewars Auto Keyer {0} *****".format(VERSION))
print()
do_main_menu()