Edited 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が送信される


設定


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のみ送信することにした。





  • 過去分のイベントが送られてくる


    • Lambdaで判定したいが、ISO-8601形式(2012-04-17T18:30:00+09:00)な為、比較ができない


      • +09:00を消した。





  • 送られてくるイベントの日時がバラバラなので近い日付から順番に送られるようにしたい


    • Lambdaでスタート日時でソート




まとめ

Mattermostの記事が少なく、API Gatewayとうまく連携取れない時が大変でした。

(どっち側が悪いのか検討がづけられず・・・)

Pythonの勉強になりました。