Lambdaを使って特定サイトのはてなブックマーク総数と増加数をSlackに通知する

6/14にはてなブックマークのブクマ数取得APIが公開された.

特定のサイトに対する合計ブックマーク数を取得する API を実験的に公開します - はてなブックマーク開発ブログ

ブックマーク総数が取得できるということで,例えば「ブログのブクマ数が今日どのくらい増えたのか」を確認できると便利だと思い作ってみた. ブクマ総数前回の実行時からどのくらい増加したかを通知してくれる. f:id:cohalz:20180616163803p:plain

CloudWatch Eventsでeventにbookmark_urlslack_urlを渡すことを想定している.

import boto3
import os
import json
import urllib.request
import re

def lambda_handler(event, context):
    bookmark_url = event['bookmark_url']
    message = bookmark_url + ' のブックマーク総数: '
    total_count = get_total_count(bookmark_url)
    total = total_count['total_bookmarks']
    env_key = re.sub(r'^[^a-zA-Z]|[^a-zA-Z0-9\_]', '', bookmark_url)
    
    prev_tmp = os.getenv(env_key)
    if prev_tmp != None:
        prev_total = int(prev_tmp)
        message  += str(total) + '(' + get_delta_str(total, prev_total) + ')'
        if total != prev_total:
            update_variables(env_key, total, context.function_name)
    else:
        message += str(total)
        update_variables(env_key, total, context.function_name)
    
    print(message)
    post_to_slack(event['slack_url'], message)

    return total_count
    
def get_total_count(bookmark_url):
    params = urllib.parse.urlencode({'url': bookmark_url})
    api_url = 'http://api.b.st-hatena.com/entry.total_count?' + params
    
    with urllib.request.urlopen(api_url) as response:
        return json.loads(response.read().decode('utf-8'))

def update_variables(env_key, total, function_name):
    env = {}
    for key, value in os.environ.items():
        if key.startswith('http'):
            env[key] = value
    env[env_key] = str(total)

    client = boto3.client('lambda')

    return client.update_function_configuration(
        FunctionName=function_name,
        Environment={
            'Variables': env
        })

def post_to_slack(slack_url, text):
    json_data = json.dumps({'text': text}).encode('utf-8')
    request = urllib.request.Request(slack_url, data=json_data, method='POST')
    with urllib.request.urlopen(request) as response:
        return response.read().decode('utf-8')

def get_delta_str(x, y):
    delta = x - y
    if delta >= 0:
        return '+' + str(delta)
    else:
        return str(delta)

前回の状態をLambdaに記憶させるために,AWS SDK経由で環境変数に書き込む方法を取った. 環境変数を使うことで別のサービスに依存しない簡易的なKVSとして扱える.

ただし,環境変数に使える文字は限られているのでkeyを工夫する必要がある.

env_key = re.sub(r'^[^a-zA-Z]|[^a-zA-Z0-9\_]', '', bookmark_url)

docs.aws.amazon.com

また,環境変数の変更にはLambdaに対してlambda:UpdateFunctionConfigurationの権限追加が必要になる.

LambdaおよびCloudWatch Eventsで定期実行するためのCloudFormationテンプレートはこちらから. github.com