やったこと
- @botunicode というTwitter-botを作った(フォローしてね)。
- 3分に1回、ランダムにunicodeのいずれかのコードポイントをつぶやく。
- 元データはunicode.orgから。
- Python3 / Node.js 8.0 on AWS Lambda。
- 色々と雑で汚くてすみません。趣味プログラマ&&初投稿なんで。指摘を頂ければ改善します。
もう少し詳しく
Twitter準備
- Twitterでアカウント作成
- メアドだけでいけるかと思ったが、後述のAPI KEY取得で電話番号(SMS認証)が必要になった
- アカウント作成後、developer.twitter.comでアプリの登録をするとKEYが払い出せる。詳細は→Qiita:Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2018年9月時点の情報
AWS Lambda
- python3のライブラリを使いたいので、一旦ローカルでライブラリを揃えzipでLambdaにアップすることに。
- ローカルでの適当な場所で
pip install requests requests-oauthlib -t ./
- そのフォルダをzipで固めておく
- Lambdaで新規関数の作成。言語はPython3.6、ロールは最低限で良い。
- 先ほどのzipを上げる。3分ごとの定期実行含め、詳細は→AWS LambdaにPythonコードをzipでアップして実行する方法を詳しく
- lambdaのコードは下記。TwitterのKEYを環境変数に設定するほか、後述のAPIエンドポイントも。
import os
import json
import requests
from requests_oauthlib import OAuth1Session
CK = os.environ['CONSUMER_KEY']
CS = os.environ['CONSUMER_SECRET']
AT = os.environ['ACCESS_TOKEN']
AS = os.environ['ACCESS_TOKEN_SECRET']
URL = 'https://api.twitter.com/1.1/statuses/update.json'
def lambda_handler(event, context):
r = requests.get(os.environ['API_ENDPOINT']+"?rand")
data = r.json()
tweet = "\n".join([data['char'], "code:"+data['code'], data['name']+"("+data['block']+")", data['desc']])
session = OAuth1Session(CK, CS, AT, AS)
params = {"status": tweet }
session.post(URL, params = params)
APIの作成
- 別建てにする必要ないが、汎用性を考えてunicodeうんぬんはAPIに分離した
- 下記のLambdaを作成する(node.js8)
const u = require('./unicode.json');
exports.handler = async (event) => {
// functions
const remove0 = (x)=>{return x.replace(/^0+/,"")};
const emoji2unicode = (emoji)=> {
var comp;
if (emoji.length === 1) {
comp = emoji.charCodeAt(0);
}else{
comp = (
(emoji.charCodeAt(0) - 0xD800) * 0x400
+ (emoji.charCodeAt(1) - 0xDC00) + 0x10000
);
if (comp < 0) {
comp = emoji.charCodeAt(0);
}
}
return comp.toString("16");
};
const help = ()=>{
return "GET Parameter\n example:\n code=1f431\n or\n char=A\n or\n rand";
};
// process
var response = {
statusCode: 200,
};
if (event.queryStringParameters){
if (event.queryStringParameters.code){
var code = remove0(((event.queryStringParameters.code).toUpperCase()).replace(/[^0-9A-F]/,""));
var r = u.filter((a)=>{return remove0(a.code) == code})[0];
response.body = JSON.stringify(r);
}else if (event.queryStringParameters.char){
var code = emoji2unicode(event.queryStringParameters.char).toUpperCase();
var r = u.filter((a)=>{return remove0(a.code) == code})[0];
response.body = JSON.stringify(r);
}else if ("rand" in event.queryStringParameters){
response.body = JSON.stringify(u[Math.floor(Math.random() * u.length)]);
}else{
response.body = help();
}
}else{
response.body = help();
}
return response;
};
- 後述する方法で作る
unicode.json
ファイルをindex.js
と同じ階層に置く。 - AWS API GatewayでAPI作成、GETアクションを追加して、前述Lambdaを指定。
Lambda プロキシ統合の使用
にチェックも忘れずに。 - APIをデプロイして発行されたエンドポイントURLを最初のLambda(python)の環境変数に指定
- なお、このAPIは本稿で使うパラメータ
rand
以外にも、code=1f431
とかchar=🐱
などのパラメータも受け付けるため、これはこれで便利。
unicode.jsonを作る
- 下記の体裁のjsonを作りたい。
[
{
"name": "\u003ccontrol\u003e",
"desc": "= NULL",
"char": "",
"code": "0000",
"block": "Basic Latin"
},
{
"name": "\u003ccontrol\u003e",
"desc": "= START OF HEADING",
"char": "",
"code": "0001",
"block": "Basic Latin"
},
....
]
- 元ネタはunicode.orgからBlocks.txtとNamesList.txt
- ダウンロードして良いとこどりすればよい。bashでやりたかったのだが、NamesList.txtが微妙に複数行が混在するなど面倒だったので(awkで出来そうだが)、Powershell(Win10)でやった。
$fn_blocks = ".\Blocks.txt"
$fn_namels = ".\NamesList.txt"
$os_blocks = (select-string -Path $fn_blocks -List '^0000').linenumber - 1
$os_namels = (select-string -Path $fn_namels -List '^0000').linenumber - 1
$db = New-Object System.Collections.ArrayList;
gc -Encoding utf8 .\Blocks.txt | select -skip $os_blocks | ?{$_ -match "^[0-9A-F]{4}"} | %{
$t0 = $_ -split "; ";
$t1 = $t0[0] -split "\.\.";
[void]$db.Add(@{
"start" = $t1[0];
"end" = $t1[1];
"name" = $t0[1];
});
}
$dn = @{};
$tmp = $null;
gc -Encoding utf8 .\NamesList.txt | select -skip $os_namels | %{
if($_ -match "^[0-9A-F]"){
if($tmp -ne $null){$tmp.desc = $tmp.desc -join " "; $dn[$($tmp.code)] = $tmp}
$tmp = @{};
$t0 = $_ -split "\t"
$tmp.code = $t0[0];
$tmp.char = "";
if(-Not ($t0[1] -match "^<")){$tmp.char=[char]::ConvertFromUtf32([int]"0x$($tmp.code)")}
$blk = ($db | ?{([int]("0x"+$_.start) -le [int]("0x"+$tmp.code)) -and ([int]("0x"+$tmp.code) -le [int]("0x"+$_.end))} | select -first 1)
if($blk){
$tmp.block = $blk.name;
}
$tmp.name = $t0[1];
$tmp.desc = @();
}else{
$tmp.desc += ($_ -replace "\t","")
}
}
$dn.Keys | sort {[Convert]::ToInt64($_,16)}| %{New-Object PSObject -prop $dn[$($_)]} | ConvertTo-Json | sc -encoding utf8 unicode.json