diff options
| -rw-r--r-- | config.py | 18 | ||||
| -rw-r--r-- | lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py | 2 | ||||
| -rw-r--r-- | lib/discordSync.py | 79 | ||||
| -rw-r--r-- | proxy.py | 24 | ||||
| -rw-r--r-- | requirements.txt | 10 | ||||
| -rw-r--r-- | testDiscord.py | 7 |
6 files changed, 124 insertions, 16 deletions
@@ -52,10 +52,20 @@ SMOKE_PLANE = True SMOKE_LIFE = 5 -#Discord chat integration -#Make sure to enable read message privilege for the bot. +# Discord chat integration +# ```bash +# pip install -r requirements.txt +# ``` +# to install dependencies required to run the discord chat integration +# +# On Modern Linux distros you may need to create a virtual environment to install +# the dependencies -DISCORD_TOKEN = "YourDiscordBotTOKEN" +DISCORD_ENABLED = False + +# Make sure to enable read message intent for the bot. + +DISCORD_TOKEN = "YOUR_DISCORD_BOT_TOKEN" # Channel ID for the chat -CHANNEL_ID = 0 # Channel ID, as an integer +CHANNEL_ID = 0 diff --git a/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py b/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py index f524b9f..1c683a5 100644 --- a/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py +++ b/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py @@ -14,7 +14,7 @@ class FSNETCMD_TEXTMESSAGE: #32 self.raw_message = self.buffer[12:].decode("utf-8").strip("\x00") match = re.match(r"^\(([^)]+)\)(.+)", self.raw_message) if match: - self.user. self.message = match.groups() + self.user, self.message = match.groups() @staticmethod def encode(message:str, with_size:bool=False): diff --git a/lib/discordSync.py b/lib/discordSync.py new file mode 100644 index 0000000..e6618a9 --- /dev/null +++ b/lib/discordSync.py @@ -0,0 +1,79 @@ +import aiohttp +import asyncio +from config import * +from lib.PacketManager.packets.FSNETCMD_TEXTMESSAGE import FSNETCMD_TEXTMESSAGE as txtMsgr +from logging import debug, warning + +BOT_TOKEN = DISCORD_TOKEN + +# API Base URL +BASE_URL = 'https://discord.com/api/v10' + +# Headers for making API requests +HEADERS = { + 'Authorization': f'Bot {BOT_TOKEN}', + 'Content-Type': 'application/json' +} + +# Function to send a message to a specific Discord channel +async def discord_send_message(channel_id, content): + url = f'{BASE_URL}/channels/{channel_id}/messages' + payload = { + 'content': content + } + + async with aiohttp.ClientSession() as session: + async with session.post(url, json=payload, headers=HEADERS) as response: + if response.status in [200, 201]: + pass + else: + warning(f'Failed to send message. Status Code: {response.status} | {await response.text()}') + +# Function to fetch messages from a specific Discord channel +async def discord_fetch_messages(channel_id, last_message_id=None): + url = f'{BASE_URL}/channels/{channel_id}/messages' + params = { + 'limit': 1 # Fetch the most recent message + } + if last_message_id: + params['after'] = last_message_id # Fetch only new messages + + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=HEADERS, params=params) as response: + if response.status == 200: + messages = await response.json() + return messages # Return the list of messages + else: + print(f'Failed to fetch messages. Status Code: {response.status} | {await response.text()}') + return [] + +# Callback function when a new message is detected +def on_new_message(message): + author = message['author'] + # Skip messages from bots (including this bot itself) + if author.get('bot'): # Safely get the 'bot' key (returns None if key does not exist) + return + + username = author['username'] + content = message['content'] + debug(f'New Discord message from {username}: {content}') + +# Asynchronous function to monitor a Discord channel for new messages +async def monitor_channel(channel_id, playerList:list): + last_message_id = None + while True: + # Fetch new messages + messages = await discord_fetch_messages(channel_id, last_message_id) + if messages: + # Process only the latest message + message = messages[0] + if messages: + message = messages[0] + if last_message_id is None or message['id'] > last_message_id and not message['author'].get("bot"): # Ensure only newer messages are processed and bots are ignored + last_message_id = message['id'] + encoded_msg = txtMsgr.encode(f"[Discord] {message['author']['username']}: {message['content']}", True) + for player in playerList: + player.streamWriterObject.write(encoded_msg) + await player.streamWriterObject.drain() + on_new_message(message) + await asyncio.sleep(1) # Poll every second (adjust as needed) @@ -12,12 +12,18 @@ from lib.PacketManager.packets import * import logging from logging import critical, warning, info, debug from config import * +if DISCORD_ENABLED: from lib.discordSync import * +import traceback # Configuration SERVER_HOST = SERVER_HOST SERVER_PORT = SERVER_PORT PROXY_PORT = PROXY_PORT +# Hold all Connected Players + +CONNECTED_PLAYERS = [] + # ANSI escape codes for colors COLORS = { "DEBUG": "\033[92m", # Green @@ -60,7 +66,7 @@ async def handle_client(client_reader, client_writer): if peername: ipAddr, clientPort = peername player.set_ip(ipAddr) - + CONNECTED_PLAYERS.append(player) debug("Player object initiated") async def forward(reader, writer, direction, player=player): @@ -108,8 +114,8 @@ async def handle_client(client_reader, client_writer): player.login(FSNETCMD_LOGON(packet)) if player.version != YSF_VERSION and VIA_VERSION: info(f"ViaVersion enabled : Porting {player.username} from {player.version} to {YSF_VERSION}") - message_to_server.append(YSchat.message(f"Porting you to YSFlight {YSF_VERSION}, This is currently Experimental")) - message_to_server.append(YSchat.message(f"Please report any bugs to the server admin or join with the correct version")) + message_to_client.append(YSchat.message(f"Porting you to YSFlight {YSF_VERSION}, This is currently Experimental")) + message_to_client.append(YSchat.message(f"Please report any bugs to the server admin or join with the correct version")) data = YSviaversion.genViaVersion(player.username, YSF_VERSION) writer.write(data) continue @@ -154,6 +160,12 @@ async def handle_client(client_reader, client_writer): if player.aircraft.get_initial_config_value("AFTBURNR") == "TRUE": message_to_client.append(player.aircraft.set_afterburner(True)) debug("Aircraft repaired!") + elif packet_type == "FSNETCMD_TEXTMESSAGE": + msg = FSNETCMD_TEXTMESSAGE(packet) + finalMsg = (f"{player.username} : {msg.message}") + if DISCORD_ENABLED: + # Make it non blocking! + asyncio.create_task(discord_send_message(CHANNEL_ID, finalMsg)) # elif packet_type == "FSNETCMD_GETDAMAGE": # h = FSNETCMD_GETDAMAGE(packet, True) @@ -249,6 +261,7 @@ async def handle_client(client_reader, client_writer): # continue except Exception as e: warning(f"Error parsing flight data: {e}", exc_info=True) + traceback.print_exc() # This will display the full traceback else : debug("S2C" + str(packet_type)) @@ -270,6 +283,9 @@ async def handle_client(client_reader, client_writer): elif packet_type == "FSNETCMD_PREPARESIMULATION": welcomeMsg = YSchat.message(WELCOME_MESSAGE.format(username=player.username)) message_to_server.append(welcomeMsg) + print("Here!") + if DISCORD_ENABLED: + asyncio.create_task(discord_send_message(CHANNEL_ID, f"{player.username} has joined the server!")) # Forward the packet to the other endpoint writer.write(data) @@ -302,6 +318,8 @@ async def handle_client(client_reader, client_writer): async def start_proxy(): server = await asyncio.start_server(handle_client, "0.0.0.0", PROXY_PORT) info(f"Proxy server listening on port {PROXY_PORT}") + if DISCORD_ENABLED: + await asyncio.create_task(monitor_channel(CHANNEL_ID, CONNECTED_PLAYERS)) async with server: await server.serve_forever() diff --git a/requirements.txt b/requirements.txt index 844f49a..859a08f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,9 @@ -discord.py +aiohappyeyeballs==2.4.6 +aiohttp==3.11.12 +aiosignal==1.3.2 +attrs==25.1.0 +frozenlist==1.5.0 +idna==3.10 +multidict==6.1.0 +propcache==0.2.1 +yarl==1.18.3 diff --git a/testDiscord.py b/testDiscord.py deleted file mode 100644 index ca084e8..0000000 --- a/testDiscord.py +++ /dev/null @@ -1,7 +0,0 @@ -from lib.DiscordClient import DiscordClient -import discord - -intents = discord.Intents.default() -intents.message_content = True -client = DiscordClient(intents) -client.start_bot() |
