Redmineの更新をslackに通知してみた
はじめに
今回、業務においてRedmineのチケット更新をslackに通知させることにしたので、そのことをまとめます。
対応の経緯
私の会社ではとある業務の委託用にRedmineを使っているのですが、委託先からの差し戻しがあった場合にチケットへの回答が遅い!!(もう少し言葉は柔らかいですが・・)と度々言われていました。
その後何が原因かと探ってみたところ、、、そもそも差し戻しに気づいていないというのが一番の原因のようでした。
そりゃあねぇ、、Redmineでチケット更新をした際に通知をキャッチできるのはメールだけだからね。。その気持はわかる・・!
Backlogのようにお知らせ通知を設定できればまだ他業務をしていた際にでも気付けるのですが、そんな機能はもちろん無し。
どうしようかなと思っていたところ、弊社でビジネスチャットツールとして大活躍しているslackに通知が行けば気づくのではないか?と思い今回の機能作成を行いました。
今回の要件
- Redmineで特定のステータスに更新されたチケットをslackに通知する
- 出来ればリアルタイム連携
- 通知内容はRedmineの題名、担当者 チケットへのリンク (差し戻し内容をslackに通知するとごちゃついてしまうと予想してこんだけ。まずは気づくことが大事)
まずは既存のインテグレーションで利用できそうなものが無いか探す
すぐに出てきたのがhttps://www.sejuku.net/blog/852461 こちらの記事
RSSアプリを利用
結果:✕
弊社で使用しているRedmineはイントラ内で作成しているものなので、そもそもslackからRSSを利用してRedmineに接続が行えないため断念。
Redmineプラグインを利用
結果:✕
「Redmine Slack」というプラグインを利用しwebhookでslackに通知するというもの
こちら内容的には今回行いたいことと合ってはいたのですが、トリガーとなるのが新しくチケットを作成した場合のみ・・・。
おしい・・・。(せっかくこんな需要ありそうなプラグインなのでもう少し自由度を高めてほしかった・・)
対応方針
最終的にRedmine APIを利用し、能動的に条件に合うデータ(チケット)を取得し、それをslackに通知する
という対応に落ち着きました。
自分で好きにAPIを叩くので、今後の汎用性も高そうだなと。
改めて要件定義
- 30分に一回お知らせする(30分に1回バッチを動かす)
- 条件:チケットのステータスが「差し戻し」、更新時間が30分以内となっているチケットを取得
- slack通知する項目はチケット番号、URL、題名、担当者
実装内容
処理Pythonファイルの作成
sashimodoshi.py
# -*- coding: utf-8 -*-
import digdag
import requests
import sys
import os
from datetime import datetime, timedelta
import json
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from .utils.util_slacker import UtilSlacker
class Main():
def __init__(self, incoming_webhook_url, slack_token):
self.incoming_webhook_url = incoming_webhook_url
self.slack_token = slack_token
self.slacker = UtilSlacker(self.incoming_webhook_url, self.slack_token)
def main(self):
redmine_api_key = "APIアクセスキー" # 個人設定>APIアクセスキーから取得
redmine_url = "http://redmineのURL"
# 30分以内の更新日
now = datetime.utcnow() + timedelta(minutes=-30)
now = now.strftime('%Y-%m-%dT%H:%M:%SZ')
request_url_for_issues = redmine_url + "/issues.json"
params = {
"key": redmine_api_key,
"project_id": "dd",
"status_id": 4,
"updated_on": ">=" + now
}
issues_res = requests.get(request_url_for_issues, params)
issues_responses = json.loads(issues_res.text)
print(issues_responses)
fields = []
if len(issues_responses["issues"]) == 0: # 0件だったら処理抜ける
return
for issue in issues_responses["issues"]:
issue_id = issue["id"]
subject = issue["subject"]
assigned_to_name = issue["assigned_to"]["name"]
field = {
"title": subject,
"value": "担当者:" + assigned_to_name + "\r\nlink:<" + redmine_url + "/issues/{0}|{0}>".format(issue_id),
}
print(field)
fields.append(field)
msg = "`差し戻し`のチケットがあります。\r\n担当者はご確認ください。"
channel = "#pj-〇〇"
user = "redmine差し戻し通知"
icon_emoji = ":redmine:"
fallback = "redmine"
title = ""
attachment1 = self.slacker.attachment_creater(
fallback=fallback, title=title, color="danger", fields=fields)
attachments = [attachment1]
self.slacker.slack_messenger(
msg, channel, user, icon_emoji, is_link_name=1, attachments=attachments)
# -*- coding: utf-8 -*-
import digdag
import requests
import sys
import os
from datetime import datetime, timedelta
import json
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from .utils.util_slacker import UtilSlacker
class Main():
def __init__(self, incoming_webhook_url, slack_token):
self.incoming_webhook_url = incoming_webhook_url
self.slack_token = slack_token
self.slacker = UtilSlacker(self.incoming_webhook_url, self.slack_token)
def main(self):
redmine_api_key = "APIアクセスキー" # 個人設定>APIアクセスキーから取得
redmine_url = "http://redmineのURL"
# 30分以内の更新日
now = datetime.utcnow() + timedelta(minutes=-30)
now = now.strftime('%Y-%m-%dT%H:%M:%SZ')
request_url_for_issues = redmine_url + "/issues.json"
params = {
"key": redmine_api_key,
"project_id": "dd",
"status_id": 4,
"updated_on": ">=" + now
}
issues_res = requests.get(request_url_for_issues, params)
issues_responses = json.loads(issues_res.text)
print(issues_responses)
fields = []
if len(issues_responses["issues"]) == 0: # 0件だったら処理抜ける
return
for issue in issues_responses["issues"]:
issue_id = issue["id"]
subject = issue["subject"]
assigned_to_name = issue["assigned_to"]["name"]
field = {
"title": subject,
"value": "担当者:" + assigned_to_name + "\r\nlink:<" + redmine_url + "/issues/{0}|{0}>".format(issue_id),
}
print(field)
fields.append(field)
msg = "`差し戻し`のチケットがあります。\r\n担当者はご確認ください。"
channel = "#pj-〇〇"
user = "redmine差し戻し通知"
icon_emoji = ":redmine:"
fallback = "redmine"
title = ""
attachment1 = self.slacker.attachment_creater(
fallback=fallback, title=title, color="danger", fields=fields)
attachments = [attachment1]
self.slacker.slack_messenger(
msg, channel, user, icon_emoji, is_link_name=1, attachments=attachments)
この処理をバッチで呼ぶのですが、弊社では既にdigdagサーバーが用意されているため、そこ上で動かすようにしました。
timezone: Asia/Tokyo
schedule:
cron>: 00,30 10-18 * * 1-5
weekly>: Thu,10:30:00
_export:
workflow_name: "sashimodoshi"
+sashimodoshi:
py>: bin.python_project.sashimodoshi.Main.main
incoming_webhook_url: ${env.incoming_webhook_url}
slack_token: ${env.slack_token}
digファイルはこんな感じです。
scheduleの設定で月曜〜金曜の10時〜18時の間で30分感覚で実行するように設定してあります。
(ここらへんは見様見真似で書いているだけで、まだ理解は追いついてません・・)
実際のslack通知
実装イメージはこの様になりました。
今回slack通知のAPIでattachmentを利用しているのですが、いい感じに見栄え良くできました!
あとがき
今回作業効率化・自動化のために勢いでslack通知を実装しましたが、残課題としては
- Redmineの個人アカウントではなく、通知用の汎用アカウントでAPIキーを発行すべき
- そもそもPythonファイル内に設定情報を書くべきではない
あたりなので近々修正しておきます。
まともにPythonを触るのが今回初なのですが、APIの接続をサクッとできたり、場合によってはExcel操作も出来たりと作業効率化・自動化の面では今後も活躍してくれそうなのでまだまだ勉強していこうと思います!!
Author And Source
この問題について(Redmineの更新をslackに通知してみた), 我々は、より多くの情報をここで見つけました https://qiita.com/marines_2027/items/3a2890907e265265e883著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .