Support Flatpak and Snap Discord; keep the timer when a game changes process

Look for discord-ipc sockets in the Flatpak and Snap runtime folders too. When a game moves to another
process (e.g. at Steam launch), keep its first start time and don't resend the activity. Bump version to 0.2.
This commit is contained in:
Daniele Fucini
2026-11-11 21:06:59 +01:00
parent 3e83c8c5d2
commit 047139cb72
3 changed files with 33 additions and 14 deletions
+10 -8
View File
@@ -16,17 +16,18 @@ import tomllib
import urllib.request
import uuid
from collections.abc import Callable
from dataclasses import dataclass, field
from dataclasses import dataclass, field, replace
from pathlib import Path
from typing import Any
__version__ = "0.1"
__version__ = "0.2"
NAME = "discord-game-presence"
DETECTABLE_URL = "https://discord.com/api/v9/applications/detectable"
CACHE_MAX_AGE = 7 * 24 * 3600 # seconds before the game list is downloaded again
CACHE_CHECK = 3600 # seconds between cache age checks in the daemon
CACHE_RETRY = 600 # seconds between download attempts while there is no game list at all
SOCKET_DIRS = ("", "app/com.discordapp.Discord", "snap.discord") # under XDG_RUNTIME_DIR: host/firejail, Flatpak, Snap
OP_HANDSHAKE, OP_FRAME, OP_CLOSE, OP_PING, OP_PONG = range(5)
@@ -208,11 +209,11 @@ def scan(index: GameIndex, proc: Path = Path("/proc")) -> list[Game]:
def choose(games: list[Game], current: Game | None) -> Game | None:
"""Keep showing the current game while it runs, else the oldest one."""
"""Keep showing the current game while it runs, timed from its first process, else the oldest one."""
if current is not None:
for game in games:
if game.app.id == current.app.id:
return game
return replace(game, start=min(game.start, current.start))
return games[0] if games else None
@@ -272,8 +273,7 @@ class DiscordIPC:
@classmethod
def connect(cls, client_id: str, runtime_dir: Path, timeout: float = 5.0) -> "DiscordIPC":
error: OSError = FileNotFoundError(f"no discord-ipc socket in {runtime_dir}")
for i in range(10):
path = runtime_dir / f"discord-ipc-{i}"
for path in (runtime_dir / d / f"discord-ipc-{i}" for d in SOCKET_DIRS for i in range(10)):
if not path.exists():
continue
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -363,8 +363,8 @@ class Presence:
if game is None:
self.failing = False
return
if game == self.shown:
return
if self.shown is not None and game.start == self.shown.start:
return # same app and timer: only the process changed
try:
if self.ipc is None:
self.ipc = DiscordIPC.connect(game.app.id, self.runtime_dir)
@@ -406,6 +406,8 @@ def run(config: Config, cache: Path, runtime_dir: Path, stop: threading.Event, p
log.info("%s exited", current.app.name)
if game is not None and (current is None or game.app.id != current.app.id):
log.info("Detected %s (pid %d)", game.app.name, game.pid)
elif game is not None and current is not None and game.pid != current.pid:
log.debug("%s now pid %d", game.app.name, game.pid)
current = game
presence.update(game)
stop.wait(config.interval)