Created
April 7, 2016 18:31
-
-
Save yappo/fc25aad3bdfe54204300db918fd41eb5 to your computer and use it in GitHub Desktop.
ikachan for LINE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use strict; | |
use warnings; | |
use DBI; | |
use Digest::SHA 'hmac_sha256_base64'; | |
use Encode; | |
use Furl; | |
use JSON::PP; | |
use Plack::Request; | |
use String::Random; | |
my $dbh = DBI->connect('dbi:SQLite:dbname=lnyappo.db', '', ''); | |
my $string_gen = String::Random->new; | |
my $furl = Furl->new( agent => 'LiNe Yappo/1.00' ); | |
my $registration_secret = $ENV{LNYAPPO_REGISTRATION_SECRET}; # この LINE Bot は、ここで設定した文字列を受け付けると、ユーザの登録を行います | |
my $channel_id = $ENV{LINE_CHANNEL_ID}; | |
my $channel_secret = $ENV{LINE_CHANNEL_SECRET}; | |
my $bot_mid = $ENV{LINE_BOT_MID}; | |
my $line_bot_api_endpoint = 'https://trialbot-api.line.me/v1/events'; | |
sub { | |
my $env = shift; | |
my $req = Plack::Request->new($env); | |
my $res = $req->new_response(200); | |
if ($req->method eq 'POST') { | |
if ($req->path eq '/linebot/callback') { | |
if (signature_validation($req)) { | |
callback($req); | |
} | |
} elsif ($req->path eq '/send') { | |
do_send($req, $res); | |
} | |
} elsif ($req->method eq 'GET') { | |
if ($req->path eq '/') { | |
do_index($req, $res); | |
} | |
} | |
$res->finalize; | |
}; | |
sub do_index { | |
my($req, $res) = @_; | |
$res->body(<<HTML); | |
<html> | |
<head><title>ln.yappo</title></head> | |
<body> | |
<h1>ln.yappo</h1> | |
<h2>API Spec</h2> | |
<table border="1"> | |
<tr> | |
<td>endpoint</td> | |
<td>method://hostname:port/send</td> | |
</tr> | |
<tr> | |
<td>method</td> | |
<td>POST</td> | |
</tr> | |
<tr> | |
<td>Content-Type</td> | |
<td>application/x-www-form-urlencoded</td> | |
</tr> | |
<tr> | |
<td>params</td> | |
<td> | |
api_token: このアプリを動かしている LINE Bot と友達になり LNYAPPO_REGISTRATION_SECRET で設定した文章を LNE Bot に送信すると api_token が生成されるので、それを利用します。<br> | |
message: LINE に送信したいメッセージ | |
</td> | |
</tr> | |
<table> | |
<h2>quick send form</h2> | |
<form action="./send" method="post"> | |
api_token: <input name="api_token"><br> | |
message: <input name="message"><br> | |
<input type="submit" value="send to LINE"> | |
</form> | |
</body> | |
<html> | |
HTML | |
} | |
sub do_send { | |
my($req, $res) = @_; | |
my $api_token = $req->param('api_token'); | |
my $message = $req->param('message'); | |
unless ($api_token && $message) { | |
$res->body('{"status":500,"message":"Bad request."}'); | |
return; | |
} | |
my $activate_mid = get_activate_mid_by_api_token($api_token); | |
unless ($activate_mid) { | |
$res->body('{"status":404,"message":"api_token is not exists."}'); | |
return; | |
} | |
my $line_res = send_text($activate_mid->{mid}, decode( utf8 => $message )); | |
$res->header( 'Content-Type' => $line_res->header('Content-Type') ); | |
$res->body($line_res->content); | |
} | |
sub signature_validation { | |
my $req = shift; | |
my $json = $req->content or return; | |
my $signature = $req->header('X-LINE-ChannelSignature'); | |
$signature =~ s/=+\z//; | |
$signature eq hmac_sha256_base64($json, $channel_secret); | |
} | |
sub callback { | |
my $req = shift; | |
my $json = $req->content or return; | |
my $data = decode_json $json; | |
for my $message (@{ $data->{result} }) { | |
next unless validation_message($message); | |
if ($message->{content}{text} eq $registration_secret) { | |
registration($message, $message->{content}{text}); | |
} | |
} | |
} | |
sub validation_message { | |
my $message = shift; | |
return unless $message->{eventType} eq '138311609000106303'; | |
return unless $message->{fromChannel} eq '1341301815'; | |
return unless $message->{toChannel} eq $channel_id; | |
return unless $message->{content}{toType} == 1; | |
return unless $message->{content}{contentType} == 1; | |
my $sent_me = 0; | |
for my $mid (@{ $message->{content}{to} }) { | |
$sent_me++ if $mid eq $bot_mid; | |
} | |
return unless $sent_me; | |
return 1; | |
} | |
sub registration { | |
my($message, $used_secret) = @_; | |
my $mid = $message->{content}{from}; | |
my $activate_mid = get_activate_mid_by_mid($mid); | |
return send_text($mid, sprintf("You are registered.\nYour api_token is '%s'.", $activate_mid->{api_token})) if $activate_mid; | |
activate($mid, $used_secret); | |
} | |
sub get_activate_mid_by_mid { | |
my $mid = shift; | |
my $rows = $dbh->selectall_arrayref('SELECT * FROM activate_mid WHERE mid=?', { Slice => +{} }, $mid); | |
return $rows->[0]; | |
} | |
sub get_activate_mid_by_api_token { | |
my $api_token = shift; | |
my $rows = $dbh->selectall_arrayref('SELECT * FROM activate_mid WHERE api_token=?', { Slice => +{} }, $api_token); | |
return $rows->[0]; | |
} | |
sub activate { | |
my($mid, $used_secret) = @_; | |
for (1..5) { | |
my $api_token = $string_gen->randregex('[a-zA-Z0-9]{32}'); | |
next if get_activate_mid_by_api_token($api_token); | |
$dbh->do( | |
'INSERT INTO activate_mid (mid, used_secret, api_token, created_at) VALUES(?, ?, ?, ?)', undef, | |
$mid, $used_secret, $api_token, time() | |
); | |
send_text($mid, "Congratulation! Your api_token is '$api_token'."); | |
return; | |
} | |
send_text($mid, 'Faild to registration.'); | |
} | |
sub send_text { | |
my($mid, $text) = @_; | |
my $json = +{ | |
to => [ $mid ], | |
toChannel => 1383378250, | |
eventType => 138311608800106203, | |
content => +{ | |
contentType => 1, | |
toType => 1, | |
text => $text, | |
}, | |
}; | |
my $res = $furl->post($line_bot_api_endpoint, [ | |
'Content-Type' => 'application/json; charset=UTF-8', | |
'X-Line-ChannelID' => $channel_id, | |
'X-Line-ChannelSecret' => $channel_secret, | |
'X-Line-Trusted-User-With-ACL' => $bot_mid, | |
], encode_json($json)); | |
$res; | |
} | |
__END__ | |
Usage | |
plackup --host $IP_ADDR -p $PORT lnyappo.pl | |
Description | |
LINE BOT API Trial を利用してシンプルな Web API を利用し、このアプリで紐付けた LINE アカウントに API に送信されたメッセージを送信することができます。 | |
ikachan for LINE 的なものです。 | |
1. https://business.line.me/ で必要な情報を設定する | |
2. LNYAPPO_REGISTRATION_SECRET に、好きな文字列を指定する | |
3. LINE_* の必要な項目を LINE の開発サイトからコピペして設定する | |
4. このアプリを適当な場所で起動する | |
5. LINE の開発者サイトに、このアプリの endpoint url を設定する https://hostname:443/linebot/callback とか | |
6. 作成した Bot と友達登録をする | |
7. その Bot に向けて LNYAPPO_REGISTRATION_SECRET を送る | |
8. Bot から設定された api_token が戻ってくるので、どこかで覚える | |
9. api_token を利用して、このアプリの API を叩けば LINE に任意のメッセージを送れます | |
for SQLite | |
$ sqlite3 lnyappo.db | |
CREATE TABLE activate_mid ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
mid TEXT NOT NULL UNIQUE, | |
used_secret TEXT NOT NULL UNIQUE, | |
api_token TEXT NOT NULL UNIQUE, | |
created_at INTEGER | |
); | |
CREATE INDEX activate_mid_mid ON activate_mid(mid); | |
CREATE INDEX activate_mid_api_token ON activate_mid(api_token); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment