diff --git a/patches/apply_pymumble_ssl.py b/patches/apply_pymumble_ssl.py index 2f6b6c3..89b8b5a 100644 --- a/patches/apply_pymumble_ssl.py +++ b/patches/apply_pymumble_ssl.py @@ -2,6 +2,7 @@ 1. pymumble: ssl.wrap_socket was removed in 3.13 2. opuslib: ctypes.util.find_library fails on musl-based distros +3. pymumble: protocol version 1.2.4 is rejected by modern servers """ import pathlib @@ -65,5 +66,29 @@ new_init = """\ self.control_socket = None""" assert old_init in src, "pymumble init_connection socket patch target not found" -p.write_text(src.replace(old_init, new_init)) +src = src.replace(old_init, new_init) print("pymumble reconnect socket patch applied") + +p.write_text(src) + +# -- pymumble: report modern protocol version -- +# PyMumble 1.6.1 reports protocol version 1.2.4, which modern Murmur +# (1.5.x) treats as an invalid/legacy client. Update to 1.5.0 so the +# server enables full feature set and stops sending warnings. +p = pathlib.Path(f"{site}/pymumble_py3/constants.py") +src = p.read_text() + +old_ver = "PYMUMBLE_PROTOCOL_VERSION = (1, 2, 4)" +new_ver = "PYMUMBLE_PROTOCOL_VERSION = (1, 5, 0)" + +assert old_ver in src, "pymumble version patch target not found" +src = src.replace(old_ver, new_ver) + +old_os = 'PYMUMBLE_OS_STRING = "PyMumble %s" % PYMUMBLE_VERSION' +new_os = 'PYMUMBLE_OS_STRING = platform.system()' + +assert old_os in src, "pymumble OS string patch target not found" +src = src.replace(old_os, new_os) + +p.write_text(src) +print("pymumble version patch applied (1.5.0, native OS string)") diff --git a/src/derp/mumble.py b/src/derp/mumble.py index 3a76e2f..f5d5a3b 100644 --- a/src/derp/mumble.py +++ b/src/derp/mumble.py @@ -218,11 +218,27 @@ class MumbleBot: self._on_sound_received, ) self._mumble.set_receive_sound(self._receive_sound) + # Raise retry interval so 2+ bots on the same IP don't trip + # the server's autoban (default: 10 attempts / 120s). + import pymumble_py3.mumble as _pm + if getattr(_pm, "PYMUMBLE_CONNECTION_RETRY_INTERVAL", 0) < 15: + _pm.PYMUMBLE_CONNECTION_RETRY_INTERVAL = 15 self._mumble.start() self._mumble.is_ready() def _on_connected(self) -> None: """Callback from pymumble thread: connection established.""" + # Enable TCP keepalive on the control socket to prevent NAT + # gateways from dropping the mapping during idle periods. + try: + import socket as _sock + raw = self._mumble.control_socket + raw.setsockopt(_sock.SOL_SOCKET, _sock.SO_KEEPALIVE, 1) + raw.setsockopt(_sock.IPPROTO_TCP, _sock.TCP_KEEPIDLE, 10) + raw.setsockopt(_sock.IPPROTO_TCP, _sock.TCP_KEEPINTVL, 5) + raw.setsockopt(_sock.IPPROTO_TCP, _sock.TCP_KEEPCNT, 3) + except Exception: + pass self._connect_count += 1 kind = "reconnected" if self._connect_count > 1 else "connected" session = getattr(self._mumble.users, "myself_session", "?")