Auto Watch GitHub Repo

Automatically watch your repositories and your newly created repositories on GitHub.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Auto Watch GitHub Repo
// @namespace    github-auto-watch
// @version      2026.01.30
// @description  Automatically watch your repositories and your newly created repositories on GitHub.
// @author       Katorly
// @match        *://github.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @icon         https://github.githubassets.com/favicons/favicon.svg
// ==/UserScript==

(function () {
    'use strict'

    const PREFIX = '[Auto Watch Repo] '
    const STORAGE_KEY = 'github-auto-watch-pending'
    let listenerAdded = false

    function getPendingRepos() {
        return GM_getValue(STORAGE_KEY, [])
    }

    function addPendingRepo(owner, repo) {
        const pending = getPendingRepos()
        const key = `${owner}/${repo}`
        if (!pending.includes(key)) {
            pending.push(key)
            GM_setValue(STORAGE_KEY, pending)
        }
    }

    function removePendingRepo(owner, repo) {
        const key = `${owner}/${repo}`
        GM_setValue(STORAGE_KEY, getPendingRepos().filter(r => r !== key))
    }

    function isCreateRepoPage() { // `/new` or `/organizations/*/repositories/new`
        return location.pathname === '/new' || /^\/organizations\/[^/]+\/repositories\/new$/.test(location.pathname)
    }

    function isRepoPage() { // `/owner/repo/*`
        const match = location.pathname.match(/^\/([^/]+)\/([^/]+)\/?$/)
        return match && !['new', 'settings', 'organizations'].includes(match[1])
    }

    function getCurrentRepo() { // { owner, repo }
        const match = location.pathname.match(/^\/([^/]+)\/([^/]+)/)
        return match ? { owner: match[1], repo: match[2] } : null
    }

    function setupCreateRepoListener() {
        if (listenerAdded) return
        listenerAdded = true

        document.addEventListener('submit', (e) => {
            if (!isCreateRepoPage()) return

            const form = e.target.closest('form')
            if (!form) return

            const ownerButton = form.querySelector('#owner-dropdown-header-button [data-component="text"]')
            const repoInput = form.querySelector('#repository-name-input')

            if (ownerButton && repoInput?.value) {
                const owner = ownerButton.textContent.trim()
                const repo = repoInput.value.trim()
                if (owner && repo) {
                    addPendingRepo(owner, repo)
                    console.log(`${PREFIX}Saved pending: ${owner}/${repo}`)
                }
            }
        }, true)
    }

    // Get current username from top-right corner
    function getCurrentUser() {
        return document.querySelector('[data-login]')?.getAttribute('data-login')
    }

    function autoWatch() {
        const current = getCurrentRepo()
        if (!current) return

        const key = `${current.owner}/${current.repo}`
        const pending = getPendingRepos()
        const isPending = pending.includes(key)
        const isOwnRepo = getCurrentUser()?.toLowerCase() === current.owner.toLowerCase()

        if (!isPending && !isOwnRepo) return

        const watchButton = document.querySelector('[data-testid="notifications-subscriptions-menu-button"]')
        if (!watchButton) return

        const buttonText = watchButton.textContent || ''
        if (buttonText.includes('Unwatch') || buttonText.includes('Watching')) {
            if (isPending) removePendingRepo(current.owner, current.repo)
            return
        }

        watchButton.click()
        setTimeout(() => {
            const option = [...document.querySelectorAll('[role="menuitemradio"]')] // menuitemradio = menu appeared after click
                .find(el => /All Activity|Watching/i.test(el.textContent))
            if (option) {
                option.click()
                if (isPending) removePendingRepo(current.owner, current.repo)
                console.log(`${PREFIX}Watched: ${key}`)
            }
        }, 300)
    }

    function runPageLogic() {
        setupCreateRepoListener()
        if (isRepoPage()) setTimeout(autoWatch, 1000)
    }

    // Script entry point
    runPageLogic()

    // GitHub page load events
    document.addEventListener('turbo:load', runPageLogic)
    document.addEventListener('pjax:end', runPageLogic)

    // Fallback: listen for URL changes
    let lastUrl = location.href
    let checkTimer = null
    new MutationObserver(() => {
        if (checkTimer) return
        checkTimer = setTimeout(() => {
            checkTimer = null
            if (location.href !== lastUrl) {
                lastUrl = location.href
                setTimeout(runPageLogic, 500)
            }
        }, 100)
    }).observe(document.body, { childList: true, subtree: true })
})()