【個人開発】Qiitaの週間レポートアプリを作りました

# はじめに

技術記事を書くのが趣味の たま と申します。

**Qiitaの週間レポートを送ってくれるアプリを作りました！**

:::note warn
※サービスとして公開はしていませんが、コードは公開します。
:::

# 完成品
:::note
![スクリーンショット 2024-09-03 19.41.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/e2e4ff39-15ba-30ed-be5d-b8640d72dbc1.png)
:::

:::note
Qiita公式のweekly reportと比較し、以下の点が長所です。

- 投稿した記事一覧の表示
- 記事ごとにいいね・ストック数を表示
- コメント・ビュー数の表示機能
:::
# きっかけ

きっかけは「しずかなインターネット」の月間振り返りレポートです。

振り返りと、ちょっとした達成感を得られるのが推しポイントです。

# 環境

| 環境 | 具体名称 |
|:-:|:-:|
| 動作環境  | Google Apps Script  |
| 言語  | JS / HTML / CSS |
| API | Qiita API |


:::note
以下の理由により、GAS（Google Apps Script）で開発しました。

- 手軽
- 経験がある
- 無料
- 定期タスクの実行が容易
- Gmailで送信するのが容易
:::
<details><summary>GASとは？という方はこちら</summary>

[【0からGASを学ぶ】ところでGASって何？](https://qiita.com/gas-suke/items/5cf7bd4c6c3f15a25838#:~:text=%E6%9C%AC%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%E3%81%A7%E3%81%AF%E5%BD%93%E7%84%B6%E3%81%AE,%E3%81%99%E3%82%8B%E7%B5%B1%E5%90%88%E9%96%8B%E7%99%BA%E3%83%84%E3%83%BC%E3%83%AB%E3%81%A7%E3%81%99%E3%80%82)

</details>

# かんたんな解説

コード一覧はこちら

※GAS上でないと動作しません

https://github.com/Kohki-Takatama/qiita_weekly_report/tree/main

## 1. Qiita APIを使用し、週間の記事一覧を取得する
:::note
Qiita APIの概要は以下です。
- `https://qiita.com/api/v2/users/${ユーザーネーム}/items`にて取得可能
- JSON形式
- tokenがあると、view数も確認することができる

コード上では、tokenがない場合も動作するようにしています。

tokenはsecretに保存しています。
<details><summary>該当コード</summary>

```js
function get_weekly_qiita_posts(username, qiita_api_token) {
  const api_url = `https://qiita.com/api/v2/users/${username}/items`;
  const week_ago = new Date();
  week_ago.setDate(week_ago.getDate() - 7);

  const options = {
    method: 'get',
    muteHttpExceptions: true,
    headers: {}
  };

  // If the qiita_api_token is provided, add the Authorization header
  if (qiita_api_token) {
    options.headers['Authorization'] = `Bearer ${qiita_api_token}`;
  }

  const response = UrlFetchApp.fetch(api_url, options);
  const articles = JSON.parse(response.getContentText());

  const weekly_articles = articles.filter(article => {
    const created_at = new Date(article.created_at);
    return created_at >= week_ago;
  });

  const formatted_articles = weekly_articles.map(article => {
    return {
      title: article.title,
      date: new Date(article.created_at).toLocaleDateString(),
      likes: article.likes_count,
      stocks: article.stocks_count,
      comments: article.comments_count || 0,
      views: article.page_views_count,
      url: article.url
    };
  });

  return formatted_articles;
}
```
</details>
:::

## 2. メール送信用HTMLを作成する

:::note
ポイントは以下です。
- GASではCSSファイルの作成ができないため、styleタグで規定する
- `<? script ?>` / `<?= output ?>`を使用し、スクリプトを埋め込む
（`erb`のようなイメージ）


<details><summary>CSS部分</summary>

```html
  <head>
    <style>
      body {
        font-family: 'Arial', sans-serif;
        color: #333;
        line-height: 1.6;
      }
      .header {
        font-size: 18px;
        color: #444;
        margin-bottom: 20px;
      }
      .article {
        margin-bottom: 15px;
        padding: 10px;
        border: 1px solid #ddd;
      }
      .article-title {
        font-size: 18px;
        color: #007BFF;
      }
      .article-title a {
        text-decoration: none;
        color: #007BFF;
      }
      .article-meta {
        font-size: 14px;
        color: #555;
      }
      .summary {
        margin-top: 20px;
        font-size: 16px;
      }
      .summary-item {
        font-size: 14px;
        color: #666;
      }
      .footer {
        margin-top: 30px;
        font-size: 12px;
        color: #999;
      }
      a:hover {
        opacity: 0.8
      }
    </style>
  </head>
  ````
</details>

<details><summary>HTML部分</summary>

```html
  <body>
    <h2><?= username ?> さん、今週の振り返りをお送りします</h2>
    <div class="header">今週（<?= startDate ?> 〜 <?= endDate ?>）に投稿された記事の一覧です</div><br>

    <? for (var i = 0; i < articles.length; i++) { ?>
    <div class="article">
      <div class="article-title">
        <a href="<?= articles[i].url ?>" target="_blank"><?= articles[i].title ?></a>
      </div>
      <div class="article-meta">
        <p>投稿日: <?= articles[i].date ?></p>
        いいね数: <?= articles[i].likes ?> | ストック数: <?= articles[i].stocks ?> | コメント数: <?= articles[i].comments ?> | ビュー数: <?= articles[i].views ?>
      </div>
    </div>
    <br>
    <? } ?>

    <div class="summary">
      <div class="summary-item">総合いいね数: <?= totalLikes ?></div>
      <div class="summary-item">総合ストック数: <?= totalStocks ?></div>
      <div class="summary-item">総合コメント数: <?= totalComments ?></div>
      <div class="summary-item">総合ビュー数：<?= totalViews ?></div>
    </div>

    <div class="footer">
      これからも、素晴らしい記事の投稿をお待ちしています！<br>
      Qiita Weekly Report Bot
    </div>
  </body>
```
</details>
:::

## 3. HTMLを呼び出し、演算を行う関数を作成する
:::note
MVCモデルのControllerに当たるものを作成します。
先程のHTMLに全て書いても動くのですが、分けることで可読性が上がります。

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

```js
function create_report(username, articles) {
  const totalLikes = articles.reduce((sum, article) => sum + article.likes, 0);
  const totalStocks = articles.reduce((sum, article) => sum + article.stocks, 0);
  const totalComments = articles.reduce((sum, article) => sum + article.comments, 0);
  const totalViews = articles.reduce((sum, article) => sum + article.views, 0);

  const startDate = new Date();
  startDate.setDate(startDate.getDate() - 7);
  const endDate = new Date();

  const template = HtmlService.createTemplateFromFile('report_template');
  
  template.username = username;
  template.startDate = startDate.toLocaleDateString();
  template.endDate = endDate.toLocaleDateString();
  template.articles = articles;
  template.totalLikes = totalLikes;
  template.totalStocks = totalStocks;
  template.totalComments = totalComments;
  template.totalViews = totalViews;

  const reportHtml = template.evaluate().getContent();
  return reportHtml;
}
```
</details>
:::

## 4. メール送信関数を作成する
:::note
- `MailApp.sendEmail`でメールを送信できます
- htmlBodyとして指定することで、HTMLとして解釈されます
<details><summary>該当コード</summary>

```js
function send_mail(email, subject, htmlBody) {
  // メール送信部分
  try {
    MailApp.sendEmail({
      to: email,
      subject: subject,
      htmlBody: htmlBody
      });

    console.log("Success:",`sent mail to ${email}`)

  } catch (error) {
    console.log("Error:", error)
  }
}
```
</details>
:::

# 5. トリガーを設定し毎週実行する
:::note
この1画面で完結します。お手軽です。

![スクリーンショット 2024-09-03 20.09.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/5ef9294a-6994-9c5e-5e45-af0e0606f582.png)
:::

# おわりに
以上、Qiitaの週間レポートアプリについてでした。

サービス化するには非公式である点と、tokenの登録をしてもらう点がネックですね。

次はZennやnoteなどでも作りたいですね。


# 参考

https://qiita.com/api/v2/docs#%E6%8A%95%E7%A8%BF

https://qiita.com/zumi0/items/85ca400d57f60728a7c7

https://developers.google.com/apps-script/reference/mail/mail-app?hl=ja