LoginSignup
7
5

More than 5 years have passed since last update.

UnicodeをひたすらつぶやくTwitter botを作ったよ(AWS Lambda)

Last updated at Posted at 2019-03-26

やったこと

  • @botunicode というTwitter-botを作った(フォローしてね)。
  • 3分に1回、ランダムにunicodeのいずれかのコードポイントをつぶやく。
  • 元データはunicode.orgから。
  • Python3 / Node.js 8.0 on AWS Lambda。
  • 色々と雑で汚くてすみません。趣味プログラマ&&初投稿なんで。指摘を頂ければ改善します。

もう少し詳しく

Twitter準備

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.txtNamesList.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
7
5
0

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
7
5