7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-09-03

はじめに

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

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

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

完成品

スクリーンショット 2024-09-03 19.41.55.png

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

  • 投稿した記事一覧の表示
  • 記事ごとにいいね・ストック数を表示
  • コメント・ビュー数の表示機能

きっかけ

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

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

環境

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

以下の理由により、GAS(Google Apps Script)で開発しました。

  • 手軽
  • 経験がある
  • 無料
  • 定期タスクの実行が容易
  • Gmailで送信するのが容易
GASとは?という方はこちら

【0からGASを学ぶ】ところでGASって何?

かんたんな解説

コード一覧はこちら

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

1. Qiita APIを使用し、週間の記事一覧を取得する

Qiita APIの概要は以下です。

  • https://qiita.com/api/v2/users/${ユーザーネーム}/itemsにて取得可能
  • JSON形式
  • tokenがあると、view数も確認することができる

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

tokenはsecretに保存しています。

該当コード
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;
}

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

ポイントは以下です。

  • GASではCSSファイルの作成ができないため、styleタグで規定する
  • <? script ?> / <?= output ?>を使用し、スクリプトを埋め込む
    erbのようなイメージ)
CSS部分
  <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>
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>

3. HTMLを呼び出し、演算を行う関数を作成する

MVCモデルのControllerに当たるものを作成します。
先程のHTMLに全て書いても動くのですが、分けることで可読性が上がります。

該当コード
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;
}

4. メール送信関数を作成する

  • MailApp.sendEmailでメールを送信できます
  • htmlBodyとして指定することで、HTMLとして解釈されます
該当コード
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)
  }
}

5. トリガーを設定し毎週実行する

この1画面で完結します。お手軽です。

スクリーンショット 2024-09-03 20.09.52.png

おわりに

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

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

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

参考

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?