Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
6
Help us understand the problem. What is going on with this article?
@uniqode

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

More than 1 year has passed since last update.

やったこと

  • @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
6
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
uniqode
趣味プログラマ

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
6
Help us understand the problem. What is going on with this article?