トップへ

ふくいi-ame

福井県河川・砂防総合情報システム(PCサイトhttp://ame.pref.fukui.jp/および携帯サイトhttp://i-ame.ame.pref.fukui.lg.jp/)の観測データ等がオープンデータであることをいいことに, i-ameメール(福井県河川・砂防総合情報メール)の内容をつぶやく非公式アカウント

ふくいi-ameが利用しているデータについて

ふくいi-ameは, 福井県河川・砂防総合情報システムデータ利用規約に基づき, 福井県河川・砂防総合情報システムから配信されるi-ameメールの内容を, TwitterアカウントとFacebookアカウントで配信しています. (利用規約は, 福井県ホームページ「福井県河川・砂防総合情報システムの観測情報はテレビ放送や報道機関等のホームページでもご覧になれます」に掲載されています.)

ふくいi-ameは以下の著作物を改変して利用しています.
福井県河川・砂防総合情報システム, 福井県・国土交通省近畿地方整備局・福井地方気象台, クリエイティブ・コモンズ・ライセンス表示 2.1 (https://creativecommons.org/licenses/by/2.1/jp/)

ふくいi-ameがi-ameメールの内容をつぶやく仕組み

ふくいi-ameは, i-ameメールをGMailのメールアカウントで受信し, それを5分おきにGoogleAppsScriptで処理し, Twitter APIでTwitterアカウントに, Facebook Graph APIでFacebookアカウントに投稿しています.
ふくいi-ameの仕組み

ふくいi-ameのソースコード(抜粋)

ふくいi-ameのソースコードのうち, GMailメールアカウントで受信したメールの処理, Twitter APIの呼び出し, Facebook Graph APIの呼び出しの部分について掲載します.

GMailメールアカウントで受信したi-ameメールの処理はtweetMail関数で行っています. このtweetMail関数が5分おきに呼び出されます.

NIKUSHI_LOGGERの名前空間が付けられた関数は「Google Apps Script カスタムLoggerでログをspreadsheetに書き出す · Nikushi's blog(http://orihubon.com/blog/2014/06/18/log-to-sheet-by-custom-logger-google-apps-script/(2019年時点のURL))」で紹介されているコードをそのまま利用しています.

var GMAIL_SEARCH_STRING      =  "in:inbox is:unread";

function tweetMail(){

  try {
    
    var threads = GmailApp.search(GMAIL_SEARCH_STRING, 0, 20);
    
    for(var j=0;j<threads.length;j++){
      var msgs = threads[j].getMessages();
    
      for(var i=0;i<msgs.length;i++){
        if(msgs[i].isUnread()){
          var mailad = msgs[i].getFrom();
          mailad = mailad.slice(mailad.indexOf("<")+1,mailad.lastIndexOf(">"));
          if(mailad == "i-ame@ame.pref.fukui.lg.jp"){
            // Twitterの処理
            var msgbody = msgs[i].getBody();
            msgbody = msgbody.replace(/<br \/>/g,"");
            msgbody = msgbody.replace(/こちらは、福井県庁です。/,"");
            msgbody = msgbody.replace(/&nbsp;/g,"");
            msgbody = msgbody.replace(/&#x3000;/g,"");
            msgbody = msgbody.replace(/---[\w\W]+$/,"");
            msgbody = msgbody.replace(/=/g,"-");
    
            if(msgs[i].getSubject() == "水位観測情報"){
              msgbody = msgbody.slice(0,140);
              NIKUSHI_LOGGER.debug(msgbody);
              sendTweet(msgbody);
            }else if(msgs[i].getSubject() == "雨量観測情報"){
              msgbody = msgbody.replace(/※[\w\W]+$/,"");
              msgbody = msgbody.slice(0,140);
              NIKUSHI_LOGGER.debug(msgbody);
              sendTweet(msgbody);
            }else{
              var l;
              if(msgs[i].getSubject() == "土砂災害警戒情報"){
                l = msgbody.indexOf("お知らせします。")+10;
              }else{
                msgbody = msgbody.replace(/\n+新たに発表または解除された市町のみ表示しています。/,"");
                var strmatch;
                strmatch = msgbody.match(/^[\w\W]+報[^かに]/);
                if(strmatch){
                  l = strmatch[0].length+1;
                }else{
                  l = 0;
                }
              }
              if(l >= 100){
                // headerとなる部分が100文字以上となる場合, headerは無し
                l = 0;
              }
              var header = msgbody.slice(0,l);
            
              var l_bodys = 0;
              do{
                // 140文字づつ(改行の位置で区切る)
                var body = msgbody.slice(l,l_bodys+140);
                var l_body = body.lastIndexOf("\n")+1;
                if(l_body == 0) l_body=body.length;
                body = body.slice(0,l_body);
                NIKUSHI_LOGGER.debug(header+body);
                sendTweet(header+body);
                l = l + l_body;
                l_bodys = l_bodys + l_body;
              }while(l < msgbody.length)
            }

            // Facebookの処理
            msgbody = msgs[i].getBody();
            msgbody = msgbody.replace(/<br \/>/g,"");
            msgbody = msgbody.replace(/こちらは、福井県庁です。/,"");
            msgbody = msgbody.replace(/&nbsp;/g,"");
            msgbody = msgbody.replace(/&#x3000;/g,"");
            msgbody = msgbody.replace(/登録変更解除[\w\W]+$/,"");
            NIKUSHI_LOGGER.debug(msgbody);
            sendFBFeed(FB_ID,msgbody);
          
          }
          msgs[i].markRead();
        }
      }
    }
  } 
  catch(e) {
    NIKUSHI_LOGGER.error(e.toString());
  }
}

Twitter API v2の呼び出しはsendTweet関数で行っています. もともとは、このsendTweet関数は「Google Apps ScriptでOAuthConfigのサポートが終了してTwitter botが危険そうだったので変更 - きじとら(https://kijtra.com/article/twitter-api-for-google-apps-script-without-oauthconfig/)」で紹介されているコードをそのまま利用していました. しかしながら, 2023年5月にTwitter API v1.1のサポートが終了したことから, 現在は「GASからTwitter API v2でツイートする【Google Apps Script】 | prtn-blog(https://prtn-life.com/blog/gas-twitter-api#toc3)」で紹介されているsendTweet関数の最初の部分を下記のとおり修正して利用しています.

function sendTweet(text) {
  var payload = {
    text: text
  }

Facebook Graph APIの呼び出しはsendFBFeed関数で行っています. なお、ソースコード中のアクセストークンやページIDは, ここでは実際の値ではなく****...*と記載しています.

var PAGE_ACCESS_TOKEN = '****...*';

var FB_ID = '****...*';

var FB_BASE_URL = 'https://graph.facebook.com/v2.6/' + FB_ID + '/feed?access_token=' + PAGE_ACCESS_TOKEN;

function sendFBFeed(id, text) {
  var url = FB_BASE_URL;
  var payload = {
    'recipient': id, 
    'message': text
  };
  payload = Object.keys(payload).map(function(key) {
    return encodeRfc3986(key) + '=' + encodeRfc3986(payload[key]);
  }).join('&');
  var options = {
    'method': 'post', 
    "headers": {
      "Content-type": "application/json"
    },
    'payload': payload, 
    muteHttpExceptions: true
  };
  var result = UrlFetchApp.fetch(url, options);
  var json = JSON.parse(result.getContentText());
  if (json) {
    if (json.error) {
      NIKUSHI_LOGGER.error(json.error);
    }
  }
  return result;
}

function encodeRfc3986(str) {
  return encodeURIComponent(str).replace(/[!'()]/g, function(char) {
    return escape(char);
  }).replace(/\*/g, "%2A");
}

ふくいi-ameを作るのに参考したWebサイト

更新履歴