{"id":28951573,"url":"https://github.com/invokable/adsense-project","last_synced_at":"2025-06-23T15:05:51.329Z","repository":{"id":300509157,"uuid":"1006328974","full_name":"invokable/adsense-project","owner":"invokable","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-22T04:49:54.000Z","size":66,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-22T05:25:42.233Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/invokable.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["invokable"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2025-06-22T02:38:57.000Z","updated_at":"2025-06-22T04:49:57.000Z","dependencies_parsed_at":"2025-06-22T05:35:53.727Z","dependency_job_id":null,"html_url":"https://github.com/invokable/adsense-project","commit_stats":null,"previous_names":["invokable/adsense-project"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/invokable/adsense-project","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/invokable%2Fadsense-project","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/invokable%2Fadsense-project/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/invokable%2Fadsense-project/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/invokable%2Fadsense-project/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/invokable","download_url":"https://codeload.github.com/invokable/adsense-project/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/invokable%2Fadsense-project/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261500618,"owners_count":23168071,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-23T15:05:50.228Z","updated_at":"2025-06-23T15:05:51.319Z","avatar_url":"https://github.com/invokable.png","language":"PHP","funding_links":["https://github.com/sponsors/invokable"],"categories":[],"sub_categories":[],"readme":"# Google AdSense Report\n\n[![Maintainability](https://qlty.sh/badges/cd9eeb28-893d-498a-8d10-e227728cb2f7/maintainability.svg)](https://qlty.sh/gh/invokable/projects/adsense-project)\n[![Code Coverage](https://qlty.sh/badges/cd9eeb28-893d-498a-8d10-e227728cb2f7/test_coverage.svg)](https://qlty.sh/gh/invokable/projects/adsense-project)\n\nThis project is a sample implementation for sending Google AdSense revenue reports via email, built with [laravel-console-starter](https://github.com/invokable/laravel-console-starter)\n\n## 🚀 Features\n\n- **Automated AdSense Reporting**: Fetches month-to-date revenue data from Google AdSense API\n- **Email Notifications**: Sends formatted reports with total metrics, daily averages, and recent 7-day breakdown\n- **Scheduled Execution**: Runs automatically every 3 hours via GitHub Actions\n- **Configurable Metrics**: Easy customization of reported AdSense metrics\n- **Comprehensive Testing**: Full test coverage with mocked API integration\n\n## 🛠 Tech Stack\n\n- **Framework**: Laravel 12.0 (Console Application)\n- **Language**: PHP 8.4\n- **API Integration**: Google AdSense Management API\n- **Authentication**: OAuth 2.0 with automatic token refresh\n- **Email**: Laravel Mail with Amazon SES\n- **Testing**: PHPUnit with Mockery\n- **Code Quality**: Laravel Pint (PSR-12)\n- **CI/CD**: GitHub Actions\n\n## 📋 Architecture\n\n### Core Components\n\n1. **AdSenseCommand** (`app/Console/Commands/AdSenseCommand.php`)\n    - Main Artisan command that orchestrates the reporting process\n    - Handles dependency injection and coordinates the workflow\n\n2. **AdSenseReport** (`app/AdSenseReport.php`)\n    - Service class for Google AdSense API integration\n    - Manages OAuth authentication and token refresh\n    - Converts API responses to usable array format\n\n3. **AdSenseReportTransformer** (`app/AdSenseReportTransformer.php`)\n    - Transforms raw AdSense API data into notification-ready format\n    - Handles data aggregation, metric calculations, and change analysis\n    - Separates data transformation concerns from notification delivery\n\n4. **AdSenseNotification** (`app/Notifications/AdSenseNotification.php`)\n    - Handles email formatting and delivery\n    - Generates Japanese/English reports with appropriate templates\n    - Focuses solely on notification presentation and delivery\n\n### Configuration\n\n- **`config/ads.php`**: AdSense API credentials and metric definitions\n- **`config/google.php`**: Google OAuth configuration\n- **`config/mail.php`**: Email delivery settings\n\n### Data Flow\n\n```\nAdSenseCommand → AdSenseReport → Google AdSense API\n       ↓               ↓\n       ↓        Raw API Data (totals, averages, rows)\n       ↓               ↓\n       ↓        AdSenseReportTransformer\n       ↓               ↓\n       ↓     Notification Data (keyMetrics, totalMetrics, etc.)\n       ↓               ↓\n       └────→ AdSenseNotification → Email Report → User\n```\n\n## 🧪 Testing\n\nComprehensive test suite with 100% core functionality coverage:\n\n```bash\n# Run all tests\ncomposer test\n\n# Run specific test suite\nphp artisan test tests/Unit/AdSenseNotificationTest.php\n\n# Code formatting\nvendor/bin/pint\n```\n\n**Test Coverage:**\n\n- Command execution and notification sending\n- Email content formatting and metric calculation\n- API integration with mocked Google services\n- Configuration management and error handling\n\n## ⚡ Quick Start\n\n```bash\n# Clone and install dependencies\ncomposer install\n\n# Copy environment file\ncp .env.example .env\n\n# Generate application key\nphp artisan key:generate\n\n# Configure your AdSense API credentials (see OAuth setup below)\n# Edit .env file with your Google credentials\n\n# Run the report command\nphp artisan ads:report\n\n# Run tests\ncomposer test\n```\n\n## 🔧 Environment Variables\n\nRequired environment variables in `.env`:\n\n```bash\n# Google OAuth Credentials\nGOOGLE_CLIENT_ID=your_client_id\nGOOGLE_CLIENT_SECRET=your_client_secret\nGOOGLE_REDIRECT=http://localhost/\n\n# AdSense API Tokens\nADS_ACCESS_TOKEN=your_access_token\nADS_REFRESH_TOKEN=your_refresh_token\n\n# Email Configuration (Amazon SES)\nMAIL_MAILER=ses\nMAIL_FROM_ADDRESS=sender@example.com\nMAIL_FROM_NAME=\"AdSense Reports\"\nMAIL_TO_ADDRESS=recipient@example.com  \nMAIL_TO_NAME=\"Report Recipient\"\n\n# AWS SES Credentials\nAWS_ACCESS_KEY_ID=your_aws_key\nAWS_SECRET_ACCESS_KEY=your_aws_secret\nAWS_DEFAULT_REGION=us-east-1\n```\n\n## 📊 Metrics Configuration\n\nCustomize reported metrics in `config/ads.php`:\n\n```php\n'metrics' =\u003e [\n    'PAGE_VIEWS',           // Page views\n    'CLICKS',               // Clicks\n    'COST_PER_CLICK',       // Cost per click\n    'ESTIMATED_EARNINGS',   // Estimated earnings\n    // 'AD_REQUESTS',       // Ad requests\n    // 'AD_REQUESTS_CTR',   // Click-through rate\n    // 'PAGE_RPM',          // RPM\n],\n```\n\n## How to Obtain AdSense API Access \u0026 Refresh Tokens Using `oauth2l` (No Web Server Required)\n\nThis guide walks you through the complete process to get AdSense Management API access and refresh tokens without\nrunning a web server.\n\n### Why This Setup Is Required\n\nSince this project is a console application that runs without a web interface, traditional OAuth authentication flows\ncannot be used. The AdSense API requires at least one-time user authorization to access account data, and service\naccounts are not supported for AdSense API access.\n\nThe solution is to:\n\n1. **One-time authorization**: Authenticate once to obtain a refresh token\n2. **Long-term access**: Use the refresh token to automatically obtain new access tokens indefinitely\n3. **No web server needed**: While you could set up a web server for OAuth flow, `oauth2l` provides a simpler\n   command-line approach\n\nOnce you have the refresh token, your console application can run completely autonomously, refreshing access tokens as\nneeded without any user interaction.\n\n### Setup Process\n\nYou'll create a Google Cloud project, configure OAuth, use `oauth2l` to authorize, and store the credentials in a `.env`\nfile.\n\n---\n\n### ✅ Step 1: Create a Project in Google Cloud Console\n\n1. Go to [Google Cloud Console](https://console.cloud.google.com/).\n2. Click the project dropdown in the top bar → **\"New Project\"**.\n3. Name your project and click **Create**.\n\n---\n\n### ✅ Step 2: Enable the AdSense Management API\n\n1. In the Cloud Console, navigate to **APIs \u0026 Services \u003e Library**.\n2. Search for **\"AdSense Management API\"**.\n3. Click on it, then click **\"Enable\"**.\n\n---\n\n### ✅ Step 3: Configure OAuth Consent Screen\n\n1. Go to **APIs \u0026 Services \u003e OAuth consent screen**.\n2. Choose **\"External\"** for user type, then click **Create**.\n3. Fill in required app information:\n    - App name, support email\n    - Developer contact email\n4. Under **Scopes**, click **\"Add or Remove Scopes\"** and include:\n   ```\n   https://www.googleapis.com/auth/adsense.readonly\n   ```\n5. Under **Test Users**, add your Google account email.\n6. Save and continue to publish the consent screen.\n\n---\n\n### ✅ Step 4: Create OAuth Client ID\n\n1. Go to **APIs \u0026 Services \u003e Credentials**.\n2. Click **\"Create Credentials\" \u003e \"OAuth Client ID\"**.\n3. Choose **\"Desktop app\"** for Application Type.\n4. Name it and click **Create**.\n5. Download the client credentials JSON file (e.g., `client_secret_XXX.json`) and save it securely.\n\n---\n\n### ✅ Step 5: Install `oauth2l` CLI\n\nYou can install `oauth2l` from the official GitHub repository:\nhttps://github.com/google/oauth2l\n\n---\n\n### ✅ Step 6: Fetch Access and Refresh Tokens\n\nRun the following command in your terminal:\n\n```bash\noauth2l fetch \\\n  --scope https://www.googleapis.com/auth/adsense.readonly \\\n  --credentials ./client_secret_XXX.json \\\n  --output_format json\n```\n\nThis will launch a browser asking you to authorize access with your Google account. After successful login and consent,\na JSON response like below will appear:\n\n```json\n{\n    \"access_token\": \"...access-token...\",\n    \"expiry\": \"2025-06-22T11:51:37.242796+09:00\",\n    \"refresh_token\": \"...refresh-token...\",\n    \"scope\": \"https://www.googleapis.com/auth/adsense.readonly\",\n    \"token_type\": \"Bearer\"\n}\n```\n\n---\n\n### ✅ Step 7: Store Tokens in `.env` File\n\nCopy the access and refresh tokens into a `.env` file:\n\n```dotenv\nGOOGLE_CLIENT_ID=client_id_from_json\nGOOGLE_CLIENT_SECRET=client_secret_from_json\nGOOGLE_REDIRECT=http://localhost/\n\nADS_ACCESS_TOKEN=...access-token...\nADS_REFRESH_TOKEN=...refresh-token...\n```\n\nThese tokens can now be used by your application or script to make authorized requests to the AdSense API. The access\ntoken will expire, but you can use the refresh token to get a new one programmatically.\n\n`GOOGLE_REDIRECT` can be anything, so set it as you like.\n\n---\n\n### 🔒 Tips\n\n- **Security**: Never commit `client_secret_XXX.json` or `.env` files to version control.\n- **Token Refreshing**: You can use `oauth2l fetch` again or implement automatic refresh logic using the refresh token.\n- **Scope Adjustments**: If you need to change scopes, update the OAuth consent screen and reauthorize.\n\n---\n\n### 🎉 Done!\n\nYou now have full access to the AdSense API with long-term credentials, without needing to run a web server. Use the\nstored tokens to authenticate your scripts or apps easily.\n\n## LICENSE\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finvokable%2Fadsense-project","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finvokable%2Fadsense-project","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finvokable%2Fadsense-project/lists"}