1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LINE botで定期的にニュース記事を通知する

Last updated at Posted at 2025-08-17

定期的に英語のニュース記事を読みたいなと思っているのですが、結構そのこと自体を忘れます。
LINE botで定期的に通知すれば、忘れずに毎日続けられると思い、GASとLINE botで定期通知botを作ってみました。

準備

準備として、①LINE botを作るためのLINE developer consoleからの設定、②GASのスクリプトプロパティへの環境変数保存、③GASの定期実行トリガーの保存、などがあります。

詳しくはここで解説しませんが、Messaging APIをLINE Developper Console上で有効にし、通知先のLINEアカウントのユーザIDをDevelopper Consoleまたはテスト関数的なものを作成して通知に使うbotから直接送信先のユーザIDを取得する必要があります。

余談:別のbotで取得した自分のユーザIDを取得して使おうとしたところ、永遠に下のエラーが出て原因がわからなかった...

{"message":"Failed to send messages"}

実行コード

ユーザID取得用

こちらはbotに対して適当なメッセージを送ることで、そのメッセージを送ってきた人のuser idを返すbotです。単純にニュース記事をbotに流すだけであれば不要ですが、こちらはWebhookとしてGASのコードを全体公開し、そのURLをLINE botのWebhook URLに指定する必要があります。

index.js
// LINE developersのメッセージ送受信設定に記載のアクセストークン
const LINE_TOKEN = PropertiesService.getScriptProperties().getProperty("LINE_ACCESS_TOKEN");
const LINE_URL = 'https://api.line.me/v2/bot/message/reply';

//ユーザーがメッセージを送信した時に下記を実行する
function doPost(e) {

  const json = JSON.parse(e.postData.contents);

  //replyToken…イベントへの応答に使用するトークン(Messaging APIリファレンス)
  // https://developers.line.biz/ja/reference/messaging-api/#message-event
  const reply_token = json.events[0].replyToken;
  const userId = json.events[0].source.userId;

  // 検証で200を返すための取り組み
  if (typeof reply_token === 'underfined') {
    return;
  }
  const option = {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + LINE_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': reply_token,
      'messages': [{
        'type': 'text',
        'text':  "Your UserId: "+ userId,
      }],
    }),
  }

  UrlFetchApp.fetch(LINE_URL,option);

  return;
}

ニュース記事通知botのコード

sendMessageをGASのトリガーに設定し、定期実行します。

index.js

// 設定値
const LINE_CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("LINE_ACCESS_TOKEN");

const USER_ID = PropertiesService.getScriptProperties().getProperty("USER_ID");

// リマインドメッセージを送信
function sendMessage() {
  const option = {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    "muteHttpExceptions": true,
    "validateHttpsCertificates": false,
    "followRedirects": false,
    'payload': JSON.stringify({
      'to': USER_ID,
      'messages': fetchAndSendTechCrunchArticles()
    }),
  }
  
  try {
    const response = UrlFetchApp.fetch('https://api.line.me/v2/bot/message/push', option);
    Logger.log(`Response: ${response.getContentText()}`);
  } catch (e) {
    Logger.log('Error sending reminder:');
    Logger.log(e);
    throw e;
  }
}

/**
 * 記事を取得して送信
 */
function fetchAndSendTechCrunchArticles() {
  try {
    // TechCrunchのRSSフィードを取得(HTMLより軽量で構造化されている)
    const rssUrl = 'https://techcrunch.com/feed/';
    const response = UrlFetchApp.fetch(rssUrl);
    
    if (response.getResponseCode() !== 200) {
      return 'TechCrunchからの記事取得に失敗しました。';
      return;
    }
    
    const xmlContent = response.getContentText();
    const articles = parseRSSFeed(xmlContent);
    
    if (articles.length === 0) {
      return '記事が見つかりませんでした。'
      return;
    }
    
    // 最新の記事を処理
    const latestArticles = articles.slice(0, 5);
    let messages = [];
    
    latestArticles.forEach((article, index) => {
      // タイトルと概要を翻訳
      const translatedTitle = translateText(article.title);
      const translatedDescription = translateText(article.description);
      
      const message = `📰 記事 ${index + 1}\n\n` +
                     `🔤 原題: ${article.title}\n\n` +
                     `🇯🇵 和訳: ${translatedTitle}\n\n` +
                     `📝 概要: ${translatedDescription}\n\n` +
                     `🔗 リンク: ${article.link}\n\n` +
                     `📅 公開日: ${article.pubDate}`;
      messages.push({
        type: 'text',
        text: message
      });
    });
    
    // メッセージを送信
    return messages
    
  } catch (error) {
    Logger.log(error);
    return '記事の取得中にエラーが発生しました。'
  }
}

/**
 * RSSフィードを解析
 */
function parseRSSFeed(xmlContent) {
  const articles = [];
  
  try {
    // XMLをパース
    const xml = XmlService.parse(xmlContent);
    const root = xml.getRootElement();
    const channel = root.getChild('channel');
    const items = channel.getChildren('item');
    
    items.forEach(item => {
      const title = item.getChildText('title') || '';
      const link = item.getChildText('link') || '';
      const description = item.getChildText('description') || '';
      const pubDate = item.getChildText('pubDate') || '';
      
      // HTMLタグを除去
      const cleanDescription = description.replace(/<[^>]*>/g, '').substring(0, 200) + '...';
      
      articles.push({
        title: title.substring(0, 100), // タイトルを100文字に制限
        link: link,
        description: cleanDescription,
        pubDate: formatDate(pubDate)
      });
    });
    
  } catch (error) {
    Logger.log('Error parsing RSS feed: ' + error.toString());
  }
  
  return articles;
}

/**
 * Google Translate APIを使用してテキストを翻訳
 */
function translateText(text) {
  if (!text || text.trim() === '') return '';
  
  try {
    const japanese = LanguageApp.translate(text, 'en', 'ja');
    Logger.log(japanese);
    
    return japanese; // 翻訳失敗時は元のテキストを返す
    
  } catch (error) {
    Logger.log('Translation error: ' + error.toString());
    return text;
  }
}

/**
 * 日付をフォーマット
 */
function formatDate(dateString) {
  try {
    const date = new Date(dateString);
    return Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd HH:mm');
  } catch (error) {
    return dateString;
  }
}

/**
 * 手動でTechCrunch記事を取得するテスト関数
 */
function testFetchArticles() {
  try {
    const rssUrl = 'https://techcrunch.com/feed/';
    const response = UrlFetchApp.fetch(rssUrl);
    const xmlContent = response.getContentText();
    const articles = parseRSSFeed(xmlContent);
    
    console.log('取得した記事数:', articles.length);
    articles.slice(0, 2).forEach((article, index) => {
      console.log(`記事 ${index + 1}:`);
      console.log('タイトル:', article.title);
      console.log('翻訳タイトル:', translateText(article.title));
      console.log('---');
    });
  } catch (error) {
    console.log('テストエラー:', error.toString());
  }
}
}

ちなみに今回は適当にTech Crunchから取得していますが、他のニュースサイトのRSS URLに置き換えても使えます。他にも使えそうなニュースサイトのRSSとしては以下のURLがありそうです。

Hacker News: https://news.ycombinator.com/rss

Ars Technica: http://feeds.arstechnica.com/arstechnica/index/

The Guardian: https://www.theguardian.com/uk/rss

LWN: https://lwn.net/headlines/newrss

BBC: http://feeds.bbci.co.uk/news/video_and_audio/news_front_page...

Reuters: http://feeds.reuters.com/Reuters/worldNews

1
0
1

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?