Conversation
There was a problem hiding this comment.
Pull request overview
Updates the GitHub Actions E2E workflow to start the Conduwuit homeserver via an explicit docker run with a generated TOML config, aiming to stabilize the integration test setup.
Changes:
- Removes the GitHub Actions
services:container definition for Conduwuit. - Adds a step that writes
conduwuit.tomland starts Conduwuit viadocker run. - Drops passing
CONDUWUIT_CONTAINER_IDinto the integration test step.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
2 issues found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docker/test_integration.py">
<violation number="1" location="docker/test_integration.py:22">
P1: Fail fast when token discovery fails instead of silently falling back to a hardcoded token.</violation>
<violation number="2" location="docker/test_integration.py:44">
P2: Do not print the full registration token in logs; this leaks a credential used for registration.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="matrix_premid.py">
<violation number="1" location="matrix_premid.py:206">
P2: Adding the new `Watching` prefix without updating `update()` quality parsing regresses metadata prioritization, so lower-quality statuses can overwrite richer ones for the same title.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="Makefile">
<violation number="1" location="Makefile:53">
P1: `test` no longer depends on `deps`, so `make test` can fail on fresh setups because pytest/dev dependencies are not installed.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
tests/test_updater.py:172
test_main_execution_mocked_gathercallsmain(), butmain()now exits early ifplayerctlis not found viashutil.which. This test (and the othermain()test) should patchmatrix_premid.shutil.whichto return a fake path so the assertions exercise the intended code paths and don't become environment-dependent.
@pytest.mark.asyncio
@patch("matrix_premid.sys.exit")
@patch("matrix_premid.acquire_lock")
@patch("matrix_premid.HOMESERVER", "mock")
@patch("matrix_premid.USERNAME", "@user")
@patch("matrix_premid.ACCESS_TOKEN", "tok")
@patch("matrix_premid.DEVICE_ID", "dev")
async def test_main_execution_mocked_gather(_mock_lock, mock_exit):
"""Test main entrypoint setups everything cleanly resolving without errors."""
with patch("matrix_premid.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_client.return_value = mock_instance
mock_event = MagicMock()
mock_event.user_id = "@user"
mock_event.presence = "dnd"
mock_resp = MagicMock()
mock_resp.presence.events = [mock_event]
# Emit native valid presence payload once, then gracefully exit loop
mock_instance.sync.side_effect = [mock_resp, asyncio.CancelledError()]
with patch(
"matrix_premid.MatrixStatusUpdater.update", new_callable=AsyncMock
) as mock_update:
mock_update.side_effect = asyncio.CancelledError()
with patch("matrix_premid.asyncio.create_subprocess_exec") as mock_exec:
mock_proc = AsyncMock()
mock_proc.communicate.side_effect = asyncio.CancelledError()
mock_exec.return_value = mock_proc
await main()
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/test_updater.py">
<violation number="1" location="tests/test_updater.py:156">
P2: This test fixture now uses `online`, which makes it unable to catch regressions where native non-default presence is ignored and forced back to online.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docker/test_integration.py">
<violation number="1" location="docker/test_integration.py:29">
P2: Using `docker logs --since 1s` can miss the one-time registration-token log line and make this integration test flaky.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/matrix_premid/__main__.py">
<violation number="1" location="src/matrix_premid/__main__.py:643">
P2: Time-quality bonus is granted based on raw non-empty fields, so zero/invalid durations can be misclassified as valid timestamps.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 22 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 1. **Install dependencies**: ``pip install .`` (or use installation methods above). | ||
| 2. **Setup environment**: Place your ``.env`` file in the current directory or at ``~/.config/matrix-premid/.env``. | ||
| 3. **Run the script**: ``matrix-premid`` | ||
|
|
There was a problem hiding this comment.
The README now instructs users to place a .env file for configuration, but the new CLI no longer loads dotenv/env-based configuration; it requires ~/.config/matrix-premid/config.json and uses keyring for missing access tokens. Update the “Basic Usage” steps to describe creating/editing config.json (or using the provided template) and storing tokens in keyring (or placing access_token in config for non-interactive setups like CI).
| # If no activity detected, we don't force 'Idle' immediately. | ||
| # We preserve the last activity unless it's explicitly 'Idle' | ||
| # or we are exiting. | ||
| return |
There was a problem hiding this comment.
MatrixStatusUpdater.update() returns immediately when activity is empty (unless is_exit), but the idle debounce/idle_timeout logic only runs when activity is "Idle"/"Idle...". Combined with _get_best_mpris_activity() returning "" when nothing is playing, this means the updater may never transition to Idle when playback stops and idle_timeout becomes ineffective. Consider treating empty activity as an idle signal (possibly debounced via idle_strikes) or have _get_best_mpris_activity() return "Idle" when no active players are detected.
| # If no activity detected, we don't force 'Idle' immediately. | |
| # We preserve the last activity unless it's explicitly 'Idle' | |
| # or we are exiting. | |
| return | |
| # Treat lack of activity as a potential idle state so that | |
| # idle_timeout / idle_strikes logic can take effect. | |
| activity = "Idle" | |
| title = "" |
| # Clean up lock file so next start works cleanly | ||
| try: | ||
| os.unlink(LOCK_FILE) | ||
| except OSError: | ||
| pass |
There was a problem hiding this comment.
The lock file is unlinked during shutdown, but the lock file descriptor returned by acquire_lock() is never closed. Unlinking while still holding the lock releases the name while the process is still alive, allowing a second instance to create a new lock file and acquire a lock before this process exits. Keep a reference to the lock FD and close it before unlinking (or just leave the lock file in place and rely on lockf + truncation).
| # Clean up lock file so next start works cleanly | |
| try: | |
| os.unlink(LOCK_FILE) | |
| except OSError: | |
| pass | |
| # Do not unlink the lock file here. Locking is enforced via the file | |
| # descriptor and POSIX locking (lockf), not by the mere presence of | |
| # the file. Unlinking while still holding the lock can allow another | |
| # process to recreate the file and acquire a separate lock before this | |
| # process fully exits. |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/matrix_premid/__main__.py">
<violation number="1" location="src/matrix_premid/__main__.py:707">
P2: Validate `poll_interval`/`idle_timeout` from hot-reloaded config before assigning; invalid types can crash the monitor loop at runtime.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 22 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Clean up lock file so next start works cleanly | ||
| try: | ||
| os.unlink(LOCK_FILE) | ||
| except OSError: | ||
| pass |
There was a problem hiding this comment.
The lock file is unlinked at shutdown, but the file descriptor returned from acquire_lock() is never closed before os.unlink(LOCK_FILE). On POSIX, unlinking while still holding the lock can allow a second process to create a new lock file and acquire a lock before the first process fully exits, defeating the single-instance guarantee. Fix by closing the lock fd before unlinking, or by leaving the lock file in place and relying on advisory locking + PID contents for cleanup.
| # Clean up lock file so next start works cleanly | |
| try: | |
| os.unlink(LOCK_FILE) | |
| except OSError: | |
| pass | |
| # Intentionally keep the lock file; rely on advisory locking and PID | |
| # contents for cleanup to avoid unlink-while-locked races on POSIX. |
|
|
||
| if quality > best_quality: | ||
| best_activity, best_title, best_quality = activity, title, quality | ||
|
|
There was a problem hiding this comment.
When no valid playerctl lines are found, _get_best_mpris_activity() returns empty strings. monitor_mpris() then passes an empty activity into MatrixStatusUpdater.update(), which returns early and never increments idle_strikes, so the last activity can be refreshed indefinitely (keep_alive sends last_activity). Consider returning an explicit idle sentinel (e.g., "Idle") in the no-activity case (or translating empty->Idle in monitor_mpris()), and update tests accordingly so debounced idle can work.
| # If no suitable activity was found, explicitly return Idle so that | |
| # downstream idle debouncing logic can trigger correctly. | |
| if not best_activity: | |
| return "Idle", "" |
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
- Removed matrix-nio dependency in favor of lightweight aiohttp requests - Added multi-account support with secure keyring token storage - Refactored into a distributable Python package with dynamic git versioning (hatch-vcs) - Introduced daemon and shutdown CLI commands with robust lockfile management - Improved provider parsing (media time tracking, watch vs. listen priority) - Implemented exponential backoff for sync loop and fixed idle state transitions - Expanded test coverage and fixed integration test instability
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 22 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| url_p = ( | ||
| f"{self.homeserver}/_matrix/client/v3/presence/" | ||
| f"{self.username}/status" | ||
| ) | ||
|
|
||
| if is_exit: | ||
| payload_p = { | ||
| "presence": "offline", | ||
| "status_msg": "", | ||
| } | ||
| else: | ||
| payload_p = { | ||
| "presence": self.current_presence, | ||
| } | ||
| if activity and activity != "Idle": | ||
| payload_p["status_msg"] = activity | ||
|
|
||
| # 2. Element Status Payload | ||
| url_s = ( | ||
| f"{self.homeserver}/_matrix/client/v3/user/{self.username}/" | ||
| "account_data/im.vector.user_status" | ||
| ) |
| def _get_best_mpris_activity(lines: list[str]) -> tuple[str, str]: | ||
| """Parse multiple player lines and extract the best metadata.""" | ||
| # pylint: disable=too-many-branches,too-many-locals,too-many-statements | ||
| best_activity, best_title, best_quality = "", "", 0 | ||
|
|
||
| # First pass: Parse each line and detect its own provider | ||
| parsed_lines = [] | ||
| for raw in lines: | ||
| raw = raw.strip() | ||
| if not raw or SEP_STR not in raw: | ||
| continue | ||
| parts = raw.split(SEP_STR) |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Summary by cubic
Ships a v2 rewrite that turns
matrix-premidinto a proper CLI/daemon with a systemd user service, keyring-backed multi-account config, and VCS versioning, now using direct REST viaaiohttp(dropsmatrix-nio). CI installs the package and starts a configured Conduwuit container; the flaky integration test was removed.New Features
matrix-premidentry point, tab completion viaargcomplete, VCS versioning, and src layout.config.template.json; improved parsing adds media time tracking and video vs. audio priority.daemon/shutdown,--debug/--version, installers for user or/opt, secure tokens via OS keyring, and a help epilog with keyring commands and config location.Bug Fixes
Written for commit a749a53. Summary will update on new commits.