0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

microCMSのコンテンツ公開の連絡をChatworkへ投稿する、画像も添えて(Webhook)

Last updated at Posted at 2025-11-15

はじめに

microCMSでコンテンツを公開した時にWebhookを利用して、その旨をChatworkと連携してメッセージ投稿する方法として、

を作成したのですが、それだけだと投稿内容が寂しいので、続編として画像を添えてみます。なお、前編を読んでもらっている前提とさせて頂き、ある程度、重複したところは省略します。

前提

microCMSのアカウントを保有済(フリーのHobbyプランでも可能)
Chatworkのアカウントを保有済(フリーアカウントでも可能)

microCMSで画像アップロード可能?

microCMSの料金プランを見て、microCMSのHobbyプランだと、画像のアップロードはできないように思っていました。

が、実際に試すと可能ですね。

Hobbyプランだと画像だけですが、可能です。データ転送量の制限がありますので、実業務で使用するのであれば、上位のプランを検討した方が良さそうです。

(ステップ1) microCMS側の準備

前編と同じく、API名は社内通知で、エンドポイントはnotice にします。
APIスキームも前編とほぼ同じですが、画像フィールドを追加しています。
image.png

(ステップ2) Chatwork側の準備

前編と同じく、APIトークンとルームIDを調べておきます。

(ステップ3) AWS側の準備

画像を添えた投稿をしようとすると、microCMSで用意しているChatwork用の標準的なWebhookでは無理ですので、Webhookのカスタム通知を利用することになります。このカスタム通知は外部のAPIを叩くものですが、本記事ではサーバレスで用意できる、AWS API Gatewayを利用します。という訳で(私の作成した)別の記事

をそのまま利用して、AWS Lambdaのコードだけを変更して、APIを用意します。
ChatworkのファイルアップロードAPIは
https://developer.chatwork.com/reference/post-rooms-room_id-files
に分かりやすい説明があるので、それを組み込みます。

import os
import hmac
import hashlib
import json
import urllib.request
from urllib.parse import urlparse
import base64
import re
import requests
import mimetypes

max_file_size = 5 * 1024 * 1024 # Chatworkのファイルアップロード上限5MB

def _get_header(event, name: str):
    headers = event.get("headers") or {}
    lname = name.lower()
    for k, v in headers.items():
        if k.lower() == lname:
            return v
    return None

def _response(status: int, body: str):
    return {
        "statusCode": status,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps(body),
    }

def get_filename(imageurl):
    return os.path.basename(urlparse(imageurl).path)

def get_content_type(filename):
    return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

def _post_chatwork_message(chatwork_token : str, roomid: str, title: str, content: str, imageurl: str):
    url = "https://api.chatwork.com/v2/rooms/" + roomid + "/messages"
    message = _make_message(title, content, imageurl) 
    payload = { "self_unread": 0, "body": message }
    headers = {
        "accept": "application/json",
        "content-type": "application/x-www-form-urlencoded",
        "x-chatworktoken": chatwork_token
    }
    response = requests.post(url, data=payload, headers=headers)
    print(response.text)

def _post_chatwork_file(chatwork_token : str, roomid: str, title: str, content: str, imageurl: str):
    image = None
    with urllib.request.urlopen(imageurl) as response:
        # 画像が大きすぎる場合はメッセージのみ投稿
        content_length = response.headers['content-length'] if 'content-length' in response.headers else 0
        print(f"レスポンスヘッダーからのサイズ: {content_length} バイト")
        if int(content_length) > max_file_size:
            _post_chatwork_message(chatwork_token, roomid, title, content, imageurl)
            return
        image = response.read(max_file_size + 1)
        image_size = len(image)
        print("image_size=", image_size)
        if image_size > max_file_size:
            _post_chatwork_message(chatwork_token, roomid, title, content, imageurl)
            return

    filename = get_filename(imageurl)
    content_type = get_content_type(filename)
    url = "https://api.chatwork.com/v2/rooms/" + roomid + "/files"
    files = { "file": (filename, image, content_type) }

    message = _make_message(title, content, None) 
    payload = { "message": message }
    headers = {
        "accept": "application/json",
        "x-chatworktoken": chatwork_token
    }
    response = requests.post(url, data=payload, files=files, headers=headers)
    print(response.text)

def _make_message(title, content, imageurl):
    message = ''
    if title:
        message += title
    if content:
        # 本文のHTMLタグを除去
        content = re.sub(re.compile('</p>'), "\n", content)
        content = re.sub(re.compile('<.*?>'), '', content)
        message += "\n" + content
    if imageurl:
        message += "\n" + imageurl   
    print("message=", message)
    return message

def lambda_handler(event, context):
    # シークレット
    secret = os.environ.get("MICROCMS_WEBHOOK_SECRET")
    if not secret:
        return _response(500, "Server misconfigured: missing secret")
    print('secret=', secret)

    # Chatwork用トークン
    chatwork_token = os.environ.get("MICROCMS_WEBHOOK_CHATWORK_TOKEN")
    if not chatwork_token:
        return _response(500, "Server misconfigured: missing chatwork token")
    print('chatwork_token=', chatwork_token)

    # Chatwork用ルームID
    roomid = os.environ.get("MICROCMS_WEBHOOK_CHATWORK_ROOMID")
    if not roomid:
        return _response(500, "Server misconfigured: missing roomid")
    print('roomid=', roomid)

    # イベントとコンテキストの内容を出力
    print( "event:" , event )
    print( "context:", context )

    # microCMSから送られる署名
    signature = _get_header(event, "x-microcms-signature")
    print('signature=', signature)

    # データから計算した署名(HMAC-SHA256)
    body = event.get('body')
    if event.get("isBase64Encoded"):
        try:
            body_bytes = base64.b64decode(body)
        except Exception:
            return _response(400, "Invalid base64 body")
    else:
        # UTF-8でbytes化
        body_bytes = body.encode("utf-8")
    expected_signature = hmac.new(
        key=secret.encode("utf-8"),
        msg=body_bytes,
        digestmod=hashlib.sha256,
    ).hexdigest()
    print('expected_signature=', expected_signature)

    # 署名の検証
    if not hmac.compare_digest(signature, expected_signature):
        print("Invalid signature")
        return _response(401, "Unauthorized")

    # 検証成功
    print("Valid signature")

    # パースする
    param = json.loads(body)
    print(param)
    title = param['contents']['new']['publishValue']['title']
    content = param['contents']['new']['publishValue']['content']
    imageurl = param['contents']['new']['publishValue']['image']['url']
    _post_chatwork_file(chatwork_token, roomid, title, content, imageurl)
    return _response(200, "OK")

少し、説明をすると、
・Chatworkでは添付ファイルは5MBまでという制限があるので、5MBを超える時は通常のメッセージ投稿にして、その時は画像ファイルのURLを貼り付けるだけにする。ちなみに、microCMSの画像URLは一般公開されているので、特に工夫しなくても読めるはず。
・画像ファイルのサイズは、レスポンスヘッダーに値が登録されていれば、その値を、設定がない時はダウンロードして判定する。(microCMSの画像ファイルは、レスポンスヘッダーに値が登録されているようです)
・画像ファイルの末尾からダウンロード時のファイル名を決定して、さらにそのファイルの添え字からMIMETYPEを決定する。
・requestsはPython標準モジュールではないので、AWS Lambdaで使用するには、requestsモジュールを組み込んだレイヤーを追加しています。

・シークレット、Chatwork用トークン、ルームIDについては環境変数として、
image.png
を用意して、プログラムから読み込んでいます。

(ステップ4) microCMSのWebhook

microCMSのカスタム通知に、API GatewayのURLと、上記のシークレットを入力します。
image.png
これで完成です。

(ステップ5) テスト

microCMSで、レコードを入力して公開ボタンをクリックすると、
image.png

Chatworkに画像付きで投稿されました。
image.png

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?