-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathapp.py
190 lines (157 loc) · 5.07 KB
/
app.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
import re
from distutils import text_file
from queue import Empty, Queue
from sys import argv
from threading import Event, Thread
from time import sleep
from pexpect import EOF, TIMEOUT
from pytchat import create
import game_api as game
from printer_api import tsp_print
VIDEO_ID = argv[1] if len(argv) == 2 else None
FONT = "Meslo LG L Bold Nerd Font Complete Mono.ttf"
TEXT_WIDTH = 42
FORBIDDEN_CMDS = [
"",
"save",
"restore",
"restart",
"reboot",
"quit",
"verbose",
"brief",
"superbrief",
]
chat_queue = Queue()
terminate_event = Event()
def chat_crawler():
"""
Continuously retrieve chat messages from YT stream and plug them into the queue
"""
chat = create(video_id=VIDEO_ID)
while chat.is_alive() and not terminate_event.is_set():
for c in chat.get().sync_items():
chat_queue.put((c.author.name, c.message))
terminate_event.set()
def local_input():
"""
Read local input as an alternative to YT chat
"""
while not terminate_event.is_set():
chat_queue.put(("[LOCAL PLAYER]", input().strip()))
terminate_event.set()
def game_loop():
"""
Primary Gameplay Controller
"""
while not terminate_event.is_set():
try:
# start & restore the game
txt = game.start(width=TEXT_WIDTH)
if game.restore():
txt = game.step("look")
print2paper(txt, "look", "[AUTO RECOVERY]")
else:
print2paper(txt)
# interactive game cycle
while not terminate_event.is_set():
try:
# retrieve latest commands from queue
spool_messages()
author, message = chat_queue.get(timeout=10)
if message.strip() not in FORBIDDEN_CMDS:
# execute game step
txt = game.step(message)
if (not f"I don't know the word" in txt) and (
not f"There was no verb in that sentence!" in txt
):
print2paper(txt, message, author)
# autosave
game.save()
# pace the game
sleep(3)
else:
print()
except Empty:
print("...waiting for new messages...")
except (TIMEOUT, EOF) as err:
pass # restart game if it crashes
def spool_messages():
"""
Skip queue ahead to the latest message
"""
n = chat_queue.qsize()
if n > 1:
print(f"...skipping {n-1} messages...")
for _ in range(n - 1):
chat_queue.get_nowait()
def print2paper(txt, cmd="", author=""):
"""
Parse game output and send formatted text to printer
"""
# print author and command
if cmd:
line = author[:13] + ".." if len(author) > 15 else author
line += " > " if author else "> "
line += cmd
try:
print(line)
tsp_print(
line,
text_width=TEXT_WIDTH,
cut=False,
font_path=FONT,
)
except Exception:
author = author or "someone"
tsp_print(
f"[ERROR] Whoopsie, {author} broke something! Let's try that again, shall we?",
text_width=TEXT_WIDTH,
cut=False,
font_path=FONT,
)
# walk through all lines of the game-text
location = None
for line in txt.split("\n"):
# print game-text header with inversed colors
if re.match(".+[S|Score]: -?\d+\s+[M|Moves]: -?\d+", line):
location = re.findall(".*(?=[S|Score]:)", line)[0].strip()
print(line)
tsp_print(
line,
fg="#fff",
bg="#000",
text_width=TEXT_WIDTH,
cut=False,
font_path=FONT,
)
# skip command & location echo
elif line in [cmd, location]:
continue
# print game-text line
else:
print(line)
tsp_print(
line,
text_width=TEXT_WIDTH,
cut=False,
font_path=FONT,
)
# add empty lines for spacing and close connection
tsp_print("\n" * 2, text_width=TEXT_WIDTH, cut=True)
def main():
"""
Start game-loop and run chat crawler
"""
try:
game_loop_thread = Thread(target=game_loop)
game_loop_thread.start()
local_input_thread = Thread(target=local_input)
local_input_thread.start()
if VIDEO_ID:
chat_crawler()
game_loop_thread.join()
finally:
terminate_event.set()
tsp_print("[stream lost]\n\n")
main()