A browser-only React application that helps you detect suspicious GitHub accounts in your followers/following graph, review detection reasons, and block or unblock accounts in a controlled queue.
The app runs completely on the client side. Your token is used at runtime in the current tab and is never persisted to local storage, session storage, cookies, or URL parameters.
- Overview
- GitHub Action
- Core Features
- How the Workflow Works
- Detection Engine
- Security Model
- Getting Started
- Available Scripts
- Token and Permission Notes
- Operational Notes and Limitations
- Troubleshooting
- Contributing
This application is designed for users who want a safer and more transparent way to moderate their GitHub social graph.
The repository now also includes a reusable GitHub Action entrypoint for teams that want scheduled or manually triggered spam detection and optional blocking without using the web UI.
Instead of blindly auto-blocking, the app provides a review-first flow:
- Analyze followers and following accounts.
- Detect suspicious profiles with explainable reasons.
- Manually review and select accounts.
- Execute block/unblock operations with progress tracking and logs.
The interface is built with Ant Design and Zustand state management, and it includes a detection sensitivity system (Aggressive, Balanced, Conservative) to tune strictness.
The root of this repository exposes a JavaScript GitHub Action so other repositories can call the blocking workflow directly.
Consumers do not need to clone this repository. They can reference it directly from any workflow with uses: sametcn99/gh-block-spam-accounts@ref.
github-token- required personal access token used for analysis and optional blockingdetection-sensitivity-aggressive,balanced, orconservative(default:balanced)custom-keywords- comma or newline separated extra keywordstarget-type-followers,following, orboth(default:both)exclude-users- comma or newline separated logins to skipapply-blocks-trueto execute blocks,falsefor dry-run (default:false)delay-ms- delay between block requests in milliseconds (default:750)
authenticated-logincandidate-countdetected-countdetected-loginsblocked-countblocked-loginsfailed-countfailed-loginscan-read-blocked-usersrate-limit-remainingrate-limit-reset-at
name: Detect GitHub spam accounts
on:
workflow_dispatch:
inputs:
apply-blocks:
description: "Execute real block operations"
required: false
type: boolean
default: false
detection-sensitivity:
description: "Detection sensitivity"
required: false
type: choice
default: balanced
options:
- aggressive
- balanced
- conservative
target-type:
description: "Which social graph to scan"
required: false
type: choice
default: both
options:
- followers
- following
- both
schedule:
- cron: "0 6 * * *"
permissions:
contents: read
jobs:
spam-blocker:
runs-on: ubuntu-latest
steps:
- name: Run spam blocker
uses: sametcn99/gh-block-spam-accounts@1.2
with:
github-token: ${{ secrets.SPAM_BLOCKER_TOKEN }}
detection-sensitivity: ${{ github.event_name == 'workflow_dispatch' && inputs['detection-sensitivity'] || 'balanced' }}
target-type: ${{ github.event_name == 'workflow_dispatch' && inputs['target-type'] || 'both' }}
apply-blocks: ${{ github.event_name == 'workflow_dispatch' && inputs['apply-blocks'] || false }}A ready-to-copy remote usage example also exists in examples/spam-blocker-remote.yml.
The repository also includes ./spam-blocker.example.yml as a local self-test workflow for this repo. For real blocking, replace ${{ github.token }} with a PAT secret such as ${{ secrets.SPAM_BLOCKER_TOKEN }}.
For external consumption, use the pinned release tag @1.2 instead of @main.
The repository GITHUB_TOKEN is usually not enough to block users. Use either:
- a classic PAT with
userscope - a fine-grained PAT with
Block another user: write
- Browser-only execution
- GitHub Action dry-run mode for scheduled analysis
- Optional GitHub Action blocking mode for reviewed automation
- Runtime-only token handling (no persistence)
- Follower + following analysis
- Blocked-user list fetch (when token permissions allow)
- Spam detection with:
- weighted rule scoring
- strong-signal overrides
- heuristic signals
- basic obfuscation handling
- detection of accounts created within the last 30 days that follow more than 1,000 accounts
- sensitivity profiles
- Review table with per-account detection reasons
- Bulk select/clear for detections
- Controlled block queue with configurable delay
- Block outcomes (success/failure) per account
- Blocked users management:
- list current blocked accounts
- single-account unblock
- bulk unblock with selection
- Runtime logs with stage and level tags
- Contribution shortcuts for issue and PR flow
- Paste your GitHub token.
- Run analysis.
- The app fetches:
- authenticated user info
- followers
- following
- blocked users (if readable)
- Candidate accounts are generated by merging followers/following and removing:
- your own login
- duplicates
- accounts already blocked (if block list can be read)
- Candidate logins are fetched in batches.
- Profile text fields are normalized.
- Detection rules and heuristics are evaluated.
- Matched signals are aggregated into a reason list for each profile.
- Detected accounts are listed in a review table.
- Each row includes explainable reasons.
- You can select all, clear selection, or customize selection.
- Blocking runs account-by-account.
- Delay between requests is configurable.
- Progress and outcome counters are updated live.
- Errors are captured per account and shown in outcomes/logs.
- If readable, blocked users are listed in a dedicated table.
- You can unblock one account or multiple selected accounts.
- Unblock operations use the same queued execution style with progress and outcomes.
The detection engine combines static rules and heuristic signals.
- login
- name
- bio
- company
- location
- website URL
- twitter username
- account creation time
- following count
Each rule has:
- reason
- regular expression
- optional weight
- optional strong-signal flag
- Matched rules become signals.
- Duplicate reasons are merged (highest weight kept).
- Signals contribute to total score.
The engine includes checks such as:
- dense call-to-action token chains
- obfuscated call-to-action terms
- multiple handle references suggesting alternate/main account patterns
- bios that redirect to another handle, especially on sparse profiles
- high call-to-action density
- short-link profiles combined with call-to-action behavior
- noisy login/text patterns
- Aggressive: lower threshold, catches more profiles
- Balanced: default tradeoff
- Conservative: higher threshold, fewer false positives
This app intentionally keeps all sensitive behavior client-side and ephemeral.
- Token is stored only in in-memory state
- Token is not persisted to:
- localStorage
- sessionStorage
- cookies
- URL/query params
- No backend server is used for token processing
- API calls are made directly from the browser to GitHub
Important: because this is a browser-only app, you should run it in an environment you trust.
- Bun (recommended) or Node.js + a compatible package manager
- A GitHub Personal Access Token
bun installbun run devbun run buildbun run previewbun run dev- Start Vite dev serverbun run build- Type-check and build production bundlebun run preview- Preview production buildbun run format- Format code using Biomebun run lint- Lint code using Biomebun run check- Run Biome check (lint + formatting diagnostics)bun run lint:eslint- Run legacy ESLint config
For best results with block/unblock functionality:
- Classic PAT: include
userscope - Fine-grained PAT: include permission equivalent to blocking another user (write)
Behavior when permissions are limited:
- Analysis may still work
- Block/unblock may fail for specific operations
- Reading blocked-user list can be unavailable
The app surfaces these states through warnings and runtime logs.
- Detection is heuristic and rule-based, not ML-backed
- False positives and false negatives are possible
- You should review detections before blocking
- GitHub API limits and permissions can affect throughput
- Blocking/unblocking is executed sequentially by design for safer control and clearer outcomes
- Try
Aggressivesensitivity - Add temporary session keywords
- Verify analysis completed and profile fetch was successful
- Token might not have required read capability
- The app will continue analysis without blocked-list dedup optimization
- Verify token scope/permissions
- Check runtime logs for per-account error details
- Increase delay to reduce burst pressure
- Check GitHub rate limits in the UI
- Retry after reset time if remaining limit is low
Contributions are welcome, especially for improving detection quality.
Suggested contribution areas:
- new keyword/pattern rules
- better heuristics for edge cases
- UX improvements in review and moderation flow
- reliability and diagnostics
You can use the in-app contribution shortcuts or open issues/PRs directly in the repository.
Repository: https://github.com/sametcn99/gh-block-spam-accounts