Help us understand the problem. What is going on with this article?

ConnpassのAPIを利用し、Mattermostからイベント検索

経緯

Connpassをよく利用させて頂いており、APIがあることを見つけました。
Mattermostへイベントのキーワードを入力し、結果が取得できたら便利かなと思い立ちました。
Pythonのコードは特に拙いのでより良い点がありましたらご教示をお願い致します。

概要図

  1. 指定のチャンネルへ検索したいキーワードを入力
  2. Mattermostの外向きウェブフックへ設定したコールバックURLへメッセージ送信
  3. API GatewayがメッセージをLambdaへパススルー
  4. Lambdaがメッセージ内から検索キーワードを元にconnpassのイベントサーチAPIを実行
  5. Connpass のイベントサーチAPIから帰ってきた結果からLambdaにてイベントURLのみMattermost(内向きウェブフック)へ送信
  6. Mattermostの指定のチャンネルにLambdaからイベントURLが送信される

image.png

設定

Lambda

Python3

import os
import json
import urllib.request
from urllib.parse import urlparse
import time
import datetime

Contact_Slack = os.environ['SLACK']
# Lambdaの環境変数に設定されたMattermostの内向きウェブフックのURLを変数へ

url = "https://connpass.com/api/v1/event/?keyword=東京都"
# Connpass APIのURLを指定。全国の結果が帰ってきてしまうので、キーワードへ東京都を追加。

p = urlparse(url)
query = urllib.parse.quote_plus(p.query, safe='=&')
url = '{}://{}{}{}{}{}{}{}{}'.format(
    p.scheme, p.netloc, p.path,
    ';' if p.params else '', p.params,
    '?' if p.query else '', query,
    '#' if p.fragment else '', p.fragment)
# URLにマルチバイトの文字を扱えない為、フォーマットを変更

def post_slack(message):
# post_slack関数に渡された内容をMattermostの内向きウェブフックへ送る関数です。

    msg = str(message)
    SLACK_POST_URL = Contact_Slack

    send_data = {
        "username": "Slack-test",
        "text": msg
    }
# usernameは不要かもしれません。

    send_text = "payload=" + json.dumps(send_data)

    request = urllib.request.Request(
        Contact_Slack, 
        data=send_text.encode('utf-8'), 
        method="POST"
    )

    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode('utf-8')

def lambda_handler(event, context):
# API Gateway経由でMattermostから送られたデータをConnpassのAPIへ投げます。
    print(event)
    keyword_list = event["text"]
    print(keyword_list)
    keyword = list(map(str, keyword_list.split(" ")))
# キーワードは半角スペース区切りにしているので、分割しlist型で変数へ。
    try:

        for events in keyword:
            dic = {}
            print(events)
            post_slack(events)
# post_slack関数へeventsを送信
            events = urllib.parse.quote(events)
# キーワードが日本語の場合があるので、エンコードを実施
            api = url + "," + events
# 入力したキーワードは東京都と合わせて二つ目の為、URLとキーワードの間に,を入れる
            print(api)
            req = urllib.request.Request(api)
            with urllib.request.urlopen(req) as res:
                data = res.read().decode('utf-8')
# キーワードごとにConnpassのAPIへ投げ、結果をdataへ格納

            events = json.loads(data)
            for key in events["events"]:
                eve = key["event_url"]
                started = key["started_at"]
                print(eve)
                print(started)
                dic.setdefault(started, eve)
# イベントのスタート日時とイベントURLを格納

            print(dic)
            dct = sorted(dic.items())
# スタート日時でソート
            print(dct)
# 検索結果は過去分のイベントが含まれている場合がある為、以下の処理でMattermostへ送らないようにします。

            for k, v in sorted(dic.items()):
# ソートは冗長な記載のような
                print(str(k) + ": " + str(v))
                k = k.replace("+09:00", "")
# イベントのスタート日時がISO-8601形式(2012-04-17T18:30:00+09:00)な為、+09:00を消します。
                #print(k)
                dt = datetime.datetime.fromisoformat(k)
# 文字型からdatetime型へ
                #print(dt)
                if dt > datetime.datetime.today():
# もし、スタート日時が今日以降であればpost_slack関数とLogへ出力
                    print(str(v))
                    post_slack(v)
            print("END")
            post_slack("END")
        print("ALL END")
        post_slack("ALL END")

    except:
        print("ERROR")
        post_slack("ERROR")

環境変数

SLACK:Mattermost内向きウェブフック

タイムアウト

1分

その他

デフォルト

Mattermost

外向きウェブフック

MattermostからAPI GatewayのURLを設定

タイトル:任意
Content-Type: application/json
チャンネル:キーワード入力するチャンネル
トリガーワード:なし
コールバックURL:API GatewayのURL

内向きウェブフック

Lambdaから結果を貰うURLの設定

タイトル:任意
チャンネル:結果を貰うチャンネル(ループしそうなので外向きウェブフックと分けました。)

API Gateway

APIの作成

項目
プロトコル REST
新しい API の作成 新しい API
API 名* 任意
説明 任意
エンドポイントタイプ リージョン

メゾットの作成

アクション→メゾットの作成

項目
メゾット POST
統合タイプ Lambda 関数
Lambda プロキシ統合の使用 チェックなし
Lambda リージョン Lambda作成したリージョン
デフォルトタイムアウトの使用 チェックあり

ステージの作成

アクション→ステージの作成

項目
ステージ名* 任意
ステージの説明 任意
デプロイメント* 任意

ここのステージに表示されるURL呼び出しのURLがMattermostの外向きウェブフックへ設定するURLになります。

リソースポリシー

このままだとなんの制限も入っていない為、API Gatewayを呼び出せるIPアドレスに制限をかけます。
作成したAPIの画面内にリソースポリシーがある為、選択し以下のjsonを入れ保存します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "メゾットリクエストのARN",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "アクセス元IP/サブネットマスク"
                }
            }
        }
    ]
}

保存が完了したら、アクションからAPIをデプロイさせ、反映させてください。

設定は以上です。

Mattermostからキーワードを入力し、指定のチャンネルへURLが受信できれば完成となります。

良いところ

  • Mattermostへのキーワード入力がトリガー
  • キーワードの複数に対応
  • 過去分のイベントは送信されない
  • 日付でソートされて送信される
  • Mattermostなのでスマホからでも利用できる

躓いた事

  • 東京都が全角でconnpassのAPIを叩けない
  • MattermostからAPI Gatewayの通信がうまくいかない
    • API Gatewayのマッピングテンプレートをデフォルトのままにする。
      • Slack等で類似の事をされている記事がたくさんあり、そちらではマッピングテンプレートの追加をされていますが私のケースではLambdaへ連携されなかった為、デフォルトのままにしています。
      • リソースポリシーはデプロイしないと反映されない。
        • 保存して満足してましたが、制限がかかっていませんでした。
      • リソースポリシーはサブネットマスクの表記までいる(気がする)
  • 通知内容がごちゃごちゃする
    • MattermostはURLを見やすくしてくれるので、URLのみ送信することにした。
    • image.png
  • 過去分のイベントが送られてくる
    • Lambdaで判定したいが、ISO-8601形式(2012-04-17T18:30:00+09:00)な為、比較ができない
      • +09:00を消した。
  • 送られてくるイベントの日時がバラバラなので近い日付から順番に送られるようにしたい
    • Lambdaでスタート日時でソート

まとめ

Mattermostの記事が少なく、API Gatewayとうまく連携取れない時が大変でした。
(どっち側が悪いのか検討がづけられず・・・)
Pythonの勉強になりました。

tomoki_s
AWSもPythonもコンテナも勉強しないと!
stylez
WEB・業務システム、インフラ構築・運用監視まで、幅広い開発実績と経験豊富なエンジニアによる自社開発体制で、スピーディかつ高クオリティのシステム開発を手掛けています。AWSをはじめ各種クラウドやベンダーパートナーとして総合的なITサービスや、独自移行ツールを使ったマイグレーション、サーバーレスなシステム構築、コンテナを利用したDevOpsコンサルティングなどを提供しています。
https://www.stylez.co.jp/
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした