Skip to content

Commit 2b3f942

Browse files
committed
feat: Add support for custom message templates with new configuration options and updated documentation to version 2.1.0.
1 parent e8068a4 commit 2b3f942

File tree

4 files changed

+110
-25
lines changed

4 files changed

+110
-25
lines changed

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ A Sentry extension to post notifications to DingTalk (钉钉) robot.
2626
> * **🚦 Level**: error
2727
> * **📍 Location**: `app/utils/math.js`
2828
>
29+
> 📢 **Trigger**: alert-rule
2930
> [👉 View Issue on Sentry](#)
3031
3132
## ✨ 核心功能
@@ -71,7 +72,7 @@ A Sentry extension to post notifications to DingTalk (钉钉) robot.
7172

7273
## ⚙️ 配置手册
7374

74-
进入 **Sentry > Project Settings > Legacy Integrations > DingTalk**
75+
进入 **Sentry > Project Settings > Legacy Integrations > DingTalk Notify**
7576

7677
### 1. Access Token (必填)
7778
钉钉机器人 Webhook URL 中的 `access_token` 参数。
@@ -102,6 +103,29 @@ A Sentry extension to post notifications to DingTalk (钉钉) robot.
102103
需要 @ 的群成员手机号。
103104
* **格式**: 英文逗号分隔,例如 `13800000000,13900000000`
104105

106+
### 5. Custom Message / 自定义消息模版 (v2.1.0+)
107+
支持使用 Markdown 自定义消息格式。勾选 "Enable Custom Message" 后填入由变量组成的模版。
108+
109+
**可用变量**:
110+
* `{project}`: 项目名 (e.g. sentry-demo)
111+
* `{title}`: 异常标题 (e.g. ValueError)
112+
* `{message}`: 异常详情
113+
* `{url}`: 报错详情页链接
114+
* `{level}`: 报错级别 (error/info)
115+
* `{environment}`: 环境 (prod/dev)
116+
* `{culprit}`: 异常位置 (code path)
117+
* `{display_title}`: 包含关键词前缀的标题 (推荐使用,用于通过关键词验证)
118+
119+
**模版示例**:
120+
```markdown
121+
### {display_title}
122+
**环境**: {environment}
123+
**位置**: `{culprit}`
124+
> {message}
125+
[👉查看详情]({url})
126+
```
127+
*(注意:钉钉 Markdown 换行需在行尾加两个空格,或使用列表格式)*
128+
105129
---
106130

107131
## ⚠️ 常见问题:为什么收不到自动通知?

README_en.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ A Sentry extension to post notifications to DingTalk (钉钉) robot.
2727
> * **🚦 Level**: error
2828
> * **📍 Location**: `app/utils/math.js`
2929
>
30+
> 📢 **Trigger**: alert-rule
3031
> [👉 View Issue on Sentry](#)
3132
3233
## ✨ Features
@@ -72,7 +73,7 @@ Quickly install without rebuilding images. **Note: The plugin will disappear if
7273

7374
## ⚙️ Configuration Guide
7475

75-
Go to **Sentry > Project Settings > Legacy Integrations > DingTalk**.
76+
Go to **Sentry > Project Settings > Legacy Integrations > DingTalk Notify**.
7677

7778
### 1. Access Token (Required)
7879
The access token for your DingTalk robot. You can find this in the Webhook URL: `https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN`.
@@ -105,6 +106,29 @@ If your robot uses "Keyword" security verification.
105106
Comma-separated list of mobile numbers to @ in the group chat.
106107
* **Example**: `13800000000,13900000000`
107108

109+
### 5. Custom Message / Template (v2.1.0+)
110+
Supports custom Markdown message format. Check "Enable Custom Message" and define your template using variables.
111+
112+
**Available Variables**:
113+
* `{project}`: Project Name (e.g. sentry-demo)
114+
* `{title}`: Exception Type (e.g. ValueError)
115+
* `{message}`: Error Details
116+
* `{url}`: Link to Sentry Issue
117+
* `{level}`: Error Level (error/info)
118+
* `{environment}`: Environment (prod/dev)
119+
* `{culprit}`: Code Location (file path)
120+
* `{display_title}`: Title with keyword prefix, use this for keyword validation robots.
121+
122+
**Example Template**:
123+
```markdown
124+
### {display_title}
125+
**Env**: {environment}
126+
**Location**: `{culprit}`
127+
> {message}
128+
[👉View Issue]({url})
129+
```
130+
*(Note: DingTalk Markdown requires **two spaces** at the end of a line for a line break)*
131+
108132
---
109133

110134
## ⚠️ Important: Why am I not receiving notifications?
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.0.1'
1+
__version__ = '2.1.0'

sentry_dingtalk_notify/plugin.py

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,22 @@ class DingTalkOptionsForm(forms.Form):
4444
help_text='Whether to @ all members in the group.',
4545
required=False
4646
)
47+
custom_message_enabled = forms.BooleanField(
48+
label='Enable Custom Message',
49+
help_text='If checked, the "Custom Message Template" below will be used.',
50+
required=False
51+
)
52+
custom_message_template = forms.CharField(
53+
label='Custom Message Template',
54+
widget=forms.Textarea(attrs={'class': 'span6', 'placeholder': '### {title}\n\n**Project**: {project}\n**Error**: {message}\n[View]({url})'}),
55+
help_text='Markdown template. Available variables: {project}, {title}, {display_title}, {message}, {url}, {level}, {environment}, {culprit}, {project_slug}, {project_name}, {org_name}.',
56+
required=False
57+
)
58+
4759

4860
from .__version__ import __version__
49-
61+
62+
5063
class DingTalkPlugin(NotificationPlugin):
5164
author = 'lanxuexing'
5265
author_url = 'https://github.com/lanxuexing/sentry-dingtalk-notify'
@@ -86,6 +99,8 @@ def notify_users(self, group, event, fail_silently=False, triggering_rules=None,
8699
custom_keyword = self.get_option('custom_keyword', group.project) or ''
87100
at_mobiles_str = self.get_option('at_mobiles', group.project) or ''
88101
is_at_all = self.get_option('is_at_all', group.project) or False
102+
custom_message_enabled = self.get_option('custom_message_enabled', group.project) or False
103+
custom_message_template = self.get_option('custom_message_template', group.project) or ''
89104

90105
logger.info(f"DingTalk notify_users called for event {event.event_id}")
91106

@@ -143,29 +158,51 @@ def notify_users(self, group, event, fail_silently=False, triggering_rules=None,
143158

144159
link = self.get_group_url(group)
145160

146-
# Markdown Content
147-
# Using a list style for better readability
148-
text = f"### {evt_title}\n\n"
149-
150-
# Key Metadata
151-
text += f"- **📦 Project**: {project_slug} ({org_name})\n"
152-
text += f"- **🌍 Env**: {environment}\n"
153-
text += f"- **🚦 Level**: {level}\n"
154-
text += f"- **📍 Location**: `{culprit}`\n"
161+
# Determine Markdown Content
162+
text = ""
163+
used_custom = False
164+
165+
if custom_message_enabled and custom_message_template:
166+
# Prepare context for custom template
167+
context = {
168+
'project': project_name, # Human readable name
169+
'project_name': project_name, # Alias
170+
'project_slug': project_slug, # Technical slug
171+
'org_name': org_name,
172+
'title': evt_title,
173+
'display_title': display_title, # Title with keyword prefix 【Keyword】
174+
'message': evt_message,
175+
'url': link,
176+
'level': level,
177+
'environment': environment,
178+
'culprit': culprit,
179+
}
180+
try:
181+
text = custom_message_template.format(**context)
182+
used_custom = True
183+
logger.info("DingTalk: Used custom message template.")
184+
except Exception as e:
185+
logger.error(f"DingTalk: Error formatting custom template: {e}. Falling back to default.")
186+
text = "" # Fallback logic below will handle empty text
155187

156-
# Error Message (Quote block)
157-
if evt_message and evt_message != evt_title:
158-
text += f"\n> {evt_message}\n\n"
159-
else:
160-
text += "\n"
161-
162-
# Trigger Context
163-
if triggering_rules:
164-
text += f"📢 **Trigger**: {', '.join(triggering_rules)}\n"
188+
if not text:
189+
# Standard Default Template
190+
text = f"### {evt_title}\n\n"
191+
text += f"- **📦 Project**: {project_slug} ({org_name})\n"
192+
text += f"- **🌍 Env**: {environment}\n"
193+
text += f"- **🚦 Level**: {level}\n"
194+
text += f"- **📍 Location**: `{culprit}`\n"
195+
196+
if evt_message and evt_message != evt_title:
197+
text += f"\n> {evt_message}\n\n"
198+
else:
199+
text += "\n"
200+
201+
if triggering_rules:
202+
text += f"📢 **Trigger**: {', '.join(triggering_rules)}\n"
203+
204+
text += f"\n[👉 View Issue on Sentry]({link})\n"
165205

166-
# Action Link
167-
text += f"\n[👉 View Issue on Sentry]({link})\n"
168-
169206
# Append @ mentions to text so they are visually highlighted
170207
if at_mobiles:
171208
at_text = ' '.join([f"@{m}" for m in at_mobiles])

0 commit comments

Comments
 (0)