← 返回 Skills 市场
alexwong27

Subscription Killer

作者 alexwong27 · GitHub ↗ · v1.2.1 · MIT-0
cross-platform ✓ 安全检测通过
80
总下载
1
收藏
0
当前安装
4
版本数
在 OpenClaw 中安装
/install subscription-killer
功能描述
Analyse a bank transactions CSV to detect recurring subscriptions, score cancellation priority, and surface actionable savings recommendations. Designed as t...
使用说明 (SKILL.md)

Subscription Killer

Analyse a bank transactions CSV file and surface every recurring subscription, ranked by cancellation / downgrade priority with estimated monthly savings.

Skill overview

This skill takes a single CSV file of bank transactions and:

  1. Sniffs the CSV structure to detect column names automatically.
  2. Normalises merchant names using a curated alias dictionary plus fuzzy matching (Levenshtein distance ≤ 2 tokens).
  3. Detects recurring charges by cadence (monthly / quarterly / annual) and amount consistency (±2 % tolerance).
  4. Scores each subscription by confidence (0–100).
  5. Ranks subscriptions by a composite priority score: spend × inactivity risk × price-creep factor × duplication penalty.
  6. Outputs a structured report with clear cancellation and downgrade actions.

Input format

The skill accepts any CSV export from a personal bank account. Common formats are auto-detected:

Bank / Provider Typical columns
Monzo Date, Name, Amount, Category
Starling Date, Counter Party, Amount, Balance
Revolut Started Date, Description, Amount, Currency
Standard OFX export Date, Description, Debit Amount
Generic date, merchant / description, amount (minimum required)

Column detection priority:

  • Date: looks for columns named date, transaction date, started date, posted date (case-insensitive). Falls back to first column that parses as ISO 8601 or DD/MM/YYYY.
  • Amount: looks for amount, debit amount, debit. Ignores credit / top-up rows (positive amounts in debit-positive conventions).
  • Merchant: looks for name, description, merchant, counter party, payee. Falls back to longest non-numeric column.

Core logic

Step 1 — Parse CSV

import csv, re, difflib
from datetime import datetime, timedelta
from collections import defaultdict

def sniff_columns(header: list[str]) -> dict:
    """Return {'date': col, 'amount': col, 'merchant': col} best guesses."""
    header_lower = [h.lower().strip() for h in header]
    DATE_HINTS    = ['date', 'transaction date', 'started date', 'posted date']
    AMOUNT_HINTS  = ['amount', 'debit amount', 'debit']
    MERCHANT_HINTS = ['name', 'description', 'merchant', 'counter party',
                      'payee', 'narrative']
    def first_match(hints):
        for hint in hints:
            if hint in header_lower:
                return header[header_lower.index(hint)]
        return None
    return {
        'date':     first_match(DATE_HINTS)     or header[0],
        'amount':   first_match(AMOUNT_HINTS)   or header[1],
        'merchant': first_match(MERCHANT_HINTS) or header[2],
    }

def parse_amount(raw: str) -> float | None:
    """Return a positive debit amount, or None for credits / errors."""
    cleaned = re.sub(r'[£$€,\s]', '', raw.strip())
    if not cleaned:
        return None
    try:
        val = float(cleaned)
        # Debit-positive CSV (most UK banks): only keep debits (negatives after
        # sign flip are credits — skip them)
        # Some CSVs use negative for debits; normalise to positive debit.
        return abs(val) if val != 0 else None
    except ValueError:
        return None

Step 2 — Normalise merchant names

Use a two-pass approach:

Pass 1 — Known alias dictionary (curated, highest-confidence)

MERCHANT_ALIASES = {
    # Streaming
    r'netflix':                 'Netflix',
    r'spotify':                 'Spotify',
    r'apple\.com/bill':         'Apple Subscriptions',
    r'itunes':                  'Apple Subscriptions',
    r'amazon prime':            'Amazon Prime',
    r'amzn\s?prime':            'Amazon Prime',
    r'disney\+|disneyplus':     'Disney+',
    r'youtube premium':         'YouTube Premium',
    r'hbo|max\.com':            'Max (HBO)',
    r'paramount':               'Paramount+',
    r'dazn':                    'DAZN',
    # Music
    r'tidal':                   'Tidal',
    r'deezer':                  'Deezer',
    r'soundcloud':              'SoundCloud',
    # Productivity / SaaS
    r'github':                  'GitHub',
    r'notion':                  'Notion',
    r'slack':                   'Slack',
    r'dropbox':                 'Dropbox',
    r'google\s?(one|storage|workspace)': 'Google One/Workspace',
    r'microsoft 365|office 365|msft': 'Microsoft 365',
    r'adobe':                   'Adobe Creative Cloud',
    r'figma':                   'Figma',
    r'zoom':                    'Zoom',
    r'1password|lastpass|bitwarden': 'Password Manager',
    r'nordvpn|expressvpn|surfshark':  'VPN Service',
    # News / Reading
    r'medium':                  'Medium',
    r'substack':                'Substack',
    r'kindle unlimited':        'Kindle Unlimited',
    r'audible':                 'Audible',
    r'the times|thetimes':      'The Times',
    r'financial times|ft\.com': 'Financial Times',
    r'economist':               'The Economist',
    # Fitness
    r'peloton':                 'Peloton',
    r'strava':                  'Strava',
    r'myfitnesspal':            'MyFitnessPal',
    r'calm|headspace':          'Meditation App',
    # Utilities / services
    r'amazon web services|aws': 'AWS',
    r'google cloud|gcp':        'Google Cloud',
    r'digitalocean':            'DigitalOcean',
    r'cloudflare':              'Cloudflare',
    r'railway\.app':            'Railway',
    r'openai':                  'OpenAI',
    r'anthropic':               'Anthropic',
}

def normalize_merchant(raw: str) -> str:
    """Return a clean, canonical merchant name."""
    s = raw.lower().strip()
    for pattern, canonical in MERCHANT_ALIASES.items():
        if re.search(pattern, s, re.IGNORECASE):
            return canonical
    # Pass 2 — strip noise tokens (card numbers, references, country codes)
    cleaned = re.sub(
        r'\b(www|com|co\.uk|ltd|limited|uk|us|gb|inc|gmbh)\b'
        r'|\d{4,}'      # card / ref numbers
        r'|\*+\S*'      # asterisk-prefixed tokens
        r'|[^a-z0-9 ]', # special chars
        '', s
    ).strip().title()
    return cleaned or raw.title()

Pass 2 — Fuzzy deduplication across the transaction list

After normalisation, cluster remaining merchant names by token-set similarity (difflib.SequenceMatcher ratio > 0.82). The most-frequent variant in each cluster becomes the canonical name.

Step 3 — Detect recurring charges

def detect_cadence(dates: list[datetime]) -> str | None:
    """
    Return 'monthly', 'quarterly', 'annual', or None.
    Requires ≥ 2 transactions for monthly/quarterly, ≥ 1 for annual hinting.
    """
    if len(dates) \x3C 2:
        # Single occurrence — flag as potential annual if amount > 30
        return 'annual_candidate'
    gaps = sorted([(dates[i+1] - dates[i]).days for i in range(len(dates)-1)])
    median_gap = gaps[len(gaps)//2]
    if 25 \x3C= median_gap \x3C= 35:   return 'monthly'
    if 85 \x3C= median_gap \x3C= 100:  return 'quarterly'
    if 340 \x3C= median_gap \x3C= 380: return 'annual'
    return None

def amounts_consistent(amounts: list[float], tolerance: float = 0.02) -> bool:
    """True if all amounts are within ±2 % of the median."""
    if not amounts:
        return False
    median = sorted(amounts)[len(amounts)//2]
    return all(abs(a - median) / median \x3C= tolerance for a in amounts)

Step 4 — Confidence scoring (0–100)

Factor Max points Logic
Cadence regularity 40 All gaps within ±3 days of median
Amount consistency 30 All amounts within ±2 %
Known merchant match 20 Exact alias dictionary hit
Transaction count 10 log₂(count) × 3, capped at 10

Confidence ≥ 70 → Confirmed subscription Confidence 40–69 → Probable subscription Confidence \x3C 40 → Possible / flag for review

Step 5 — Priority ranking

Each confirmed/probable subscription gets a composite priority score:

priority = monthly_cost
         × price_creep_multiplier   # max(amounts) / min(amounts); 1.0 = stable
         × duplication_penalty       # 1.5 if same category has 2+ subscriptions
         × inactivity_risk           # 1.3 if last charge > 45 days ago

Higher priority score = stronger cancellation candidate.

Special flags:

  • Trial trap: single charge in the last 7 days from a previously-unseen merchant, followed by a recurring pattern. Flag as "check for auto-renew".
  • Price creep: amount has increased > 5 % over the observation window. Flag with the exact amount delta.
  • Annual renewal due: annual-cadence subscription whose last charge was 300–365 days ago. Flag with estimated renewal date.
  • Unknown merchant: low-confidence recurring charge with no alias match. Flag for manual review.

Output schema

The skill returns a structured report. Print to stdout as JSON for downstream agent consumption, or render the human-readable summary below.

{
  "summary": {
    "observation_window_days": 90,
    "total_subscriptions": 12,
    "confirmed": 9,
    "probable": 2,
    "flagged_for_review": 1,
    "monthly_spend_gbp": 187.43,
    "annual_spend_gbp": 2249.16,
    "potential_monthly_saving_gbp": 54.20
  },
  "subscriptions": [
    {
      "merchant": "Adobe Creative Cloud",
      "cadence": "monthly",
      "median_amount": 54.99,
      "last_charge": "2025-04-01",
      "confidence": 95,
      "priority_score": 89.2,
      "flags": ["price_creep"],
      "price_creep_delta": 5.00,
      "cancel_url": "https://account.adobe.com/plans",
      "actions": ["cancel", "downgrade"]
    }
  ],
  "top_cancellation_targets": ["Adobe Creative Cloud", "..."],
  "unknown_merchants": ["TRSF 48291 REF77"],
  "annual_renewals_due": []
}

Human-readable summary (terminal / Telegram)

╔══════════════════════════════════════════╗
║        SUBSCRIPTION KILLER  v1.0         ║
╠══════════════════════════════════════════╣
║  12 subscriptions  •  £187/mo  •  £2,249/yr
╚══════════════════════════════════════════╝

TOP CANCELLATION TARGETS
1. Adobe Creative Cloud   £54.99/mo  ↑ price crept +£5.00
2. Calm                   £39.99/yr  last used 3 months ago
3. LinkedIn Premium       £29.99/mo  duplicate: 2 job-search tools

POTENTIAL SAVING:  £54/mo  •  £648/yr

UNKNOWN MERCHANTS (review manually)
• TRSF 48291 REF77  — £12.99/mo  (confidence 38%)

Cancel guidance (top 50 services)

The skill bundles a cancel_urls.json file mapping canonical merchant names to their self-serve cancellation pages. This removes friction for the user.

{
  "Netflix":                "https://www.netflix.com/cancelplan",
  "Spotify":                "https://www.spotify.com/account/subscription/cancel",
  "Adobe Creative Cloud":   "https://account.adobe.com/plans",
  "Amazon Prime":           "https://www.amazon.co.uk/mc/pipeline/cancelEndBenefit",
  "Disney+":                "https://www.disneyplus.com/account/subscription",
  "Microsoft 365":          "https://account.microsoft.com/services",
  "GitHub":                 "https://github.com/settings/billing/subscriptions",
  "Dropbox":                "https://www.dropbox.com/account/plan",
  "Zoom":                   "https://zoom.us/billing",
  "LinkedIn Premium":       "https://www.linkedin.com/premium/products",
  "Google One/Workspace":   "https://myaccount.google.com/payments-and-subscriptions",
  "OpenAI":                 "https://platform.openai.com/account/billing"
}

Invocation

# Analyse a CSV file
python3 subscription_killer.py --file transactions.csv

# Override currency display
SUBSCRIPTION_KILLER_CURRENCY=EUR python3 subscription_killer.py --file transactions.csv

# Output raw JSON for piping to another skill
python3 subscription_killer.py --file transactions.csv --json

The script reads SUBSCRIPTION_KILLER_CURRENCY from the environment for display only; all internal calculations use the amounts as-found in the CSV.


Roadmap (future skills in this suite)

This skill is intentionally scoped to subscription detection. Planned companion skills that share the CSV parsing layer:

Skill Description
savings-rate-analyser Track income vs spend, compute savings rate, compare to benchmarks
deposit-rate-scout Pull live UK/EU savings account rates via open banking APIs
emergency-fund-checker Assess months of runway based on average monthly spend
investment-nudger Identify investable surplus after subscriptions + essentials

The bank-csv-parser module within this skill will be extracted into a shared utility skill once the suite matures.


Security & privacy

  • All processing is local. No transaction data leaves the machine.
  • The skill does not write any files outside the working directory.
  • No API keys required for the core subscription detection flow.
  • The cancel_urls.json is a static lookup table; no network calls are made unless the user explicitly requests live rate data (future skill).
安全使用建议
This skill looks purpose-aligned and locally scoped, but it works on private bank transaction data. Before installing or running it, inspect the packaged files if provenance matters to you, provide only the CSV data needed for the analysis, and manually review any cancellation or downgrade recommendation before taking action.
功能分析
Type: OpenClaw Skill Name: subscription-killer Version: 1.2.1 The subscription-killer skill is a local utility designed to analyze bank transaction CSV files for recurring charges. The Python script (subscription_killer.py) implements heuristic column sniffing, regex-based merchant normalization, and cadence detection using only the Python standard library. Analysis of the code and SKILL.md confirms that all processing is performed locally, with no evidence of network communication, data exfiltration, or unauthorized file system access. The logic is transparent and aligns perfectly with the stated purpose of identifying and ranking subscriptions for potential savings.
能力标签
cryptocan-make-purchases
能力评估
Purpose & Capability
The purpose and behavior are coherent: it analyzes bank transaction CSVs for recurring subscriptions. Because the input is sensitive financial data and the output can guide subscription changes, users should review what they provide and what actions they take.
Instruction Scope
The documented workflow is user-invoked and scoped to a CSV file supplied with --file. The artifacts do not show instructions to override user intent, run automatically, or cancel accounts without user action.
Install Mechanism
There is no install spec and only python3 is required, but the registry lists the source as unknown and homepage as none. The included code was static-scanned clean, but provenance is limited.
Credentials
The environment needs are modest: python3 and an optional currency display variable. The registry's can-make-purchases capability appears related to billing/cancellation links rather than code that performs purchases or cancellations.
Persistence & Privilege
The provided artifacts show no background process, persistence mechanism, credential use, browser session access, or storage of bank data beyond processing the user-supplied CSV.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install subscription-killer
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /subscription-killer 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.2.1
Fix date parsing priority — full datetime formats now precede date-only formats, preventing silent midnight truncation of Wise/ISO timestamps
v1.2.0
Universal column sniffing — token-scoring replaces exact header matching, works with Wise, Revolut, HSBC and any generic bank export
v1.1.0
Added subscription_killer.py — full terminal output with colour, confidence bars, cancel URLs, price creep detection, and renewal alerts
v1.0.0
Initial release — CSV-based subscription detection with confidence scoring and cancel guidance
元数据
Slug subscription-killer
版本 1.2.1
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 4
常见问题

Subscription Killer 是什么?

Analyse a bank transactions CSV to detect recurring subscriptions, score cancellation priority, and surface actionable savings recommendations. Designed as t... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 80 次。

如何安装 Subscription Killer?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install subscription-killer」即可一键安装,无需额外配置。

Subscription Killer 是免费的吗?

是的,Subscription Killer 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Subscription Killer 支持哪些平台?

Subscription Killer 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Subscription Killer?

由 alexwong27(@alexwong27)开发并维护,当前版本 v1.2.1。

💬 留言讨论