{
  "count": 15,
  "source": "https://yigitalpkorpe.com/projects/",
  "projects": [
    {
      "id": "ads-automation",
      "title": "Enterprise Ad Campaign Automation",
      "tagline": "Google Ads API automation that replaced a 50-step manual PPC workflow at Quandoo.",
      "summary": "Engineered a Google Ads API automation that replaced a 50-step manual workflow. Slashed CPA by 65% and reclaimed 30 hours of weekly analyst capacity for strategic growth initiatives.",
      "url": "https://yigitalpkorpe.com/projects/ads-automation/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/ads-automation/index.md",
      "category": "automation",
      "tech": [
        "Python",
        "Google Ads API",
        "Pandas",
        "BigQuery"
      ],
      "tags": [
        "Enterprise Automation",
        "Process Replacement",
        "API Integration"
      ],
      "impact": [
        "30 hrs/week reclaimed",
        "65% CPA reduction",
        "4× budget efficiency"
      ],
      "featured": true,
      "selected": true,
      "links": [],
      "body": "A Python application built on the [Google Ads API](https://developers.google.com/google-ads/api) to automate a process that, at Quandoo, originally consisted of fifty manual steps run by hand every week: pulling current performance, editing bids, cleaning up label hygiene, and reshuffling budget across campaigns. Turning that into a script eliminated the risk of human error in the handoffs between steps and reclaimed approximately 30 hours every week that had been going into list-pulling and spreadsheet edits instead of strategy.\n\n## Replacing a 50-step manual PPC workflow\n\nThe workflow it replaced was the kind that accretes over years: a checklist somebody wrote down once, followed literally by every analyst since, regardless of whether every line still earned its place. Bid edits, budget pacing checks, label hygiene, campaign-level QA: fifty discrete steps, each one small enough to seem not worth automating on its own, but adding up to a manual ritual that ran every single week without fail.\n\n## How the Google Ads API automation runs\n\nThe script runs the team's full weekly workflow in one command: pulling current performance data through the Google Ads API, applying the rule set the team had previously been running by hand, and writing the changes back through the same API. Configuration lives in human-readable YAML rather than buried in code, specifically so a non-technical marketer can open it, see what a rule does, and adjust a threshold without needing an engineer in the loop.\n\n## Auditing the process, not just automating it\n\nThe audit that came with building it mattered more than the automation itself. The same fifty steps had been running since long before I joined, and turning each one into code meant asking, one at a time, whether it still needed to exist. Roughly a third of them disappeared in the first month. They had quietly stopped being load-bearing, but nobody had noticed, because nobody wanted to be the one to break a working manual process to test that theory. Automation forced the question that a checklist never would have.\n\n## Results: reclaimed hours, lower CPA, better budget efficiency\n\n- **30+ hours a week reclaimed** across the user-acquisition team, redirected from list-pulling into actual strategy work.\n- **65% reduction in CPA** on the campaigns the automation touched.\n- **4× improvement in paid-budget efficiency**, measured across Paid Search, Social, and Display on UA spend approaching €1M a year.\n\nThe system didn't stay a one-off script. It became the substrate the in-house attribution framework was built on top of afterward, since it was already the thing pulling and reconciling campaign-level performance data on a schedule.\n\n## Related projects\n\n- [Daily Reporting Automation](/projects/daily-reporting/) applies the same Python-first approach to a recurring ETL and email workflow.\n- [Autonomous RevOps AI Agent](/projects/ai-marketing-agent/) turns warehouse data into scheduled strategy reports."
    },
    {
      "id": "ai-marketing-agent",
      "title": "Autonomous RevOps AI Agent",
      "tagline": "AI agent that turns BigQuery marketing data into executive-ready strategy reports.",
      "summary": "Delivers automated efficiency analysis and executive-grade strategy reports without analyst intervention. Runs Executive Briefs, Deep Dives, and Rebudgeting Scenarios as distinct modules.",
      "url": "https://yigitalpkorpe.com/projects/ai-marketing-agent/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/ai-marketing-agent/index.md",
      "category": "ai-agents",
      "tech": [
        "Python 3.11+",
        "BigQuery",
        "Claude API",
        "Jinja2",
        "Pandas"
      ],
      "tags": [
        "AI Agent",
        "RevOps",
        "Strategic Reporting"
      ],
      "impact": [
        "5+ hours saved weekly on manual analysis",
        "Generates 5 distinct rebudgeting scenarios per run"
      ],
      "featured": true,
      "selected": true,
      "links": [],
      "body": "An autonomous CLI tool that turns raw BigQuery marketing data into executive-grade strategy reports on its own schedule, no analyst required to kick it off or clean it up afterward. It's the difference between \"how are we doing?\" costing someone a four-hour SQL-and-spreadsheet session and costing one command.\n\n## Replacing manual SQL reporting\n\nWeekly strategic reporting used to mean hours of manual SQL extraction and spreadsheet work before anyone could act on what it said. By the time a report was actually ready, RevOps and Marketing leaders were reacting to budget problems and channel cannibalization that had already been running for days, instead of catching them while they were still cheap to fix.\n\n## Executive Briefs, Deep Dives, and Rebudgeting Scenarios\n\nThe agent runs three reporting modules on its own schedule. Executive Briefs give a fast top-line read for leadership. Deep Dives go channel by channel for anyone who needs to act on the numbers, not just see them. Rebudgeting Scenarios is the most concrete output: the agent calculates efficiency frontiers across channels and generates five distinct scenarios per run, each one a specific, actionable move, such as \"cut spend by 20% while maintaining pipeline,\" rather than a vague directional recommendation.\n\n## Architecture: BigQuery, Claude API, and four layers\n\nThe system is split into four strict layers so the data path stays predictable and auditable end to end:\n\n- **`queries.py`**: pure SQL against the warehouse, nothing else.\n- **`analyzer.py`**: Pandas data structuring and metric computation.\n- **`strategist.py`**: the only layer that calls the Claude API. The model receives structured JSON, never raw rows.\n- **`report.py`**: HTML report generation via Jinja2.\n\nPython does the heavy mathematical work first: efficiency frontiers, scenario modeling, the numbers that actually drive the recommendation. Claude only sees the structured JSON that comes out the other end, and uses it to run forensic analysis on top of numbers that are already correct, rather than being asked to do arithmetic on raw warehouse rows.\n\n## Keeping warehouse data secure\n\nThe application authenticates with Application Default Credentials through secure API gateways. No warehouse data leaves the perimeter except as model inputs explicitly scoped per module. The LLM sees exactly the structured JSON `strategist.py` hands it, and nothing upstream of that.\n\n## Why a scheduled AI agent beats a chat interface\n\nThis project is a working proof that an LLM can move past being a chat window someone has to remember to open, and become a scheduled, deterministic part of the reporting pipeline itself. It standardizes the reporting methodology across every executive report instead of leaving it to whoever's writing that week's version, and it turns \"how are we doing?\" from a four-hour analyst question into a one-command run that's ready before the standup.\n\n## Related projects\n\n- [Enterprise Ad Campaign Automation](/projects/ads-automation/) automates the campaign operations that feed performance data downstream.\n- [Daily Reporting Automation](/projects/daily-reporting/) is the simpler ETL-and-email precursor to this reporting agent."
    },
    {
      "id": "datalayer-sidepanel",
      "title": "DataLayer Sidepanel",
      "tagline": "Chrome side-panel extension that shows Google Tag Manager dataLayer events in real time.",
      "summary": "A Chrome extension that surfaces GTM dataLayer events in real time, directly in the browser's side panel. Featured on Product Hunt; used by marketing analytics teams as a default install.",
      "url": "https://yigitalpkorpe.com/projects/datalayer-sidepanel/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/datalayer-sidepanel/index.md",
      "category": "web-tools",
      "tech": [
        "JavaScript",
        "Chrome Extension API",
        "Google Tag Manager",
        "DataLayer API"
      ],
      "tags": [
        "Chrome Extension",
        "GTM",
        "Real-time Debugging"
      ],
      "impact": [
        "Featured on Product Hunt",
        "Used daily by marketing & analytics teams"
      ],
      "featured": true,
      "selected": true,
      "links": [
        {
          "label": "Install on Chrome Web Store",
          "href": "https://chromewebstore.google.com/detail/gkfagomcegoajdklkjnllkhhcocedoag"
        },
        {
          "label": "Product Hunt",
          "href": "https://www.producthunt.com/products/datalayer-sidepanel"
        }
      ],
      "body": "DataLayer Sidepanel is a Chrome extension for monitoring and analyzing Google Tag Manager dataLayer pushes and events in real time. Instead of opening the developer console, switching tabs, and parsing raw arrays by eye, it gives you a clean, persistent view inside Chrome's own side panel, running alongside whatever page you're auditing.\n\n## Real-time dataLayer monitoring\n\nEvery dataLayer push appears the moment it fires, with auto-refresh so there's no manual reload to trigger between page interactions. Click through a funnel, submit a form, trigger a custom event: the panel updates as the page does, live, without a second window competing for screen space.\n\n## Inspecting events without the console\n\nEach entry is expanded into a readable structure rather than a stringified blob you have to mentally parse. That distinction matters more than it sounds: `console.log(dataLayer)` gives you an array of objects that's technically inspectable but slow to scan under pressure, especially mid-QA when you're trying to confirm one specific field fired correctly. A structured, always-visible panel turns that into a glance instead of an expand-click-scroll cycle repeated for every event.\n\n## Why the side panel beats a modal debugger\n\nGTM debugging tooling has always been a little awkward. Google's own Tag Assistant works, but it's modal and disruptive: it takes over the browser window instead of sitting alongside your work. Console.log spelunking is the fallback, and it's worse. The side panel is the natural place for a live tracking inspector: always visible, never in the way, updating in the background while you interact with the page normally. Once Chrome shipped the side panel API, building the right tool on top of it became the obvious move rather than working around Tag Assistant's limitations.\n\n## Adopted by analytics teams\n\nFeatured on Product Hunt at launch, and adopted since as a default install by marketing analytics teams, specifically for QA'ing tracking changes before they ship to production, where catching a broken or missing dataLayer push before a release matters more than catching it after.\n\n## Related projects\n\n- [PageNote](/projects/page-notes/) keeps research notes attached to the URL where they matter.\n- [ChatGPT Copier](/projects/chatgpt-copier/) is another one-purpose Chrome extension designed to remove repeated friction."
    },
    {
      "id": "admocker",
      "title": "Ad Mocker",
      "tagline": "Ad mockup generator for Facebook, Instagram, and Google Display Network campaigns.",
      "summary": "A dynamic ad-preview tool for Facebook and Google campaigns. Lets marketers preview creative across Facebook feed, Instagram, and GDN with custom business names, headlines, and visuals. JavaScript and Canvas API.",
      "url": "https://yigitalpkorpe.com/projects/admocker/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/admocker/index.md",
      "category": "web-tools",
      "tech": [
        "JavaScript",
        "HTML5 Canvas",
        "CSS3",
        "Responsive Design"
      ],
      "tags": [
        "Side Project",
        "MarTech",
        "SaaS"
      ],
      "impact": [],
      "featured": true,
      "selected": true,
      "links": [
        {
          "label": "admocker.com",
          "href": "https://admocker.com/"
        },
        {
          "label": "Product Hunt",
          "href": "https://www.producthunt.com/posts/ad-mocker"
        }
      ],
      "body": "Every campaign launch I'd worked on involved someone pasting ad copy into Photoshop to fake how it would look in a Facebook feed. Ad Mocker replaces that with a real, in-browser mockup generator: pixel-accurate previews across every major placement, updated as you type, with nothing to install and nothing to export from a design tool first.\n\n## Facebook ad mockups\n\nPreview a Facebook feed placement the way it will actually render before it goes anywhere near Ads Manager: business name, profile photo, headline, primary text, and creative, composed the way Facebook's own UI lays them out. Facebook Stories placements are covered too, so a full-funnel campaign, feed and stories together, can be checked in one pass instead of guessing at how a story crop will look from a flat feed comp.\n\n## Instagram ad previews\n\nThe same mockup engine renders Instagram feed and Stories placements, matching the platform's cropping, safe zones, and caption treatment. It's the fastest way to catch a headline that runs past Instagram's safe zone, or a business name that truncates oddly, before a designer has to redo the creative from scratch.\n\n## Google Display Network mockups\n\nGDN is the least forgiving surface to mock up by hand, since a single creative gets reflowed across dozens of ad sizes and publisher layouts, and there's no single \"GDN preview\" screen the way Ads Manager has one for Facebook. Ad Mocker renders GDN placements directly, so a display campaign can be previewed the same way as the social placements, without exporting a dozen separate image sizes just to sanity-check one headline.\n\n## Customization and export\n\nEvery mockup is editable in real time: business name, headline, description text, and the creative image itself. Change a headline and the preview updates immediately, so a reviewer can compare two or three variants side by side before anything ships to a client or goes into an ad account. Finished mockups export to PNG, sized for a deck, a client email, or a quick Slack thread when someone just needs a gut check on how an ad reads.\n\n## Stack\n\nHTML5 Canvas does the rendering, plain JavaScript holds the state, CSS3 skins each platform's specific layout. No framework: the whole tool is too small for a dependency to be worth the weight, and Canvas gives pixel-level control over placement that a DOM-based mockup would fight against. Responsive by default, so the previews hold up on a laptop mid-call as well as on a desktop monitor. Featured on Product Hunt, live at [admocker.com](https://admocker.com/).\n\n## Related project\n\n[AI-Powered Content Engine](/projects/blog-automation/) is the publishing pipeline built to expand Ad Mocker's organic-search footprint."
    },
    {
      "id": "blog-automation",
      "title": "AI-Powered Content Engine",
      "tagline": "Automated blog pipeline that proved programmatic SEO works, even at baseline quality.",
      "summary": "A Python pipeline using the ChatGPT API and Unsplash to generate SEO-optimized blog posts. Proved the viability of programmatic SEO, driving a 2× increase in organic traffic on a side-project domain.",
      "url": "https://yigitalpkorpe.com/projects/blog-automation/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/blog-automation/index.md",
      "category": "automation",
      "tech": [
        "Python",
        "OpenAI API",
        "Unsplash API",
        "Basic NLP"
      ],
      "tags": [
        "SEO",
        "Content",
        "Side Project"
      ],
      "impact": [
        "2× organic traffic increase",
        "Fully automated post production"
      ],
      "featured": true,
      "selected": true,
      "links": [
        {
          "label": "GitHub repository",
          "href": "https://github.com/yigitalpkorpe/blog_automation"
        }
      ],
      "body": "I run [Ad Mocker](/projects/admocker/), live at [admocker.com](https://admocker.com/), and thought it had room to pick up organic traffic, but writing blog posts isn't my favorite work, and I was curious how Google would actually respond to AI-assisted content on a real, indexed domain rather than a test property. So I built the smallest possible pipeline that could take a topic and ship a published post end-to-end, with no manual step in between.\n\n## Topic generation from sitemap gaps\n\nThe pipeline starts by reading the site's current sitemap to see what's already covered, then identifies the gaps: topics adjacent to existing content that nothing on the site addresses yet. It asks the model for a batch of SEO-relevant title candidates against those gaps, so every post starts from an actual hole in the site's coverage instead of a topic picked at random.\n\n## Drafting and sourcing imagery automatically\n\nOnce a title is chosen, the post is generated through the ChatGPT API against a structured prompt and a fixed format the site already uses, so output is consistent post to post rather than reinventing structure every time. A light NLP pass over the title extracts keywords, which drive a call to the Unsplash API to pull relevant photography. Credits are attributed automatically, no manual image search or licensing check required.\n\n## Publishing pipeline\n\nThe finished post is saved as a static page and committed straight to the site. There's no CMS, no draft-review queue, no human gate between generation and publication. That's deliberate: the whole point of the experiment was measuring what happens when the content surface area exists, not building an editorial workflow around it.\n\n## Doubled organic traffic\n\nAfter the AI-assisted pages were indexed, impressions and clicks on the domain roughly doubled. A domain with thin content gets meaningfully more search visibility once the content surface area exists at all, even at a baseline quality bar. The content being AI-written mattered less than the content existing in the first place: more indexed pages addressing more specific queries beat fewer, thinner pages every time.\n\nThe code lives [on GitHub](https://github.com/yigitalpkorpe/blog_automation).\n\n## When programmatic SEO is the right tool\n\nThis only works because admocker.com had genuine coverage gaps, not because automation is a substitute for having something to say. A domain that's already thin loses far more from having ten pages than from having one bad page, since search engines reward topical coverage as much as they reward any single piece being excellent. The pipeline is intentionally narrow for the same reason the rest of my side projects are: it does one job, close a specific content gap on a specific domain, and doesn't try to be a general-purpose CMS.\n\n## Related projects\n\n- [Page Density Checker](/projects/seo-tool/) handles the crawling and content analysis behind the same SEO workflow.\n- [IsVisible.ai](/projects/isvisible/) checks whether search and AI crawlers can reach the pages once they are published."
    },
    {
      "id": "isvisible",
      "title": "IsVisible.ai",
      "tagline": "Free AI crawler visibility checker — tests whether GPTBot, ClaudeBot, and 11 others can reach your site.",
      "summary": "Tests a URL against 13 real crawlers and search bots, including GPTBot, ClaudeBot, and PerplexityBot, by reading robots.txt and llms.txt and sending live requests with each crawler's real user agent. Returns a 0-100 visibility score with an agent-by-agent breakdown of what's blocked. Built with the team at Hardal.",
      "url": "https://yigitalpkorpe.com/projects/isvisible/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/isvisible/index.md",
      "category": "web-tools",
      "tech": [
        "Crawler Simulation",
        "robots.txt & llms.txt Parsing",
        "REST API"
      ],
      "tags": [
        "AI Crawlers",
        "AI Visibility",
        "Free Tool"
      ],
      "impact": [
        "Scores 100/100, Grade A, when run on this site"
      ],
      "featured": true,
      "selected": true,
      "links": [
        {
          "label": "Try IsVisible.ai",
          "href": "https://isvisible.ai"
        }
      ],
      "body": "[IsVisible.ai](https://isvisible.ai) checks whether AI crawlers can actually reach a website. Paste in a URL and it tests against 13 crawlers and search bots: search engines (Googlebot, Bingbot), AI training crawlers (GPTBot, ClaudeBot, Google-Extended, CCBot, Bytespider, Applebot-Extended, meta-externalagent), and AI answer engines (ChatGPT-User, OAI-SearchBot, Claude-User, PerplexityBot). It reads the site's [robots.txt](https://developers.google.com/search/docs/crawling-indexing/robots/intro) and llms.txt, sends live requests using each crawler's real user agent, and merges the declared rules with what actually happens to produce a 0-100 visibility score and an agent-by-agent access map.\n\n## Why sites block AI crawlers without knowing it\n\nThe Hardal team has been building an AI visibility reporting product, one that shows companies which AI crawlers are actually visiting their site. Working on that, the same gap kept turning up: sites expected to show up when someone asked ChatGPT or Claude about them, with no idea they were blocking the exact crawlers that make that possible. Old robots.txt rules written for a pre-LLM web, a missing llms.txt, a WAF setting nobody had touched in years. None of it shows up unless someone actually simulates the crawlers and checks. IsVisible.ai is the free, standalone check for that specific gap.\n\n## Seeing it work\n\nRun against this site, yigitalpkorpe.com scores 100 out of 100, Grade A: fully accessible to Google Search, Bing/Copilot, Google Gemini and AI Overviews training, GPTBot training, and the rest of the 13 crawlers it checks.\n\n![IsVisible.ai report for yigitalpkorpe.com: 100/100, Grade A](../../assets/projects/isvisible-score.png)\n\n## How the crawler simulation works\n\nGetting the crawler simulation right took more tweaking than expected. Different bots honor different rules: some respect a wildcard `Disallow`, some only match their exact user-agent string, some ignore robots.txt for training purposes but respect it for live retrieval. Redirects add another layer: a crawler blocked on the final URL but allowed on a redirecting one produces a different result than checking either URL in isolation. How a site responds to a user agent it's never seen before turned into its own category of edge case, since some servers default to blocking unfamiliar bots rather than allowing them.\n\n## API access\n\nThe tool is free with no signup for the standard check, and it also exposes an unauthenticated REST API for checking a site programmatically, useful for teams that want the same crawler-access check as part of a CI pipeline or a recurring monitor, rather than a one-off manual lookup.\n\n## Related projects\n\n- [Page Density Checker](/projects/seo-tool/) analyzes the pages a crawler can reach for density and duplication.\n- [AI-Powered Content Engine](/projects/blog-automation/) automates the production side of the same SEO workflow."
    },
    {
      "id": "daily-reporting",
      "title": "Daily Reporting Automation",
      "tagline": "Python ETL pipeline that automates the daily marketing report, end to end.",
      "summary": "A Python ETL pipeline that replaced daily CSV merging across the team. Collects yesterday's data, merges it with running history, generates a report, and emails it to the right recipients on a schedule.",
      "url": "https://yigitalpkorpe.com/projects/daily-reporting/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/daily-reporting/index.md",
      "category": "automation",
      "tech": [
        "Python",
        "Pandas",
        "NumPy",
        "smtplib"
      ],
      "tags": [
        "ETL",
        "Scheduling",
        "Email"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [],
      "body": "A Python script that automates a daily, repetitive routine end to end: collecting raw data, merging it with the previous days' history, generating a summary, and sending the report by email. It runs on a cron schedule and is configured per use case, so the same pipeline can handle different data sources and different recipient lists without a rewrite.\n\n## The daily morning-report problem\n\nMost marketing teams have a \"morning report\": a spreadsheet someone updates every day before the standup. The work is mechanical, the schedule is unforgiving, and the output is the same shape every time: exactly the kind of task a script should own instead of a person. Miss a day by hand and the history has a gap that someone eventually has to explain or backfill; miss a day with a cron job and it just runs at 6am regardless of who's on vacation or out sick.\n\n## How the ETL pipeline runs\n\nEach run collects yesterday's data from the configured source, merges it with the running history file, and generates the report in the same format the team already reads, then emails it to the configured recipient list on schedule. Nothing about the routine depends on a person remembering to kick it off.\n\n## Trend context, not just a daily snapshot\n\nPandas merges yesterday's pull against the running history file, so the report always carries real trend context (this week versus last week, this month's trajectory) rather than a single day's numbers in isolation. A daily snapshot on its own answers \"what happened yesterday\"; merged against history, the same report starts answering \"is this normal,\" which is the question a standup actually needs answered.\n\n## Configuring new sources without touching code\n\nThe recipient list, source paths, and schedule all live in a small YAML config file, so pointing the pipeline at a new team or a new data source is an edit, not a deploy. Python handles the data work with Pandas and NumPy, and `smtplib` sends the finished report: a deliberately plain stack, chosen so a non-engineer can read the config and understand exactly what will run without needing to open the script itself.\n\n## Related projects\n\n- [Enterprise Ad Campaign Automation](/projects/ads-automation/) replaces a larger Google Ads workflow with the same kind of scheduled Python system.\n- [Autonomous RevOps AI Agent](/projects/ai-marketing-agent/) takes automated reporting from recurring summaries to strategic analysis."
    },
    {
      "id": "ga-slack-integration",
      "title": "GA-Slack Bot",
      "tagline": "Slack bot that posts Google Analytics anomaly alerts and scheduled reports.",
      "summary": "A Python bot that reads Google Analytics, watches for unusual traffic patterns, and posts anomalies plus scheduled summaries to a Slack channel. Built so a marketing team's daily check-in lives where they already work.",
      "url": "https://yigitalpkorpe.com/projects/ga-slack-integration/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/ga-slack-integration/index.md",
      "category": "automation",
      "tech": [
        "Python",
        "Google Analytics API",
        "Slack API"
      ],
      "tags": [
        "Slack Bot",
        "Analytics",
        "Anomaly Detection"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [],
      "body": "A Slack bot that watches a website's Google Analytics for unusual patterns and pushes both anomaly alerts and scheduled summaries directly into a team's Slack channel. The point is to remove the daily ritual of opening GA at all. If something matters, the bot says so; otherwise the channel stays quiet and nobody has to go looking for a problem that isn't there.\n\n## Anomaly alerts in Slack, not another GA login\n\nTraffic and conversion anomalies get flagged the moment the deviation appears, posted straight into the channel the team already has open. That's a deliberately different default from most analytics tooling, which assumes someone will log in and check. This one assumes nobody will, most days, and is built so that's fine.\n\n## Scheduled GA reports on your cadence\n\nBeyond anomaly alerts, the bot posts scheduled summaries at whatever cadence a team picks (daily, weekly, or monthly) and it's configurable per channel, since different teams inside the same company usually care about different metrics. A paid-acquisition channel and a content channel don't need the same report shape, and shouldn't have to filter one shared digest to find their numbers.\n\n## Why a statistical baseline beats ML here\n\nMost marketing teams don't have the traffic volume to make a machine-learned anomaly model reliable: thin data makes for noisy, overconfident predictions that cry wolf as often as they catch anything real. A simple statistical baseline, flagging deviation from a rolling average, is honest about what the signal actually supports at typical marketing-team traffic levels. It's also transparent in a way a trained model isn't: a marketer can see exactly why something got flagged, which matters for trusting the alert enough to act on it.\n\n## Stack\n\nPython runs the core logic. The GA Reporting API pulls the underlying data, and Slack's incoming webhooks push the alerts and summaries back out. Anomaly detection stays a simple statistical baseline rather than a trained model: the right level of sophistication for the signal density most marketing teams actually have, not the level a vendor demo would show off.\n\n## Related projects\n\n- [Daily Reporting Automation](/projects/daily-reporting/) runs a parallel scheduled reporting workflow over email.\n- [Autonomous RevOps AI Agent](/projects/ai-marketing-agent/) expands automated reporting into executive analysis and rebudgeting scenarios."
    },
    {
      "id": "chatgpt-copier",
      "title": "ChatGPT Copier",
      "tagline": "Chrome extension that adds a one-click copy button to every ChatGPT response.",
      "summary": "A small Chrome extension that puts a copy button next to every ChatGPT answer. Removes the awkward triple-click-and-drag from a workflow people repeat hundreds of times a day.",
      "url": "https://yigitalpkorpe.com/projects/chatgpt-copier/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/chatgpt-copier/index.md",
      "category": "productivity",
      "tech": [
        "JavaScript",
        "Chrome Extension API",
        "Content Scripts"
      ],
      "tags": [
        "Chrome Extension",
        "Productivity"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [
        {
          "label": "Chrome Web Store",
          "href": "https://chromewebstore.google.com/detail/chatgpt-copier/nkbmeknchklpmhfgafgfnmmegdacafbe"
        },
        {
          "label": "Product Hunt",
          "href": "https://www.producthunt.com/posts/chatgpt-copier"
        }
      ],
      "body": "A small Chrome extension that adds a one-click copy button to every ChatGPT response. There's no configuration and no settings page. Install it, and the button just appears wherever ChatGPT's responses do, from the next reply onward.\n\n## The triple-click-and-drag problem\n\nCopying a ChatGPT answer without the extension means the triple-click-drag-Cmd-C ritual: click to select a paragraph, drag to extend the selection across line breaks and code blocks, and hope the formatting comes along cleanly instead of picking up stray whitespace or breaking mid-sentence. It's a small friction, but a small friction repeated constantly: people who use ChatGPT heavily do that dance several hundred times a week. The math justifies a tool this narrow.\n\n## One-click copy, and nothing else\n\nA copy button doesn't need an options page, an account, or a dashboard. It needs to work every single time and get out of the way otherwise. The scope stays deliberately narrow: copy the response text, cleanly, and do nothing else. For a utility this small, every added feature is one more chance to break the one thing it's supposed to do reliably, so the extension resists the urge to grow one.\n\n## How it tracks new responses\n\nChatGPT's interface streams responses in and re-renders as the conversation grows, so a copy button can't just be placed once at page load. It has to appear correctly on messages that don't exist yet when the extension first runs. A content script watches the DOM for new response blocks as they're added and attaches the button to each one as it appears, rather than relying on a fixed list of selectors that would break the moment ChatGPT's UI shifts.\n\n## Install and permissions\n\nVanilla JavaScript, no external dependencies, no analytics, no framework: there's nothing here that needs one. The extension asks for nothing beyond permissions on the chat domain itself: no broad host access, no background tracking, no data leaving the browser. Available on the [Chrome Web Store](https://chromewebstore.google.com/detail/chatgpt-copier/nkbmeknchklpmhfgafgfnmmegdacafbe) and featured on [Product Hunt](https://www.producthunt.com/posts/chatgpt-copier).\n\n## Related projects\n\n- [PageNote](/projects/page-notes/) is another deliberately narrow Chrome extension built around one repeated browser task.\n- [DataLayer Sidepanel](/projects/datalayer-sidepanel/) uses Chrome's side panel for real-time analytics debugging."
    },
    {
      "id": "page-notes",
      "title": "PageNote",
      "tagline": "Chrome extension that pins a note to any URL, right where you'll see it again.",
      "summary": "A Chrome extension for marketers and researchers that pins per-URL notes to whatever page you're looking at. Built for the 'what was I thinking when I last looked at this?' problem.",
      "url": "https://yigitalpkorpe.com/projects/page-notes/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/page-notes/index.md",
      "category": "productivity",
      "tech": [
        "JavaScript",
        "Chrome Extension API",
        "LocalStorage",
        "DOM"
      ],
      "tags": [
        "Chrome Extension",
        "Note-taking",
        "Marketing Research"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [
        {
          "label": "Chrome Web Store",
          "href": "https://chromewebstore.google.com/detail/page-notes/chcogmlnlajbbfpicbljbhnpimjdakln"
        }
      ],
      "body": "PageNote is a Chrome extension that lets you attach a note to any URL. Open the page again, even weeks later, and your notes are right there waiting. No separate app to open, no filename to remember.\n\n## Notes for competitor and landing-page research\n\nIt's built for the kind of research workflow marketers and researchers run every day: comparing a handful of competitor sites side by side, tracking how a campaign landing page changes over time, or just remembering \"I already looked at this and decided X\" the next time the same URL comes up in a different context. The note-taking habit is one click on the same toolbar icon regardless of which site you're on, so it doesn't compete for attention with whatever tool you're actually using to do the research.\n\n## Why per-URL, not a separate app\n\nA dedicated notes app means switching windows and re-establishing context from scratch: which tab was this about, what was the note referring to. Pinning the note to the URL itself keeps the note and the evidence for it in the same place, since the note only really makes sense next to the page it's about. Revisit the URL later and the context comes back with it automatically, with nothing to search for across a separate notes archive. It also removes a small but real daily decision (\"which app do I open for this\") since the answer is always the same regardless of what site you're currently looking at.\n\n## No accounts, no sync, no telemetry\n\nStandard Chrome extension setup: content scripts handle the on-page UI, and the Chrome storage API persists notes per URL locally. The surface area stays deliberately small: it doesn't create accounts, doesn't sync across devices, and doesn't send telemetry anywhere. The tradeoff is explicit: notes stay on the machine where you wrote them, in exchange for a tool that has nothing to leak and nothing to sign into.\n\n## Related projects\n\n- [ChatGPT Copier](/projects/chatgpt-copier/) removes another small, frequently repeated action from the browser.\n- [DataLayer Sidepanel](/projects/datalayer-sidepanel/) is the analytics-focused extension in the same family of tools."
    },
    {
      "id": "monitor",
      "title": "Bedeni Geldi mi?",
      "tagline": "Watches a product page and emails you the moment your size is back in stock.",
      "summary": "A small site (\"Has My Size Arrived?\" in Turkish) that watches a product URL for a specific size and emails the user when it becomes available. Born from the irritation of constantly refreshing brand pages.",
      "url": "https://yigitalpkorpe.com/projects/monitor/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/monitor/index.md",
      "category": "web-tools",
      "tech": [
        "Python",
        "Web Scraping",
        "PHP",
        "Email Automation"
      ],
      "tags": [
        "Personal Tool",
        "Web Scraping",
        "Notifications"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [],
      "body": "\"Bedeni Geldi mi?\" (Turkish for *Has My Size Arrived?*) is a small web service that monitors a product URL for a specific size and emails the user the moment it comes back in stock. The interface is a single form: brand, link, size, your email. No account, no dashboard to check back on.\n\n## The restock-checking problem\n\nMostly this exists because I wanted it. The pattern of refreshing a clothing brand's product page, hoping a single sold-out size has restocked since the last check, is a small but real tax on attention: a background task your brain keeps re-running for no good reason. A daemon does this far better than a person: it doesn't get bored, doesn't forget, and doesn't need to be reminded to check again tomorrow.\n\n## How the watch list works\n\nEach submission becomes an entry on a watch list that a scheduled job walks on a fixed interval, re-scraping every tracked product and diffing current size availability against the last known state for that specific size. The moment a watched size flips from sold-out to available, the notification fires on its own. There's nothing left for the user to check, refresh, or remember. The diff is per size, not per product, so a restock of a different size on the same page doesn't trigger a false alert.\n\n## Brand-specific scraping, one shared notification path\n\nThe form runs on a PHP front end; a Python worker handles scraping and notification, triggered by the same scheduled job that walks the watch list. The scraping side is brand-specific by necessity (every retailer marks up size availability differently, so the parsers diverge site by site), but the notification path is shared across all of them. Adding support for a new brand means writing one new parser, not rebuilding the pipeline.\n\n## Related project\n\n[Series Watcher](/projects/series-watcher/) uses the same scheduled-email pattern to deliver a weekly TV episode digest."
    },
    {
      "id": "seo-tool",
      "title": "Page Density Checker",
      "tagline": "Python SEO toolkit: site crawler, keyword density checker, and duplicate-content detector.",
      "summary": "A Python toolkit covering the SEO tasks that don't justify a paid tool subscription: crawling a site, measuring word density per page, comparing against the top Google results, and flagging near-duplicate content via cosine similarity.",
      "url": "https://yigitalpkorpe.com/projects/seo-tool/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/seo-tool/index.md",
      "category": "web-tools",
      "tech": [
        "Python",
        "Web Scraping",
        "NLP",
        "Cosine Similarity"
      ],
      "tags": [
        "SEO",
        "Academic Project",
        "Toolkit"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [],
      "body": "Page Density Checker is an SEO toolkit built during my Master's degree. The aim was simple: a budget-friendly, code-first alternative to the parts of paid SEO suites that are mostly arithmetic (crawling, word density, similarity), the kind of tools that are easy enough to write and unreasonable to license by the month.\n\n## Web crawler\n\nA crawler walks a site and pulls per-page metadata for analysis: titles, headings, word counts, and the structural information the other three tools build on. It's the foundation the rest of the toolkit runs on top of, rather than a standalone feature.\n\n## Page and search density checkers\n\nThe page density checker reports word density on any URL, useful for checking whether a target keyword is actually present at a reasonable frequency, or missing entirely. The search density checker goes a step further: run a query, fetch the top 10 Google results, and get their density profiles back side by side. That turns \"what does a page that ranks for this term actually look like\" from a manual, page-by-page exercise into one report covering the whole first page of results at once.\n\n## Duplicate content detection via cosine similarity\n\nThe similar page checker runs cosine similarity across a site's pages to flag near-duplicates and cannibalization risk: two pages competing for the same query because they're similar enough that Google can't confidently pick one to rank. That's a problem that's invisible from inside a single page and only shows up when you compare documents directly, which is exactly what the similarity scoring is for.\n\n## Why build vs. buy\n\nThe individual pieces here (crawl a site, count words, compare two documents) are each a few hundred lines of Python once you know what to build. Paid SEO suites bundle them behind a subscription because the value they're selling is the workflow around the numbers, not the arithmetic itself. Building the toolkit from scratch was as much about understanding what those suites actually compute under the hood as it was about avoiding the subscription.\n\nRequests and BeautifulSoup handle the scraping, Pandas shapes the resulting data, and a simple NLP layer covers keyword extraction and similarity scoring, all in Python, no paid APIs anywhere in the pipeline.\n\n## Related projects\n\n- [AI-Powered Content Engine](/projects/blog-automation/) turns identified content gaps into published pages.\n- [IsVisible.ai](/projects/isvisible/) tests whether search and AI crawlers can access those pages."
    },
    {
      "id": "series-watcher",
      "title": "Series Watcher",
      "tagline": "Python script that emails a weekly digest of episodes airing on the shows you follow.",
      "summary": "A small weekly script that uses The Movie Database (TMDB) API to check the shows on a watch list, notes which episodes aired in the last week and which are coming next, and emails the summary.",
      "url": "https://yigitalpkorpe.com/projects/series-watcher/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/series-watcher/index.md",
      "category": "automation",
      "tech": [
        "Python",
        "TMDB API",
        "Pandas",
        "smtplib"
      ],
      "tags": [
        "Personal Tool",
        "Automation",
        "Email"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [],
      "body": "A Python script that uses [The Movie Database (TMDB) API](https://developer.themoviedb.org/docs/getting-started) to gather data on a list of TV series and email a weekly summary: what aired in the past week, what's coming in the next one. Configured by editing a list, with no UI and no settings page to open.\n\n## What airs this week\n\nIt's the kind of script that exists because the alternative is checking five different streamers' schedules on a Sunday evening, one at a time, to see whether anything on your list actually has a new episode. The email itself stays deliberately plain text, a scannable list rather than a dashboard, because the whole point is trading five minutes of app-switching for thirty seconds of reading a single message.\n\n## How the watch list decides what to report\n\nThe watch list is just a list of show titles, nothing more elaborate. Each run, the script queries MovieDB for every title on the list, compares each show's air dates against the current week's date range, and buckets episodes into two groups: \"aired\" for the past week, \"upcoming\" for the next one. The API is treated as the source of truth on every single run, so there's no local schedule cache to keep in sync or go stale. Add a show to the list and it's covered on the very next run, with full history already available through the API.\n\n## One list across every streaming service\n\nMost streaming apps will tell you when their own new episode drops, but only inside that one app, and only for shows on that one service. A watch list spanning shows across several different services only comes together if something sits above all of them, which is what a single weekly email, sourced from one API instead of five separate apps, actually gives you.\n\n## Stack\n\nThe TMDB API supplies the show and episode data, Pandas handles the date-range comparisons that sort episodes into aired versus upcoming, and `smtplib` sends the finished email: plain Python throughout, with no framework and no database, since a flat list and a weekly cron run are all the problem actually needs.\n\n## Related project\n\n[Bedeni Geldi mi?](/projects/monitor/) applies the same scheduled monitoring and email-notification pattern to product restocks."
    },
    {
      "id": "murder-matrix",
      "title": "Murder Matrix",
      "tagline": "Cozy logic-deduction puzzle game for iOS. No ads, no timers, no accounts.",
      "summary": "An indie iOS puzzle game. Procedurally generated cases across six original locations, three difficulty tiers, a daily challenge with a shareable seed. No ads, tracking, or accounts, built in pure SwiftUI.",
      "url": "https://yigitalpkorpe.com/projects/murder-matrix/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/murder-matrix/index.md",
      "category": "ios-apps",
      "tech": [
        "SwiftUI",
        "SwiftData",
        "CloudKit"
      ],
      "tags": [
        "iOS App",
        "Puzzle Game",
        "Solo Project"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [
        {
          "label": "Download on the App Store",
          "href": "https://apps.apple.com/us/app/murder-matrix/id6766099815"
        }
      ],
      "body": "Murder Matrix is a cozy logic-deduction puzzle for iOS. Each case asks you to place suspects on a small grid based on the clues they give, and the empty square left over reveals the victim. It's a small, careful game, closer to a crossword than a thriller. Designed and built solo, pure SwiftUI, no third-party dependencies, from first principles.\n\n## How to play\n\nRead the clues. Each suspect tells you something about where they were standing relative to the others. Place them on the grid so no two share a row or column, using the clues to eliminate positions one at a time the way you'd work a logic puzzle on paper. When all but one suspect is placed, the last empty square reveals itself, and whoever shares that room with it is the murderer.\n\n## Six locations, three difficulty tiers\n\n- **Six original locations**, each with its own pastel palette and floor plan, so the puzzle grid itself changes shape and mood from case to case.\n- **Procedurally generated puzzles** with one guaranteed solution. Every case is unique: nothing is hand-authored or reused, so there's no puzzle database to exhaust.\n- **Three difficulty tiers**: Rookie, Detective, and Mastermind, scaling grid size and clue density rather than adding artificial obstacles.\n- **A daily challenge** with a shareable seed, so you can compare your solve time against a friend working the exact same case.\n- **No ads, tracking, or accounts to set up.** Open the app and there's a puzzle waiting, nothing to configure first.\n\n## A cozy puzzle with no timer pressure\n\nNothing about the format punishes the player for taking their time. Cozy was the design target from the start: the tension comes from the logic itself, not from the interface pressuring you to hurry. The daily challenge is the one nod to competition in the whole game, and even that stays asynchronous: you compare a solve time with a friend whenever it's convenient, not against a live leaderboard ticking in real time.\n\n## Built with SwiftUI, no third-party trackers\n\nSwiftUI handles the interface, SwiftData handles local storage, and CloudKit provides optional cross-device sync for anyone who plays on both an iPhone and an iPad. Everything else runs entirely on-device, with no server component and no third-party analytics or SDKs collecting data in the background. That's a deliberate match for a game whose whole pitch is that it doesn't want anything from you beyond attention to the puzzle.\n\n## Related project\n\n[Musento](/projects/musento/) is another privacy-conscious iOS app built in SwiftUI, this time for logging museum visits."
    },
    {
      "id": "musento",
      "title": "Musento",
      "tagline": "Letterboxd for museums. Log every visit, earn points, and race friends up the leaderboard.",
      "summary": "Letterboxd for museums: an iOS app for keeping a personal record of the museums you visit. Log a visit with a date and private note, earn points and achievements, follow friends and compare leaderboards, and browse an extensive museum catalogue. Built with SwiftUI, no ads and no data sold.",
      "url": "https://yigitalpkorpe.com/projects/musento/",
      "markdownUrl": "https://yigitalpkorpe.com/projects/musento/index.md",
      "category": "ios-apps",
      "tech": [
        "SwiftUI"
      ],
      "tags": [
        "iOS App",
        "Museums",
        "Travel",
        "Social"
      ],
      "impact": [],
      "featured": false,
      "selected": false,
      "links": [],
      "body": "Musento is Letterboxd for museums. Find a museum, log the visit with a date and an optional private note, and it joins your personal collection: points, achievements, and a history you can look back on years later. Built solo in SwiftUI.\n\n## Log a visit, keep a diary\n\nEvery visit is a small record: which museum, which date, and what you thought of it. The date picker reaches back decades, so a lifetime of museum trips can be entered retroactively, not just the ones from after installing the app. Notes are private by default — visible only to you, never shown to friends or on any leaderboard — while the visit itself, the points it earned, and the achievements it unlocks become part of a public collection friends can see.\n\n## An extensive museum catalogue\n\nRather than maintain a hand-curated list, Musento builds its museum catalogue from [Wikidata](https://www.wikidata.org/), covering a huge range of institutions worldwide, searchable by name or city and browsable by what's nearby. Location is optional and used only to answer \"what's near me\" — it's sent for that one request and never stored, and the app never tracks location in the background. Search works fine without granting it at all.\n\n## Points, achievements, and friends\n\nVisits earn points and unlock achievements, and friends whose requests you've accepted can see your collection alongside their own. Global leaderboards rank handles and scores, turning a solitary hobby into something a little competitive. None of it depends on ads or trackers: points and standings have no monetary value, and the app doesn't sell data to anyone.\n\n## Private by design\n\nSign in with Apple or email, with your data protected behind row-level access controls so one member can never read another's private data. Deleting an account from Settings removes the profile, visits, notes, friendships, and achievements immediately and irreversibly.\n\nRead Musento's [privacy policy](/musento/privacy/) and [terms of use](/musento/terms/), or visit [support](/musento/support/) for help.\n\n## Related project\n\n[Murder Matrix](/projects/murder-matrix/) is a second independently built iOS app, a logic-deduction game with no ads, trackers, or accounts."
    }
  ]
}