めんどくさいより速くいけ

その場にとどまるためには、全力で走り続けなければならない

ドングリ FM の 2020 年 をデータとともに振り返る

この記事は「ドングリ FM リスナーの Advent Calendar 2020」の 14 日目の記事です。

adventar.org


ドングリ FMとは

「つまらなくておもしろい、いつも聴いてるのに内容は覚えていない」

で有名な narumi さんとなつめぐさんの 2 人が話すポッドキャスト番組です。

現在(2020 年 12 月 14 日)のエピソード数は 660 回 を超えて 700 回へ向かっています。

そして今年の 1 月に

ドングリ FM の過去回を検索できるサイトドングリ FM 検索」をつくりました!

dongurifm-search.netlify.app

つくった背景

第 493 回 でドングリ FM の過去のエピソードを探すときに
ひたすら SoundCloud をスクロールしてさかのぼっている」と聞き
その時間を使ってこの楽しいポッドキャストを 1 エピソードでも配信を増やしてほしい...!
と思ったのがきっかけでした。

soundcloud.com

お二人の SoundCloud ひたすらスクロールを解決するために作ったサービスでしたが

多くのドングリ FM リスナーの方にも使ってもらえています!

第 513 回の放送と note でも紹介されました!

soundcloud.com

donguri.fm

データで振り返る 2020 年のドングリ FM

そんなドングリ FM 検索を運用しているので

世界一ドングリ FM のデータを持っていると言っても過言ではないと思い

以下、そのデータを活用して 2020 年のドングリ FM を振り返ってみようのコーナーです!

2020 年の最初に公開されたエピソードは?

494 令和2年になってしまいました

お正月の BGM & 米津玄師のレモンから始まったおめでたい回です。

soundcloud.com

2020 年に公開されたエピソード数は?

現在(2020 年 12 月 14 日)の最新が第 660 回なので

2020 年に公開されたエピソード数は 166!

ざっくり 2 日 に 1 エピソードのペースで公開されています。

2020 年で長編(時間が長かった)エピソード Top 3

年末年始に聞き返したい長編エピソード!

1 位: 592 カツセマサヒコさんをゲストに創作と青春を語る

1 時間 17 分 23 秒

soundcloud.com

2 位: 572 朽木誠一郎さんをゲストにメディアとダイエットを語る

1 時間 13 分 15 秒

soundcloud.com

3 位: 526 ラジオ番組「たのしいラジオ・ドングリ FM」

1 時間 6 分 44 秒

soundcloud.com

2020 年で短編(時間が短かかった)エピソード Top 3

まだドングリ FM を聴いたことない方にオススメな短編エピソード!

1 位: 619 Google Pixel Buds とかいう翻訳イヤホンがすごいらしい

5 分 29 秒

soundcloud.com

2 位: 646 猥談バーに行きたい

6 分 11 秒

soundcloud.com

3 位: 620 「サマータイムレンダ」とマイナースポーツ漫画が面白い

6 分 16 秒

soundcloud.com

2020 年 のドングリ FM 頻出単語 Top 5

2020 年に公開された全てのエピソードのタイトルから単語を抽出して意味がわかるもので選出しました。

1 位: 語る

2 位: ドングリ

3 位: 面白い

4 位: おすすめ, 最強, Zoom, ポッドキャスト, ラジオ

5 位: 収録, 退職, 公開

AI にタイトルを付けさせたとすると
「面白いポッドキャストを語る」
「最強の退職ブログを公開」
のようなタイトルが生まれてきそうです。

おまけ

626 ビッグデータを少しぶっ壊したいで語られていた、壮大な目標 1000 万エピソードまで

このペースだと 60606 年、平均 80 歳とすると 757 世代後に達成されます、大変なことだなあ。

Amazon Alexa の日本語エンジンが壊れるその日まで、ドングリ FM を聞き続けていきます。


この記事は「ドングリ FM リスナーの Advent Calendar 2020」の 14 日目の記事でした。

明日はもこ 🐈tomoko miura さんです。お楽しみに!

Monthly LINE API Update 2020-11

先日開催された LINE DEV DAY 2020 のセッションで
約 60 万アカウントのチャットボットが作成されLINE 上で動いていていると発表されていました。

logmi.jp

多くの開発者に使われている LINE API の情報は

LINE DevelopersLINE Developers Community にありますが

日々のアップデートに伴う新機能の解説・ユースケースなどの情報はまだまだ少ないと感じています。

そういった情報を月 1 回まとめて紹介してきます。

2020 年 11 月のアップデート

11 月は 4 件のアップデートがありました。

Social API が LINE ログインの一部になりました

2021 年 3 月 1 日に LIFF アプリおよび LINE ミニアプリのパーマネントリンクのリダイレクト設定から「置換(後方互換性モード)」が廃止されます

  • 「LIFF URL の追加情報の処理方法」の「置換(後方互換性モード)」が非推奨になった後に廃止されて「連携」のみになります
  • LIFF SDK v2.3.0 から「連結」をサポートしているため、それ以前のバージョンを使っている場合はアップデートが必要になります
  • スケジュール
    • 2021 年 1 月 11 日に 「置換(後方互換性モード)」の利用は非推奨に、2021 年 3 月 1 日に「置換(後方互換性モード)」が廃止、連携に切り替えが行われます
  • 「連結」一択になると設定不要なので LIFF の設定項目から消える?

2020 年末にビーコンイベントの leave イベントを廃止します

  • ビーコンイベントの leave の廃止が予定されており、代わりに stay の利用が推奨されています
  • スケジュール
    • 2020 年末に廃止、2021 年 1 月上旬に Webhook の送信が停止されます
      • 2020 年末 = 2020 年 12 月 31 日?

2020 年 12 月より Messaging API のレート制限が変更されます

まとめ

Messaging API レート制限の変更は制限緩和のアップデートなので LINE 公式アカウントをチャットボットとしてヘビーに使っている開発者にとっては良いアップデートですね。

以上、2020 年 11 月の LINE API アップデートでした!

Serverless の TypeScript テンプレートを使って Lambda 関数をデプロイする

Lambda をはじめサーバーレスアプリケーションの管理ができる Serverless Framework
TypeScript を使ってデプロイして動作確認まで試してみました。

TypeScript テンプレートを使うと関数を TypeScript で書けるのはもちろん、
YAML で書いていた構成設定ファイルも TypeScript で書けるのは大きなメリットです!

www.serverless.com

Node.js のバージョン確認

$ node -v
v12.18.4

Serverless をインストール

$ npm install -g serverless

serverless もしくは省略した sls でコマンドが使えるようになるので、バージョンを確認します。

$ sls --version
Framework Core: 2.8.0
Plugin: 4.1.1
SDK: 2.3.2
Components: 3.2.7

それぞれなんのバージョンなのか?

Framework Core: Serverless 本体のバージョン

Plugin: @serverless/enterprise-plugin のバージョン

Serverless のエンタープライズ版を使う時に有効にするプラグインです。
エンタープライズ版は Serverless を使ってデプロイしたアプリケーションの監視・テストなどができるサービスです。

SDK: @serverless/platform-sdk のバージョン

各プラットフォーム(AWS, GCP など)とのインターフェースとなる SDKです。
今後は廃止予定(今後はバグ修正のみ)の SDKで@serverless/platform-clientへ統合される予定です。

Components: @serverless/components のバージョン

sls --version コマンドの実装箇所はこちら

github.com

Serverless プロジェクトの作成

sls create コマンドを使ってプロジェクトを作成します。

-t にはテンプレート名を指定します。今回は AWS Lambda の TypeScript テンプレートを使うので aws-nodejs-typescriptを指定します。

-p にはプロジェクト名を指定します。

$ sls create -t aws-nodejs-typescript -p example-project

Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/path/example-project"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v2.8.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs-typescript"

プロジェクトの作成が完了したら、必要なパッケージをインストールします。

$ cd example-project

$ npm install

つづいて生成されたファイルを確認します。

$ ls | xargs basename

handler.ts
package.json
serverless.ts
tsconfig.json
webpack.config.js

serverless.ts の確認

注目箇所はproviderfunctionsの 2 点です。

providerには「利用するサービスは AWS で Node.js v12 を使う」

functions には「handler.ts の hello 関数を hello という名称の Lambda 関数として使い、API Gateway 経由で /hello に GET リクエストでアクセスできるようにする」

といった内容が書かれています。

TypeScript で書くことで必須項目の有無、型による制限などの恩恵が受けられます。

import type { Serverless } from 'serverless/aws';

const serverlessConfiguration: Serverless = {
  service: {
    name: 'example-project',
    // app and org for use with dashboard.serverless.com
    // app: your-app-name,
    // org: your-org-name,
  },
  frameworkVersion: '2',
  custom: {
    webpack: {
      webpackConfig: './webpack.config.js',
      includeModules: true,
    },
  },
  // Add the serverless-webpack plugin
  plugins: ['serverless-webpack'],
  provider: {
    name: 'aws',
    runtime: 'nodejs12.x',
    apiGateway: {
      minimumCompressionSize: 1024,
    },
    environment: {
      AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
    },
  },
  functions: {
    hello: {
      handler: 'handler.hello',
      events: [
        {
          http: {
            method: 'get',
            path: 'hello',
          },
        },
      ],
    },
  },
};

module.exports = serverlessConfiguration;

handler.ts の確認

hello 関数が生成され デフォルト では APIGatewayProxyHandler が指定されており、
API Gateway + Lambda の構成を前提としたコードが生成されます。

S3 からのイベントを受け取る関数の場合は S3Handler
DynamoDB からのイベントを受け取る関数の場合は DynamoDBStreamHandler など
関数の目的に合わせた型を指定することで event オブジェクトで型情報が使えるようになります。

import { APIGatewayProxyHandler } from 'aws-lambda';
import 'source-map-support/register';

export const hello: APIGatewayProxyHandler = async (event, _context) => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!',
        input: event,
      },
      null,
      2
    ),
  };
};

ローカル環境で動作確認

sls invoke コマンドを使って、ローカル環境で関数を実行して動作確認をします。

sls invoke local でローカル環境で関数を実行できます。

-f には実行対象の関数名を指定します。

$ sls invoke local -f hello

Serverless: Bundling with Webpack...
Time: 153ms
Built at: 2020-10-23 22:32:09
     Asset      Size   Chunks             Chunk Names
handler.js  6.29 KiB  handler  [emitted]  handler
Entrypoint handler = handler.js
[./handler.ts] 316 bytes {handler} [built]
[source-map-support/register] external "source-map-support/register" 42 bytes {handler} [built]
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!\",\n  \"input\": \"\"\n}"
}

AWS にデプロイ

sls deploy コマンドを使って関数を AWS にデプロイします。

$ sls deploy

~~~~~ 省略 ~~~~~
Serverless: Stack create finished...
Service Information
service: example-project
stage: dev
region: ap-northeast-1
stack: example-project-dev
resources: 8
api keys:
  None
endpoints:
  GET - https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
functions:
  hello: example-project-dev-hello
layers:
  None

Serverless: Stack update finished... が表示されたらデプロイ完了です!

デプロイされた関数の動作確認

sls invokelocal を付けないと AWS にデプロイされた関数が実行されます。

$ sls invoke -f hello

{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!\",\n  \"input\": {}\n}"
}

デプロイログの endpoints に表示されている URL でアクセス可能です。

$ curl -X GET https://xxxxx.execute-api.us-east-1.amazonaws.com/dev/hello

{
  "message": "Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!",
  "input": {
    ~~~~~ 省略 ~~~~~
  }
}

以上で、Serverless Framework で TypeScript テンプレートを使った Lambda 関数のデプロイは完了です!

おまけ

他に使えるテンプレートは?

Serverless Framework で使えるテンプレートこちらです。

serverless/lib/plugins/create/templates at master · serverless/serverless · GitHub

GCP や Azure のテンプレートもあります。

TypeScript の型定義はどこにあるの?

Serverless Framework の本体には型定義は入っておらず、TypeScript の型定義は @type/serverless に存在しています。

www.npmjs.com

ほとんどの型が網羅されていますが、用途として少ない型はないことも。
必要であればコントリビュートして追加することが可能です。

Nginx のアクセスログを Embulk で PostgreSQL に入れて分析する

モチベーション

  • SQL でアクセスログを集計可能にして「今週のアクセス数ランキング」的なページを作成するため
  • AWS であればマネージドサービスを使って CloudWatch -> Kinesis -> RDS でも実現可能であるが、汎用的に使える Embulk (or Fluentd) を触ってみたかったため

環境

  • Ubuntu 18.04

Nginx のログフォーマットを変更

log_format embulk '"$http_x_forwarded_for" "[$time_local]" "$request_method" "$request_uri" '
                  '$status $body_bytes_sent $request_time "$http_referer" '
                  '"$http_user_agent"';

access_log /var/log/nginx/access.log embulk;

Nginx のアクセスログを tsv 形式に変換

$ cat /var/log/nginx/access.log | nkf --url-input | perl -ne 'print join("\t", /^"(.*?)" "\[(.*?)\]" "(.*?)" "(.*?)" (.*?) (.*?) (.*?) "(.*?)" "(.*?)"/), "\n"' | grep -v '^\s*$' > access.log.csv

1. nkf --url-input

/search?q=検索 の URL にアクセスしたときにアクセスログ上には
URI エンコードされて /search?q=%E6%A4%9C%E7%B4%A2 になっていたのでデコード

2. print join("\t", /^"(.?)" "[(.?)]" "(.?)" "(.?)" (.?) (.?) (.?) "(.?)" "(.*?)"/), "\n"'

アクセスログを tsv 形式に変換

3. grep -v '^\s*\$'

2 で指定した tsv 形式に一致しないアクセスログは空白行になるので削除

Embulk の環境設定

Embulk は Java で動作するアプリケーションなので Java をインストール

$ sudo apt-get install openjdk-8-jre
$ java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-8u242-b08-0ubuntu3~18.04-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)

Embulk をインストール

$ curl --create-dirs -o ~/.embulk/bin/embulk -L "https://dl.embulk.org/embulk-latest.jar"
$ chmod +x ~/.embulk/bin/embulk
$ echo 'export PATH="$HOME/.embulk/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc
$ embulk -version
embulk 0.9.23

Embulk の設定ファイルを生成

seed.yml を作成
アクセスログから作成した .csv ファイルのパスを指定

in:
  type: file
  path_prefix: "./access.log.csv"
out:
  type: stdout

embulk guess コマンドで config.yml を作成

$ embulk guess seed.yml -o config.yml
in:
  type: file
  path_prefix: ./access.log.csv
  parser:
    charset: UTF-8
    newline: LF
    type: csv
    delimiter: "\t"
    quote: '"'
    escape: '"'
    trim_if_not_quoted: false
    skip_header_lines: 0
    allow_extra_columns: false
    allow_optional_columns: false
    columns:
    - {name: c0, type: string}
    - {name: c1, type: timestamp, format: '%d/%b/%Y:%H:%M:%S %z'}
    - {name: c2, type: string}
    - {name: c3, type: string}
    - {name: c4, type: long}
    - {name: c5, type: long}
    - {name: c6, type: double}
    - {name: c7, type: string}
    - {name: c8, type: string}
out: {type: stdout}

生成されたカラム名をアクセスログの内容に合わせて修正

in:
  type: file
  path_prefix: ./access-log.csv
  parser:
    charset: UTF-8
    newline: LF
    type: csv
    delimiter: "\t"
    quote: '"'
    escape: '"'
    trim_if_not_quoted: false
    skip_header_lines: 0
    allow_extra_columns: false
    allow_optional_columns: false
    default_timezone: Asia/Tokyo
    columns:
    - {name: ip, type: string}
    - {name: time, type: timestamp, format: '%d/%b/%Y:%H:%M:%S %z'}
    - {name: request_method, type: string}
    - {name: request_uri, type: string}
    - {name: status_code, type: long}
    - {name: body_bytes_sent, type: long}
    - {name: request_time, type: double}
    - {name: referer, type: string}
    - {name: user_agent, type: string}
out: {type: stdout}

embulk-output-postgresql をインストール

Embulk 本体はデータを読み込んで変換を行う基本機能のことを指すので
input, output で利用する各サービスに合わせプラグインのインストールが必要になる

$ embulk mkbundle bundle

./bundle/Gemfile が作成されるので embulk-output-postgresql を追記

source 'https://rubygems.org/'

gem 'embulk'
gem 'embulk-output-postgresql' # 追記

./bundle 配下で embulk bundle コマンドを実行してプラグインをインストール

$ embulk bundle

PostgreSQL の設定追記

設定ファイル内で環境変数を利用可能にするため config.ymlconfig.yml.liquid に変更

$ mv config.yml config.yml.liquid

config.yml.liquidout: 以下を変更
PostgreSQL への接続情報は環境変数から取得しテーブル名は access_logs とする場合の例

in:
  type: file
  path_prefix: ./access-log.csv
  parser:
    charset: UTF-8
    newline: LF
    type: csv
    delimiter: "\t"
    quote: '"'
    escape: '"'
    trim_if_not_quoted: false
    skip_header_lines: 0
    allow_extra_columns: false
    allow_optional_columns: false
    default_timezone: Asia/Tokyo
    columns:
    - {name: ip, type: string}
    - {name: time, type: timestamp, format: '%d/%b/%Y:%H:%M:%S %z'}
    - {name: request_method, type: string}
    - {name: request_uri, type: string}
    - {name: status_code, type: long}
    - {name: body_bytes_sent, type: long}
    - {name: request_time, type: double}
    - {name: referer, type: string}
    - {name: user_agent, type: string}
out:
  type: postgresql
  host: {{ env.DB_HOST }}
  user: {{ env.DB_USER }}
  password: "{{ env.DB_PASSWORD }}"
  database: {{ env.DB_NAME }}
  table: access_logs
  mode: insert

Embulk を実行

$ embulk run -b bundle config.yml.liquid

テーブルが存在しない場合は自動で生成され、tsv 形式のアクセスログが PostgreSQL へ登録される

参考リンク

ローカル環境でも快適に LIFF の開発ができる liff-sdk-local を作りました

なぜ作ったのか

LIFF(LINE Front-end Framework) は SSL 対応した URL を元に LIFF URL(line://app/xxx)を生成することで使えるようになります。
その仕様で動作確認のしずらさを感じることがよくあり、どうしたらもっと開発効率があがるかなと考えていました。

解決策その1として liff-cli を去年作り、1年で約5,600回!ダウンロード(npm install liff)されています。

github.com

LIFF のリリース当初(2018/6) は LIFF URL の生成が LINE Messaging API を直接実行するしかなかったため、毎回ドキュメントの curl サンプルをコピペして実行していました。
今では LINE Developers Console にて LIFF を管理できる機能が追加されてたため、LIFF URL の生成は楽になりました。

LIFF URL 生成は楽になったものの
そもそも LIFF URL を生成せずに LIFF の開発できないんだっけ?
と考えて今回の liff-sdk-local を作りました。

github.com

なにが変わるのか

liff-sdk-local を使うことで次のように LIFF 開発が変わります!

今までの LIFF 開発
  1. LIFF SDK を導入、実装
  2. SSL 対応のサーバーにファイルをアップロード or ngrok などを使ってローカル環境を外部公開
  3. 2 で生成された URL を LINE Developers Console or LINE Messaging API を使い LIFF URL を生成
  4. 3 を LINE に送信して動作確認
これからの LIFF 開発
  1. LIFF SDK と liff-local-sdk を導入、実装
  2. ローカル環境で動作確認

liff-sdk-local 対応 API

以下の LIFF API に対応しています。

liff-sdk-local 使い方

  1. GitHub リポジトリより最新版をダウンロードしてください。
  2. dist/liff-sdk-local.min.js をプロジェクトに追加し、本家の LIFF SDK をコメントアウトしてください。
<!-- <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script> -->
<script src="path/to/liff-sdk-local.min.js"></script>
<script>
   liff.init(function(data) {
       // 処理...
   });
</script>

これで LIFF URL を生成せずにローカル環境で LIFF の動作確認ができます。

戻り値をカスタマイズする

各 API の戻り値はデフォルトでは LIFF API のドキュメントのサンプルと同じ値が返ります。

戻り値を変更するには window.liffSettings に設定してください。

<script>
   window.liffSettings = {
       userId: 'U12345',
   };
</script>
<!-- <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script> -->
<script src="path/to/liff-sdk-local.min.js"></script>
<script>
   liff.init(function(data) {
       console.log(data.context.userId); // 出力: U12345
   });
</script>

設定可能な戻り値は以下です。

  • userId
  • displayName
  • pictureUrl
  • statusMessage
  • accessToken
  • language
  • type
  • utouId
  • groupId
  • roomId
  • viewType

おまけ

9/22 に開催された技術書典 7 にて販売された LINE API HANDBOOK で第 4 章「LIFF 活用事例と開発 Tips の紹介」を書かきました!

booth.pm