2025年6月
1234567
891011121314
15161718192021
22232425262728
2930  

検索・リンク

GASを使ってLINEボットを作る

高専の KOSEN ハッカソン LINE にて、学生さんがいろいろと作品を作る間、自分でも LINE メッセージAPIで遊んでみた。

LINEボットのオーム返しの例

参考にしたサイトのプログラムを若干修正してある。

// line developersに書いてあるChannel Access Token
var access_token = "xxxxxx";

// pushしたいときに送る先のuser_id or group_idを指定する
var admin = "Uxxxx";

// postされたログを残すスプレッドシートのid
var spreadsheet_id = "xxxx";

// スプレッドシートの"user-db"シートにユーザIDと名前の対応表を保存
//   通常のサーバ処理なら、データベースに保存なんだろうけど、
//   すべてをGoogle Driveで完結させたいので、Google Spredsheed を
//   データベースのように使う。
var user_db_size = 10 ;

function get_user_name(source) {
  // スプレッドシートのuser-dbシートに、IDと名前の一覧を記録
  //  Uxxxxxxxx なまえ
  var array = SpreadsheetApp.openById(spreadsheet_id)
                .getSheetByName('user-db')
                .getRange(1,1,2,user_db_size).getValues() ;

  for( var i = 0 ; i < array.length ; i++ ) {
    if ( array[i][0] == "" ) // 無記入欄で終了
      return source.userId ;
    else if ( array[i][0] == source.userId )
      return array[i][1] ;   // 対応するものを見つけた
  }
  return source.userId ;
}

/**
 * 指定のuser_idにpushをする
 */
function push(text,to) {
  var url = "https://api.line.me/v2/bot/message/push";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };
 
  var postData = {
    "to" : to,
    "messages" : [
      {
        'type':'text',
        'text':text,
      }
    ]
  };
 
  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };
 
  return UrlFetchApp.fetch(url, options);
}
/**
 * 管理者宛にメッセージを送る
 */
function push_admin(text){
  return push(text,admin);
}
 
/**
 * reply_tokenを使ってreplyする
 */
function reply(event,text) {
  var url = "https://api.line.me/v2/bot/message/reply";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };

  var postData = {
    "replyToken" : event.replyToken,
    "messages" : [
      {
        'type':'text',
        'text':text ,
      }
    ]
  };
 
  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };
 
  return UrlFetchApp.fetch(url, options);
}

function do_reply(event) {
  // リプライの処理
  var text = '' ;
  switch( event.message.type ) {
    case 'text' : // メッセージ
      text = event.message.text ;
      break ;
    case 'sticker' : // ステッカー
      text = 'すてっかーID=' + event.message.id ;
      break ;
    case 'image' : // 画像
    default :
      text = 'event.message.type=' + event.message.type ;      
      break ;
  }
  return reply(event,'りぷらい:'+text);
}

/**
 * push api で beacon 受信時の処理
 */
function do_beacon(event) {
  var text = '' ;

  var user = get_user_name( event.source );

  var bcn = event.beacon ;
  switch( bcn.type ) {
    case 'enter' :  // ビーコン領域に入った
      text += 'ENTER' + bcn.hwid ;
      break ;
    case 'leave' :  // ビーコン領域を抜けた(実験したけど通知は実質来ない!?)
    case 'banner' : // ビーコンバナーをタップした
    default :
      text += bcn.type + '=' + bcn.hwid ;
      break ;
  }
  
  return push_admin( 'びーこん=' + text + '(' + user + ')' , admin ) ;
}

/**
 * postされたときの処理
 */
function doPost(e) {
  var json = JSON.parse(e.postData.contents);
  
  // 利用者のuseridを取得するためのlog
  var data = SpreadsheetApp.openById(spreadsheet_id)
               .getSheetByName('log')
               .getRange(1, 1).setValue(json.events);

  // 1回のPostで、メッセージと画像が複数のイベントで来る場合がある
  for( var i = 0 ; i < json.events.length ; i++ ) {
    var event = json.events[i] ;
    switch( event.type ) {
      case 'message' : // Message API の処理
        do_reply(event);
        break ;
      case 'beacon' : // Beacon API の処理
        do_beacon(event);
        break ;
      default: // デバッグのため
        push_admin( 'それ以外=' + event.type ) ;
        break ;
    }
  }
}
 
/**
 * pushをしてみる
 */
function test() {
  push_admin('test()の実行');
}

/**
 * getされたときの処理
 * URL?message=テキスト
 */
function doGet(e) {
  // var data = SpreadsheetApp.openById(spreadsheet_id)
  //              .getSheetByName('log')
  //              .getRange(2, 1).setValue(e.parameter.message);

  // https://script.google.com/macros/s/..../exec?message=メッセージ
  if ( "message" in e.parameter )
    push_admin(e.parameter.message);
}

ふくいソフトコンペ2018にて大賞🎉

ふくいソフトコンペ2018にて大賞

ふくいソフトコンペ2018に5EIから応募していた、水上くん,森川くん,山本くん(指導 村田先生)のチームが、大賞を受賞しました。

LINEハッカソン2018

2018/12/15,16の両日に、LINE本社で開催された”KOSENハッカソン feat.LINE”に4名の学生が参加しました。

{CAPTION}

LINEの様々なサービスを使ったものづくり

最初に、LINE Message API や、LINE クローバ , LINE LIFF , LINE Beacon などの説明を受け、これらをできたら利用して、自分たちで考えたサービスを作ろう…と頑張りました。

{CAPTION}

参加した学生さんは、他の高専からの参加者と5〜6人のグループを作り、それぞれ開発を行いました。

LINE bot や LINE Clova を使い、お小遣いの管理を目指した作品や、レポート作成支援を目指した作品英単語を覚えるサポートをする作品、さらにLINEビーコンを用いた商品販売支援の作品などを作りました。開発が2日間という短い期間であったため、基本的な機能に留まったチームもありましたが、新しい技術に触れるいい機会になったようです。

{CAPTION}

{CAPTION}

さすがLINE本社

{CAPTION}

{CAPTION}

ドメイン名とDNS

ドメイン名とDNS

インターネットでの通信では、IPプロトコルでコンピュータを指定するが、IPアドレスは無機質で覚えるのが大変であり、コンピュータに名前をつけて利用する。この際に、コンピュータの所属などが分かるようにしたものをドメイン名と呼ぶ。

例えば、電子情報工学科のドメイン名 www.ei.fukui-nct.ac.jp は、ピリオド部分で区切られ、以下のような意味を持つ。

  • .jp – 国ドメイン(.uk イギリス,.ch 中国,アメリカは無し)
  • .ac – 種別ドメイン(.co.jp,.com:会社,.ne.jp,net:ネットワーク系)
  • fukui-nct – 組織ドメイン
  • .ei. – サブドメイン(組織内が細分化されている場合)
  • www. – ホスト名

ただしアメリカでは、国ドメインを一般的に使わない。また最近では、世界的な企業では国ドメインが意味をなさないので、アメリカ以外でも .com や .net といった、トップレベルドメインが使われる。様々なサービスを展開している企業では、組織種別が意味をなさないため、toyota.jp といったものも増えてきた。

以下に、主要な組織ドメイン・国ドメインをあげる。

DNSのしくみ

DNSは、Domain Name Service であり、コンピュータ名(ドメイン名)から、IPアドレスを調べるサービスで、ポート番号53,UDPを使っている。

インターネットに接続する際には、最も身近なDNSの情報が与えられ、ユーザがコンピュータ名を問い合わせると、身近なDNSがコンピュータのIPアドレスを返してくれる。この際に、検索結果はキャッシュとして一定期間保存される。身近なDNSがそのコンピュータ名を知らない場合は、上位のDNSに問い合わせを行い、DNSルートサーバもコンピュータ名をキャッシュしていない場合は、管理元の組織のDNSに問い合わせが行われる。


DNSと様々な情報

DNS では、様々な情報が取得できる。IPアドレス以外にも、メールを送ってもらうサーバのIPアドレス(MXレコード)なども取得できる。

DNSとセキュリティ

DNSは、コンピュータ名とIPアドレスを対応付けるものであり、これには正引き(コンピュータ名からIPアドレスを求める)と、逆引き(IPアドレスからコンピュータ名を求める)がある。セキュリティ対策が厳しい場所では、

  • 正引きを使うことで、特定の組織のドメイン名を持つコンピュータからのアクセスを許可/禁止する。
  • 正引きで、コンピュータ名が登録されている所からのみ許可する。
  • IPアドレスから逆引きして求めたコンピュータ名をさらに正引きして同じIPアドレスが求まるかを確認

といった対策を行う。

  • DNSのドメイン名は、当初は最初に申請した人に割り当てられる。このため、nintendo.com といったドメイン名を、関係ない人が取得するといったトラブルがあった。(サイバースクワッティング)
  • DNSを用いたクラッキングでは、ウィルスに感染させたパソコンに偽物のIPアドレスを教えることで、偽装した別コンピュータに誘導し個人情報を盗む手口がある。(DNSポイズニング)
  • 他にもウィルスに感染させた大量のパソコンから、同時にルートサーバに大量のDNSの問合せを送ることで、処理能力を低下させると、インターネット全体でDNS参照ができなくなる攻撃もある。(DNSルートサーバへの分散DoSアタック)

香港のSTEM事情

APIEMSの発表会場近くのショッピングモールで、子供向けのパソコン教材のお店。STEM教材のロボットや、交換パーツまで売ってて驚き。
日本なら大きい電気屋の一角なんだろうけど。
{CAPTION}

{CAPTION}

{CAPTION}

APIEMS 2018 in Hong Kong

12/5-12/8まで香港にて開催されているAPIEMS 2018 にて、12/6に斉藤、12/7に、5EI 小林さん、PS2 前田くん、西先生が発表しました。12/8にPS1 田中さん、PS2 山田くんが、発表し無事に終えることができました。

{CAPTION}

12/6 初日

{CAPTION}

12/7 2日目・午前

{CAPTION}

{CAPTION}

{CAPTION}

12/7 2日目・午後

{CAPTION}

{CAPTION}

12/8 最終日

{CAPTION}

{CAPTION}

情報構造論・後期中間テスト結果の講評

情報構造論のテストが終わり、採点中。今回は、各ページごとに採点中。

  1. 設問1のイメージ図は、ほぼ全員が正解。簡単過ぎたか。
    設問2でprintf(“\n(%d)\n”,i) で、未だに¥と\が同じことを知らないのか!?!?
  2. 設問1の正規表現は、それなりに不正解が多い。
    設問2は、インタプリタ,コンパイラの欠点を述べるとき、「時間がかかる」という表現では「1命令毎に時間がかかる」のか「ソースコードから機械語が生成されてプログラムが動きだすまでの時間がかかる」(ビルド時間)のか不明確なものは、✕とする。
    「コンパイラはエラーが見つけにくい」との回答があったけど、インタプリタは未実行の命令部にバグが残る可能性があるので、どんなエラーのことなのか明記してなければ✕とする。
  3. 採点前
  4. 採点前
  5. 採点前
  6. 採点前

icloud.com メールが届かない

qmail の DNS 512byte パケット問題のパッチをあてたものを使っているつもりだが、また緊急連絡システムのメールQueueが icloud.com 宛のメールで埋まっていた。

そこで、参考記事(icloud.comやme.comやhotmailにメールが届かない時)を参考に以下の設定を行った。

mail.add_x_header

(( /etc/php/7.0/apache/php.ini ))
mail.add_x_header = Off

smtproutes

(( /var/qmail/control/smtproutes ))
icloud.com:     mx1.mail.icloud.com
me.com:         mx1.mail.icloud.com
nifty.com:      smmx.nifty.com
hotmail.com:    mx1.hotmail.com
:

((2018/11/30))
icloud.com の情報を smtproutes に書き込んで、メールが流れるようになったけど、改めて確認したら、me.com 宛が詰まってた。”me.com:mx1.mail.icloud.com”を追加。

B木とデータベース

2分探索木の考え方を拡張したもので、B木がある。

B木の構造

2分木では、データの増減で木の組換えの発生頻度が高い。そこで、1つのノード内に複数のデータを一定数覚える方法をとる。B木では、位数=Nに対し、最大2N個のデータd0..d2N-1と、2N+1本のポインタp0..p2Nから構成される。piの先には、di-1<x<di を満たすデータが入った B木のノードを配置する。ただし、データの充填率を下げないようにするため、データは最小でもN個、最大で2N個を保存する。

B木からデータの検索

データを探す場合は、ノード内のデータ diの中から探し、見つからない場合は、ポインタの先のデータを探す。位数がある程度大きい場合、ノード内の検索は2分探索法が使用できる。また、1つのノード内の検索が終われば、探索するデータ件数は、1/N〜1/2Nとなることから、指数的に対象件数が減っていく。よって、検索時間のオーダは、O(logN) となる。

B木へのデータの追加

B木にデータを追加する場合は、ノード内に空きがあれば、単純にデータの追加を行う。ノード内のデータが2N個を越える場合は、以下のような処理を行う。

ノード内のデータと追加データを並べ、その中央値を選ぶ。この中央値より大きいデータは、新たにつくられたノードに移す。中央値のデータは上のノードに追加処理を行う。このような方法を取ることで、2分木のような木の偏りが作られにくい構造となるようにする。

データを削除する場合も同様に、データ件数がN個を下回る場合は、隣接するノードからデータを取ってくることで、N個を下回らないようにする。

B木とデータベース

このB木の構造は、一般的にデータベースのデータを保存するために広く利用されている。

データベースシステムでは、データを効率よく保存するだけでなく、データの一貫性が保たれるように作られている。
例えば、データベースのシステムが途中でクラッシュした場合でも、データ更新履歴の情報を元にデータを元に戻し、データを再投入して復旧できなければならない。データを複数の所からアクセスした場合に、その順序から変な値にならないように、排他制御も行ってくれる。

データベースで最も使われているシステムは、データすべてを表形式で扱うリレーショナル・データベースである。

((リレーショナル・データベースの例))
STUDENT                             RESULT
ID   | name     | grade | course    ID   | subject | point
-----+----------+-------+--------   -----+---------+-------
1001 | t-saitoh |  5    | EI        1001 | math    | 83
1002 | sakamoto |  4    | E         1001 | english | 65
1003 | aoyama   |  4    | EI        1002 | english | 90

((SQLの例))
select STUDENT.name , RESULT.subject , RESULT.point --射影--
   from STUDENT , RESULT                            --結合--
   where STUDENT.ID == RESULT.ID    -- 串刺し --     --選択--
         and RESULT.point >= 60 ;

((上記SQLをC言語で書いた場合))
for( st = 0 ; st < 3 ; st++ )                   // 結合
   for( re = 0 ; re < 3 ; re++ )
      if ( student[ st ].ID == result[ re ].ID  // 選択
        && result[ re ].point >= 60 )
           printf( "%s %s %d" ,                 // 射影
                   student[ st ].name ,
                   result[ re ].subject ,
                   result[ re ].point ) ;

B+木

データベースの処理では、目的のデータを O(log N) で見つける以外にも、全データに対する処理も重要である。この場合、全てのデータに対する処理では、単純なB木では再帰呼び出しが必要となる。しかし、他の表でも再帰処理を伴うと、プログラムは複雑になってしまう。

そこで、B木のデータを横方向に並べて処理を行う場合に、その処理が簡単になるように B+木が用いられる。
この方法では、末端のノードは、隣接するノードへのポインタを持つ。

Wikipedia B+木 より引用

JavaScriptの再帰の例題

再帰呼び出しで分割統治法の考え方のプログラムをJavaScriptで書いたサンプル

単純に、配列の指定範囲の合計を求める関数を、再帰を用いて記述する。

<html>
<head>
</head>
<body>
 <p>
  このプログラムは、実行結果を console.log() で出力するので、
  Ctrl+Shift+I で、JavaScript の console を表示させてね。
 </p>
 <script type="text/javascript">
   // a : 配列
   // start : 合計を計算する範囲の戦闘
   // end : 合計を計算する範囲の最後+1
   // 配列の合計は、配列の先頭 + 残りの合計
   function array_sum( a , start , end ) {
     console.log( "array_sum:" + start + "," + end ) ;
     if ( start == end ) {
       console.log( "array_sum(" + start + "," + end + ")=0" ) ;
       return 0 ;
     } else {
       var ans = a[ start ] + array_sum( a , start + 1 , end ) ;
       console.log( "array_sum("+start+","+end+")="+ans ) ;
       return ans ;
     }
   }
   var array = new Array( 11 , 22 , 33 , 44 , 55 , 66 , 77 , 88 ) ;
   console.log( array_sum( array , 0 , array.length ) ) ;

   // 配列の合計は、データが1個ならその値。
   // そうでなければ、配列の前半の合計 + 配列の後半の合計
   function array_sum2( a , start , end ) {
     console.log( "array_sum2:" + start + "," + end ) ;
     if ( start + 1 == end ) {
       var ans = a[ start ] ;
       console.log( "array_sum2("+start+","+end+")="+ans ) ;
       return ans ;
     } else {
       var mid = Math.floor( ( start + end ) / 2 ) ;
       var ans = array_sum2( a , start , mid ) // 配列前半の合計を求める
               + array_sum2( a , mid , end ) ; // 配列後半の合計を求める
       console.log( "array_sum2("+start+","+end+")="+ans ) ;
       return ans ;
     }
   }
   console.log( array_sum2( array , 0 , array.length ) ) ;
  </script>
</body>
</html>

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー