LoginSignup
7

More than 3 years have passed since last update.

posted at

updated at

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の勉強になりました。

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
What you can do with signing up
7