|
31 | 31 | import numpy as np |
32 | 32 |
|
33 | 33 | #Websocket Dependencies |
| 34 | +import websockets |
34 | 35 | from websockets.sync.client import connect, ClientConnection |
35 | 36 |
|
| 37 | + |
36 | 38 | #Discord Dependencies |
37 | 39 | from discord.ext import tasks |
38 | 40 | import discord |
|
63 | 65 | EnableCountdownMessages = os.getenv('CountdownMessages') |
64 | 66 | EnableDeathlinkMessages = os.getenv('DeathlinkMessages') |
65 | 67 |
|
| 68 | +EnableDiscordBridge = os.getenv('DiscordBridgeEnabled') |
| 69 | + |
66 | 70 | EnableFlavorDeathlink = os.getenv('FlavorDeathlink') |
67 | 71 | EnableDeathlinkLottery = os.getenv('DeathlinkLottery') |
68 | 72 |
|
|
96 | 100 | ReconnectionTimer = 5 |
97 | 101 | EnvPath = os.getcwd() + "/.env" |
98 | 102 |
|
| 103 | +## These are the main queues for processing data from the Archipelago Tracker to the Discord Bot |
| 104 | +item_queue = Queue() |
| 105 | +death_queue = Queue() |
| 106 | +chat_queue = Queue() |
| 107 | +seppuku_queue = Queue() |
| 108 | +discordseppuku_queue = Queue() |
| 109 | +websocket_queue = Queue() |
| 110 | +lottery_queue = Queue() |
| 111 | +port_queue = Queue() |
| 112 | + |
99 | 113 | if(DebugMode == "true"): |
100 | 114 | WSdbug = True |
101 | 115 | else: |
|
149 | 163 |
|
150 | 164 | ## ARCHIPELAGO TRACKER CLIENT + CORE FUNCTION |
151 | 165 | class TrackerClient: |
152 | | - tags: set[str] = {'Tracker', 'DeathLink'} |
| 166 | + tags: set[str] = {'TextOnly','Tracker', 'DeathLink'} |
153 | 167 | version: dict[str, any] = {"major": 0, "minor": 6, "build": 0, "class": "Version"} |
154 | 168 | items_handling: int = 0b000 # This client does not receive any items |
155 | 169 |
|
@@ -188,61 +202,67 @@ def __init__( |
188 | 202 | self.socket_thread: Thread = None |
189 | 203 |
|
190 | 204 | def run(self): |
191 | | - """Handles incoming messages from the Archipelago MultiServer.""" |
192 | | - DebugMode = os.getenv('DebugMode') |
193 | | - for RawMessage in self.ap_connection: |
194 | | - |
195 | | - if(DebugMode == "true"): |
196 | | - print("==RawMessage==") |
197 | | - print(RawMessage) |
198 | | - print("=====") |
199 | | - |
200 | | - for i in range(len(json.loads(RawMessage))): |
201 | | - |
202 | | - args: dict = json.loads(RawMessage)[i] |
203 | | - cmd = args.get('cmd') |
| 205 | + try: |
| 206 | + """Handles incoming messages from the Archipelago MultiServer.""" |
| 207 | + DebugMode = os.getenv('DebugMode') |
| 208 | + for RawMessage in self.ap_connection: |
204 | 209 |
|
205 | 210 | if(DebugMode == "true"): |
206 | | - print("==Args==") |
207 | | - print(args) |
| 211 | + print("==RawMessage==") |
| 212 | + print(RawMessage) |
208 | 213 | print("=====") |
209 | 214 |
|
210 | | - if cmd == self.MessageCommand.ROOM_INFO.value: |
211 | | - self.send_connect() |
212 | | - WriteRoomInfo(args) |
213 | | - self.check_datapackage() |
214 | | - elif cmd == self.MessageCommand.DATA_PACKAGE.value: |
215 | | - WriteDataPackage(args) |
216 | | - elif cmd == self.MessageCommand.CONNECTED.value: |
217 | | - WriteConnectionPackage(args) |
218 | | - print("Connected to server.") |
219 | | - elif cmd == self.MessageCommand.CONNECTIONREFUSED.value: |
220 | | - print("Connection refused by server - check your slot name / port / whatever, and try again.") |
221 | | - print(args) |
222 | | - seppuku_queue.put(args) |
223 | | - elif cmd == self.MessageCommand.PRINT_JSON.value: |
224 | | - if args.get('type') == 'ItemSend' and self.on_item_send: |
225 | | - self.on_item_send(args) |
226 | | - elif args.get('type') == 'Chat': |
227 | | - if EnableChatMessages == "true" and self.on_chat_send: |
228 | | - self.on_chat_send(args) |
229 | | - elif args.get('type') == 'ServerChat': |
230 | | - if EnableServerChatMessages == "true" and self.on_chat_send: |
231 | | - self.on_chat_send(args) |
232 | | - elif args.get('type') == 'Goal': |
233 | | - if EnableGoalMessages == "true" and self.on_chat_send: |
234 | | - self.on_chat_send(args) |
235 | | - elif args.get('type') == 'Release': |
236 | | - if EnableReleaseMessages == "true" and self.on_chat_send: |
237 | | - self.on_chat_send(args) |
238 | | - elif args.get('type') == 'Collect': |
239 | | - if EnableCollectMessages == "true" and self.on_chat_send: |
240 | | - self.on_chat_send(args) |
241 | | - elif args.get('type') == 'Countdown': |
242 | | - if EnableCountdownMessages == "true" and self.on_chat_send: |
243 | | - self.on_chat_send(args) |
244 | | - elif 'DeathLink' in args.get('tags', []) and self.on_death_link: |
245 | | - self.on_death_link(args) |
| 215 | + for i in range(len(json.loads(RawMessage))): |
| 216 | + |
| 217 | + args: dict = json.loads(RawMessage)[i] |
| 218 | + cmd = args.get('cmd') |
| 219 | + |
| 220 | + if(DebugMode == "true"): |
| 221 | + print("==Args==") |
| 222 | + print(args) |
| 223 | + print("=====") |
| 224 | + |
| 225 | + if cmd == self.MessageCommand.ROOM_INFO.value: |
| 226 | + self.send_connect() |
| 227 | + WriteRoomInfo(args) |
| 228 | + self.check_datapackage() |
| 229 | + elif cmd == self.MessageCommand.DATA_PACKAGE.value: |
| 230 | + WriteDataPackage(args) |
| 231 | + elif cmd == self.MessageCommand.CONNECTED.value: |
| 232 | + WriteConnectionPackage(args) |
| 233 | + print("Connected to server.") |
| 234 | + elif cmd == self.MessageCommand.CONNECTIONREFUSED.value: |
| 235 | + print("Connection refused by server - check your slot name / port / whatever, and try again.") |
| 236 | + print(args) |
| 237 | + seppuku_queue.put(args) |
| 238 | + elif cmd == self.MessageCommand.PRINT_JSON.value: |
| 239 | + if args.get('type') == 'ItemSend' and self.on_item_send: |
| 240 | + self.on_item_send(args) |
| 241 | + elif args.get('type') == 'Chat': |
| 242 | + if EnableChatMessages == "true" and self.on_chat_send: |
| 243 | + self.on_chat_send(args) |
| 244 | + elif args.get('type') == 'ServerChat': |
| 245 | + if EnableServerChatMessages == "true" and self.on_chat_send: |
| 246 | + self.on_chat_send(args) |
| 247 | + elif args.get('type') == 'Goal': |
| 248 | + if EnableGoalMessages == "true" and self.on_chat_send: |
| 249 | + self.on_chat_send(args) |
| 250 | + elif args.get('type') == 'Release': |
| 251 | + if EnableReleaseMessages == "true" and self.on_chat_send: |
| 252 | + self.on_chat_send(args) |
| 253 | + elif args.get('type') == 'Collect': |
| 254 | + if EnableCollectMessages == "true" and self.on_chat_send: |
| 255 | + self.on_chat_send(args) |
| 256 | + elif args.get('type') == 'Countdown': |
| 257 | + if EnableCountdownMessages == "true" and self.on_chat_send: |
| 258 | + self.on_chat_send(args) |
| 259 | + elif 'DeathLink' in args.get('tags', []) and self.on_death_link: |
| 260 | + self.on_death_link(args) |
| 261 | + else: |
| 262 | + print("Unknown command received from Archipelago MultiServer:") |
| 263 | + print(args) |
| 264 | + except websockets.exceptions.ConnectionClosedError as e: |
| 265 | + print(e) |
246 | 266 |
|
247 | 267 | def on_error(self, string, opcode) -> None: |
248 | 268 | if self.verbose_logging: |
@@ -310,6 +330,13 @@ def start(self) -> None: |
310 | 330 | print(e) |
311 | 331 | websocket_queue.put("!! Tracker start error...") |
312 | 332 |
|
| 333 | + def debug_print(self, who: str, what: str) -> None: |
| 334 | + relayed_message = "Discord; " + who + ": " + what |
| 335 | + payload = { |
| 336 | + 'cmd': 'Say', |
| 337 | + 'text': relayed_message} |
| 338 | + self.send_message(payload) |
| 339 | + |
313 | 340 |
|
314 | 341 |
|
315 | 342 | ## DISCORD EVENT HANDLERS + CORE FUNTION |
@@ -397,23 +424,34 @@ async def on_message(message): |
397 | 424 | rtrnmessage = SetEnvVariable(pair[0], pair[1]) |
398 | 425 | await SendMainChannelMessage(rtrnmessage) |
399 | 426 |
|
400 | | - if message.content.startswith('$reloadbot'): |
| 427 | + if message.content.startswith('$reloadtracker'): |
401 | 428 | ReloadBot() |
402 | | - await SendMainChannelMessage("Reloading bot... Please wait.") |
| 429 | + await SendMainChannelMessage("Reloading tracker... Please wait about 5-10 seconds.") |
| 430 | + |
| 431 | + if message.content.startswith('$reloaddiscord'): |
| 432 | + discordseppuku_queue.put("Reloading Discord bot...") |
| 433 | + await SendMainChannelMessage("Reloading Discord bot... Please wait.") |
| 434 | + |
| 435 | + # Broken code for sending messages to AP from discord. :( im working on it |
| 436 | + #if not message.content.startswith('$'): |
| 437 | + # tracker_client.debug_print(str(message.author), message.content) |
| 438 | + # return |
403 | 439 |
|
404 | 440 | @tasks.loop(seconds=1) |
405 | 441 | async def CheckCommandQueue(): |
406 | 442 | if discordseppuku_queue.empty(): |
407 | 443 | return |
408 | 444 | else: |
| 445 | + while not discordseppuku_queue.empty(): |
| 446 | + QueueMessage = discordseppuku_queue.get() |
| 447 | + print("++ Shutting down Discord tasks") |
409 | 448 | CheckArchHost.stop() |
410 | 449 | ProcessItemQueue.stop() |
411 | 450 | ProcessDeathQueue.stop() |
412 | 451 | ProcessChatQueue.stop() |
413 | | - while not discordseppuku_queue.empty(): |
414 | | - item = discordseppuku_queue.get() |
415 | | - |
416 | | - await DiscordClient.close() |
| 452 | + |
| 453 | + print("++ Closing Discord Client") |
| 454 | + exit() |
417 | 455 |
|
418 | 456 | @tasks.loop(seconds=900) |
419 | 457 | async def CheckArchHost(): |
@@ -546,7 +584,8 @@ async def ProcessChatQueue(): |
546 | 584 | return |
547 | 585 | else: |
548 | 586 | chatmessage = chat_queue.get() |
549 | | - await SendMainChannelMessage(chatmessage['data'][0]['text']) |
| 587 | + if not (chatmessage['data'][0]['text']).startswith(ArchipelagoBotSlot): |
| 588 | + await SendMainChannelMessage(chatmessage['data'][0]['text']) |
550 | 589 |
|
551 | 590 | @tree.command(name="register", |
552 | 591 | description="Registers you for AP slot", |
@@ -1344,18 +1383,9 @@ async def CancelProcess(): |
1344 | 1383 | return 69420 |
1345 | 1384 |
|
1346 | 1385 | def Discord(): |
| 1386 | + print("++ Starting Discord Client") |
1347 | 1387 | DiscordClient.run(DiscordToken) |
1348 | 1388 |
|
1349 | | -## Three main queues for processing data from the Archipelago Tracker to the bot |
1350 | | -item_queue = Queue() |
1351 | | -death_queue = Queue() |
1352 | | -chat_queue = Queue() |
1353 | | -seppuku_queue = Queue() |
1354 | | -discordseppuku_queue = Queue() |
1355 | | -websocket_queue = Queue() |
1356 | | -lottery_queue = Queue() |
1357 | | -port_queue = Queue() |
1358 | | - |
1359 | 1389 | ## Threadded async functions |
1360 | 1390 | if(DiscordJoinOnly == "false"): |
1361 | 1391 | # Start the tracker client |
@@ -1469,6 +1499,16 @@ def main(): |
1469 | 1499 | DiscordThread = Process(target=Discord) |
1470 | 1500 | DiscordThread.start() |
1471 | 1501 | DiscordCycleCount = 0 |
| 1502 | + |
| 1503 | + if not DiscordThread.is_alive(): |
| 1504 | + print("++ Discord thread is not running, restarting it") |
| 1505 | + print("++ Closing the discord thread") |
| 1506 | + DiscordThread.close() |
| 1507 | + print("++ Sleeping for 3 seconds to allow the discord thread to close") |
| 1508 | + time.sleep(3) |
| 1509 | + print("++ Starting the discord thread again") |
| 1510 | + DiscordThread = Process(target=Discord) |
| 1511 | + DiscordThread.start() |
1472 | 1512 |
|
1473 | 1513 | try: |
1474 | 1514 | time.sleep(1) |
|
0 commit comments