discord-rich-presence-plex/core/discord.py

118 lines
4.1 KiB
Python
Raw Normal View History

2024-02-06 20:10:41 +00:00
from config.constants import discordClientID, isUnix, processID, ipcPipeBase
2022-05-14 09:43:02 +00:00
from typing import Any, Optional
from utils.logging import logger
2022-05-10 20:23:12 +00:00
import asyncio
import json
2022-05-14 09:43:02 +00:00
import models.discord
2022-05-10 20:23:12 +00:00
import os
import struct
import time
class DiscordIpcService:
2022-05-10 20:23:12 +00:00
2024-02-06 20:10:41 +00:00
def __init__(self, pipeNumber: Optional[int]):
pipeNumber = pipeNumber or -1
pipeNumbers = range(10) if pipeNumber == -1 else [pipeNumber]
self.pipes: list[str] = []
for pipeNumber in pipeNumbers:
pipeFilename = f"discord-ipc-{pipeNumber}"
self.pipes.append(os.path.join(ipcPipeBase, pipeFilename))
self.pipes.append(os.path.join(ipcPipeBase, "app", "com.discordapp.Discord", pipeFilename))
self.pipes.append(os.path.join(ipcPipeBase, ".flatpak", "com.discordapp.Discord", "xdg-run", pipeFilename))
self.loop: Optional[asyncio.AbstractEventLoop] = None
self.pipeReader: Optional[asyncio.StreamReader] = None
self.pipeWriter: Optional[asyncio.StreamWriter] = None
2022-05-10 20:23:12 +00:00
self.connected = False
2022-05-14 09:43:02 +00:00
async def handshake(self) -> None:
if not self.loop:
return
2024-02-06 20:10:41 +00:00
for pipe in self.pipes:
try:
if isUnix:
2024-02-10 07:11:18 +00:00
self.pipeReader, self.pipeWriter = await asyncio.open_unix_connection(pipe) # pyright: ignore[reportAttributeAccessIssue,reportUnknownMemberType]
else:
self.pipeReader = asyncio.StreamReader()
2024-02-10 07:11:18 +00:00
self.pipeWriter = (await self.loop.create_pipe_connection(lambda: asyncio.StreamReaderProtocol(self.pipeReader), pipe))[0] # pyright: ignore[reportAttributeAccessIssue,reportUnknownMemberType,reportArgumentType]
self.write(0, { "v": 1, "client_id": discordClientID })
if await self.read():
self.connected = True
2024-02-06 20:10:41 +00:00
logger.info(f"Connected to Discord IPC pipe {pipe}")
break
except FileNotFoundError:
pass
except:
2024-02-06 20:10:41 +00:00
logger.exception(f"An unexpected error occured while connecting to Discord IPC pipe {pipe}")
if not self.connected:
2024-02-06 20:10:41 +00:00
logger.error(f"Discord IPC pipe not found (attempted pipes: {', '.join(self.pipes)})")
2022-05-10 20:23:12 +00:00
2022-05-14 09:43:02 +00:00
async def read(self) -> Optional[Any]:
if not self.pipeReader:
return
2022-05-10 20:23:12 +00:00
try:
dataBytes = await self.pipeReader.read(1024)
data = json.loads(dataBytes[8:].decode("utf-8"))
logger.debug("[READ] %s", data)
return data
except:
logger.exception("An unexpected error occured during an IPC read operation")
2022-05-10 21:57:06 +00:00
self.connected = False
2022-05-10 20:23:12 +00:00
2022-05-14 09:43:02 +00:00
def write(self, op: int, payload: Any) -> None:
if not self.pipeWriter:
return
2022-05-10 20:23:12 +00:00
try:
logger.debug("[WRITE] %s", payload)
payload = json.dumps(payload)
self.pipeWriter.write(struct.pack("<ii", op, len(payload)) + payload.encode("utf-8"))
except:
logger.exception("An unexpected error occured during an IPC write operation")
2022-05-10 21:57:06 +00:00
self.connected = False
2022-05-10 20:23:12 +00:00
def connect(self) -> None:
if self.connected:
logger.warning("Attempt to connect to Discord IPC pipe while already connected")
return
logger.info("Connecting to Discord IPC pipe")
self.loop = asyncio.new_event_loop()
self.loop.run_until_complete(self.handshake())
2022-05-14 09:43:02 +00:00
def disconnect(self) -> None:
2022-05-10 21:57:06 +00:00
if not self.connected:
logger.warning("Attempt to disconnect from Discord IPC pipe while not connected")
return
if not self.loop or not self.pipeWriter or not self.pipeReader:
2022-05-10 21:57:06 +00:00
return
logger.info("Disconnecting from Discord IPC pipe")
2022-05-10 21:57:06 +00:00
try:
self.pipeWriter.close()
except:
logger.exception("An unexpected error occured while closing the IPC pipe writer")
2022-05-10 21:57:06 +00:00
try:
self.loop.run_until_complete(self.pipeReader.read())
except:
logger.exception("An unexpected error occured while closing the IPC pipe reader")
2022-05-10 20:23:12 +00:00
try:
self.loop.close()
except:
logger.exception("An unexpected error occured while closing the asyncio event loop")
2022-05-10 20:23:12 +00:00
self.connected = False
2022-05-14 09:43:02 +00:00
def setActivity(self, activity: models.discord.Activity) -> None:
if not self.connected:
logger.warning("Attempt to set activity while not connected to Discord IPC pipe")
return
if not self.loop:
return
2022-05-10 20:23:12 +00:00
logger.info("Activity update: %s", activity)
payload = {
"cmd": "SET_ACTIVITY",
"args": {
"pid": processID,
"activity": activity,
},
2022-05-10 21:57:06 +00:00
"nonce": "{0:.2f}".format(time.time()),
2022-05-10 20:23:12 +00:00
}
self.write(1, payload)
self.loop.run_until_complete(self.read())