【企画書】AIで変革するタスク管理と報酬支払いの未来！

# プロジェクト概要
AI技術を活用して、企画書から自動でタスクを生成し、相互レビューと報酬支払いまでを一元化する革新的なプラットフォームを開発します。フリーランスエンジニアや開発チームリーダーが抱える課題を解決し、効率的で公平なプロジェクト運営を実現。

:::note
CAMPFIREを通じて資金調達を計画していましたが、現在は募集を終了しました。
https://camp-fire.jp/projects/799339/view
:::

## 類似サービス
https://issuehunt.jp/

### 相違点

このプロジェクトと既存サービスの違いは以下のとおりです。

1. AIによるタスク生成
企画書やスクリーンショットをアップロードすると、AIが自動で必要なタスク（GitHub Issue）を生成します。これにより、タスクの手動作成の手間が大幅に削減されます。

2. レビューと報酬支払いの連携
プルリクエスト作成者とレビュワーの間で報酬が自動的に分配されます。プルリクエストの作成者には報酬の60%、レビュワーには40%が支払われ、報酬支払いはGitHubでのApprove操作に基づいて行われます。
割合は仮の値です。

3. 自動支払い機能
毎月GitHub Actionsを通じて、前月のプルリクエストとレビューに基づいた報酬が自動的に支払われます。PayPal APIなど決済APIを使用して支払いが処理されます。

# システムフロー
1. プロジェクトの企画書やアイデアを提出
プロジェクトの企画書やスクリーンショットをプラットフォームにアップロードします。

2. AIによるタスクの自動生成
提出された企画書や資料をもとに、**AIが自動的にプロジェクトに必要なタスク（GitHub Issue）を生成**します。AIは、プロジェクトの規模や内容に応じて適切なタスクの粒度を設定し、効率的にタスクをリストアップします。ユーザーは生成されたタスクを確認し、必要に応じて微調整を行うことが可能です。

3. タスクへの割り当てと進行
自動生成されたタスクはGitHubリポジトリに登録され、プロジェクトメンバーがそれぞれのタスクを選択し、取り組むことができます。進行状況やタスクの優先順位をダッシュボードで簡単に確認でき、プロジェクト全体の進行を一目で把握できます。

4. プルリクエストと相互レビュー
タスクが完了したら、ユーザーはプルリクエストを作成し、他の開発者がそのプルリクエストをレビューします。レビューが完了することで、そのタスクが正式にクローズされます。レビューは相互に行われるため、コミュニケーションの活性化とコードの品質向上が期待されます。

5. 報酬の自動支払い
プルリクエストがレビューされ、タスクがクローズされると、**報酬が自動的に支払われます**。決済にはPayPal APIやPayPay APIといった安全かつスムーズな支払いシステムを採用しており、迅速な報酬受け取りが可能です。

## インフラ構成図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411120/e21054ff-7ff6-1052-907d-57b3b1c1d378.png)

# システム構成

## プロジェクトの企画書やアイデアを提出
プロジェクトの企画書やスクリーンショットをプラットフォームにアップロードします。

### フロントエンド

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411120/6f20dc8a-65c3-1bbb-d9d8-f3a58dfbf999.png)

<details><summary>コード</summary>

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container">
        <div class="input-section">
            <label for="proposal">企画書を入力してください：</label>
            <textarea id="proposal" rows="5" placeholder="ここに企画書を入力..."></textarea>
            <div class="button-wrapper">
                <button id="list-issues-btn">Issueを列挙</button>
            </div>
        </div>
        <div class="issues-section" id="issues-section" style="display: none;">
            <h2>生成されたIssue一覧</h2>
            <ul id="issues-list"></ul>
            <button id="create-issues-btn">GitHub Issueを作成</button>
        </div>
        <div class="message" id="message"></div>
    </div>
    <script src="script.js"></script>
</body>
</html>
```

</details>

## AIによるタスクの自動生成

提出された企画書や資料をもとに、**AIが自動的にプロジェクトに必要なタスク（GitHub Issue）を生成**します。

### フロントエンド

<details><summary>コード</summary>

```js
document.getElementById('list-issues-btn').addEventListener('click', async function() {
    const proposal = document.getElementById('proposal').value.trim();
    if (proposal === "") {
        alert("企画書を入力してください。");
        return;
    }

    // OpenAI APIを呼び出してプロンプトを送信
    const response = await fetch('https://api.openai.com/v1/completions', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer YOUR_OPENAI_API_KEY`
        },
        body: JSON.stringify({
            model: 'gpt-3.5-turbo',
            prompt: `企画書から必要なタスクを抽出し、GitHub Issue形式で生成してください: ${proposal}`,
            max_tokens: 300
        })
    });

    const data = await response.json();

    // OpenAIのレスポンスからIssueを抽出
    const issues = data.choices[0].text.trim().split('\n').map(issueText => {
        const [title, ...description] = issueText.split(':');
        return { title: title.trim(), description: description.join(':').trim() };
    });

    const issuesList = document.getElementById('issues-list');
    issuesList.innerHTML = "";

    // 生成されたIssueをリストに表示
    issues.forEach((issue, index) => {
        const li = document.createElement('li');
        li.innerHTML = `<strong>${index + 1}. ${issue.title}</strong><br>${issue.description}`;
        issuesList.appendChild(li);
    });

    document.getElementById('issues-section').style.display = 'block';
});


document.getElementById('create-issues-btn').addEventListener('click', async function() {
    const token = 'YOUR_GITHUB_TOKEN'; // あなたのGitHubアクセストークンを設定
    const repo = 'YOUR_GITHUB_USERNAME/YOUR_REPO_NAME'; // GitHubのリポジトリ名

    const issuesList = document.querySelectorAll('#issues-list li');
    if (issuesList.length === 0) {
        alert("生成されたIssueがありません。");
        return;
    }

    for (let i = 0; i < issuesList.length; i++) {
        const issue = issuesList[i];
        const title = issue.querySelector('strong').innerText;
        const description = issue.querySelector('br').nextSibling.textContent.trim();

        // GitHub APIを使ってIssueを作成
        const response = await fetch(`https://api.github.com/repos/${repo}/issues`, {
            method: 'POST',
            headers: {
                'Authorization': `token ${token}`,
                'Accept': 'application/vnd.github.v3+json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                title: title,
                body: description
            })
        });

        if (response.ok) {
            console.log(`Issue "${title}" 作成成功`);
        } else {
            console.error(`Issue "${title}" 作成失敗`);
        }
    }

    document.getElementById('message').innerText = "GitHub Issueを作成しました！";
});

```

</details>

## 報酬の自動支払い

プルリクエストがレビューされ、タスクがクローズされると、**報酬が自動的に支払われます**。

### バックエンド

<details><summary>コード</summary>

```yaml
name: PayPal Payment

on:
  schedule:
    # 毎月5日の午前0時（UTC）に実行
    - cron: '0 0 5 * *'

jobs:
  paypal_payment:
    runs-on: ubuntu-latest
    steps:
      - name: チェックアウトリポジトリ
        uses: actions/checkout@v2

      - name: Pythonセットアップ
        uses: actions/setup-python@v2
        with:
          python-version: '3.x'

      - name: 依存関係をインストール
        run: |
          python -m pip install --upgrade pip
          pip install requests

      - name: 報酬情報を取得して支払いを実行
        env:
          PAYPAL_CLIENT_ID: ${{ secrets.PAYPAL_CLIENT_ID }}
          PAYPAL_CLIENT_SECRET: ${{ secrets.PAYPAL_CLIENT_SECRET }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          python scripts/paypal_payment.py
```

```python
import requests
import os
from datetime import datetime, timedelta

GITHUB_API_URL = "https://api.github.com"
REPO_OWNER = "YOUR_GITHUB_USERNAME"
REPO_NAME = "YOUR_REPO_NAME"
PAYPAL_API_URL = "https://api-m.sandbox.paypal.com/v1/payments/payouts"
PAYPAL_CLIENT_ID = os.getenv("PAYPAL_CLIENT_ID")
PAYPAL_CLIENT_SECRET = os.getenv("PAYPAL_CLIENT_SECRET")
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")

def get_last_month_range():
    now = datetime.now()
    first_day_of_current_month = datetime(now.year, now.month, 1)
    last_day_of_previous_month = first_day_of_current_month - timedelta(days=1)
    first_day_of_previous_month = datetime(last_day_of_previous_month.year, last_day_of_previous_month.month, 1)
    return first_day_of_previous_month, last_day_of_previous_month

def get_paypal_access_token():
    auth_response = requests.post(
        "https://api-m.sandbox.paypal.com/v1/oauth2/token",
        headers={"Accept": "application/json", "Accept-Language": "en_US"},
        auth=(PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET),
        data={"grant_type": "client_credentials"},
    )

    if auth_response.status_code != 200:
        raise Exception("PayPalトークンの取得に失敗しました")

    return auth_response.json()['access_token']

def get_pull_requests_and_reviews():
    # 前月の報酬対象のプルリクエストとレビューを取得
    first_day, last_day = get_last_month_range()
    headers = {'Authorization': f'token {GITHUB_TOKEN}'}
    
    url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/pulls?state=closed&since={first_day.isoformat()}&until={last_day.isoformat()}"
    response = requests.get(url, headers=headers)
    pull_requests = response.json()
    
    payments = []
    
    for pr in pull_requests:
        pr_author = pr['user']['login']
        pr_number = pr['number']
        
        # プルリクエストに紐づくレビューを取得
        reviews_url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/pulls/{pr_number}/reviews"
        reviews_response = requests.get(reviews_url, headers=headers)
        reviews = reviews_response.json()

        # 各プルリクエストの報酬設定とレビューの処理
        reward = 10000  # 各Issueに設定された報酬
        pr_reward = reward * 0.6  # プルリクエスト作成者の報酬
        review_reward = reward * 0.4  # レビュワーの報酬
        approved_reviews = [r for r in reviews if r['state'] == 'APPROVED']

        # プルリク作成者への報酬データを作成
        payments.append({
            'username': pr_author,
            'amount': pr_reward,
            'pull_requests': [pr_number],
            'reviews': []
        })

        # レビュワーへの報酬を均等に分配
        for review in approved_reviews:
            reviewer = review['user']['login']
            payments.append({
                'username': reviewer,
                'amount': review_reward / len(approved_reviews),
                'pull_requests': [],
                'reviews': [pr_number]
            })

    return payments

def get_paypal_email(github_username):
    # GitHub SecretsからユーザーのPayPalメールアドレスを取得
    paypal_email = os.getenv(f"PAYPAL_{github_username.upper()}_EMAIL")
    if not paypal_email:
        raise ValueError(f"{github_username} のPayPalメールアドレスが見つかりません")
    return paypal_email

def send_payment_to_paypal(paypal_email, amount, access_token):
    headers = {
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json'
    }
    payload = {
        "sender_batch_header": {
            "sender_batch_id": f"batch-{datetime.now().timestamp()}",
            "email_subject": "報酬支払い"
        },
        "items": [
            {
                "recipient_type": "EMAIL",
                "amount": {
                    "value": f"{amount:.2f}",
                    "currency": "JPY"
                },
                "receiver": paypal_email,
                "note": "プルリクエストとレビューの報酬",
                "sender_item_id": f"item-{datetime.now().timestamp()}"
            }
        ]
    }
    response = requests.post(PAYPAL_API_URL, json=payload, headers=headers)

    if response.status_code == 201:
        print(f"{paypal_email} に {amount} 円の支払い成功")
    else:
        print(f"{paypal_email} への支払い失敗: {response.status_code} - {response.text}")

def process_payments():
    access_token = get_paypal_access_token()
    payments = get_pull_requests_and_reviews()

    for payment in payments:
        github_username = payment['username']
        paypal_email = get_paypal_email(github_username)
        amount = payment['amount']
        
        # PayPalで報酬を支払う
        send_payment_to_paypal(paypal_email, amount, access_token)

if __name__ == "__main__":
    process_payments()

```

</details>

# クラウドファンディングの目的
このプロジェクトは、サービスの実現に向けて必要な資金を調達するためにクラウドファンディングを利用します。資金は主に以下の目的に使われます

- GPT APIの利用料
- 開発チームの人件費
- サーバーやインフラの費用

# 最後に
このプロジェクトは、より多くの人にAIを身近に感じてもらい、日々のコミュニケーションをサポートできるサービスを提供したいという思いからスタートしました。