重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
email-notifications by groeimetai/snow-flow
npx skills add https://github.com/groeimetai/snow-flow --skill email-notificationsServiceNow 通知由事件触发,并向用户发送电子邮件、短信或其他警报。
| 组件 | 表 | 用途 |
|---|---|---|
| 通知 | sysevent_email_action | 主通知记录 |
| 邮件模板 | sysevent_email_template | 可重用的邮件布局 |
| 事件 | sysevent | 触发通知 |
| 事件注册 | sysevent_register | 定义自定义事件 |
| 邮件脚本 | sys_script_email | 动态内容脚本 |
Notification: Incident Assigned
├── When to send
│ ├── Table: incident
│ ├── When: Record inserted or updated
│ └── Conditions: assigned_to changes AND is not empty
├── Who will receive
│ ├── Users: ${assigned_to}
│ └── Groups: (optional)
├── What it will contain
│ ├── Subject: Incident ${number} assigned to you
│ ├── Message: HTML body with ${field} references
│ └── Email Template: (optional)
└── Advanced
├── Weight: 0 (priority)
└── Send to event creator: false
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// Simple field conditions
assigned_to CHANGES
priority = 1
state = 6 // Resolved
// Script condition (ES5 only!)
// Returns true to send, false to skip
(function() {
// Only notify for VIP callers
var caller = current.caller_id.getRefRecord();
return caller.vip == true;
})()
// Advanced condition with multiple checks
(function() {
// Don't notify on bulk updates
if (current.sys_mod_count > 100) return false;
// Only for production CIs
var ci = current.cmdb_ci.getRefRecord();
return ci.used_for == 'Production';
})()
<!-- Field references -->
${number}
<!-- Direct field value -->
${caller_id.name}
<!-- Dot-walked reference -->
${opened_at.display_value}
<!-- Display value -->
<!-- Special variables -->
${URI}
<!-- Link to record -->
${URI_REF}
<!-- Reference link -->
${mail_script:script_name}
<!-- Include email script -->
<!-- Conditional content -->
${mailto:assigned_to}
<!-- Mailto link -->
<html>
<body style="font-family: Arial, sans-serif;">
<h2>Incident ${number} - ${short_description}</h2>
<table border="0" cellpadding="5">
<tr>
<td><strong>Priority:</strong></td>
<td>${priority}</td>
</tr>
<tr>
<td><strong>Caller:</strong></td>
<td>${caller_id.name}</td>
</tr>
<tr>
<td><strong>Assigned to:</strong></td>
<td>${assigned_to.name}</td>
</tr>
<tr>
<td><strong>Description:</strong></td>
<td>${description}</td>
</tr>
</table>
<p>
<a href="${URI}">View Incident</a>
</p>
${mail_script:incident_history}
</body>
</html>
// Email Script: incident_history
// Table: incident
// Script (ES5 only!):
;(function runMailScript(current, template, email, email_action, event) {
// Build activity history
var html = "<h3>Recent Activity</h3><ul>"
var history = new GlideRecord("sys_journal_field")
history.addQuery("element_id", current.sys_id)
history.addQuery("name", "incident")
history.orderByDesc("sys_created_on")
history.setLimit(5)
history.query()
while (history.next()) {
html += "<li><strong>" + history.sys_created_on.getDisplayValue() + "</strong>: "
html += history.value.substring(0, 200) + "</li>"
}
html += "</ul>"
template.print(html)
})(current, template, email, email_action, event)
// Add attachments from the record to the email
;(function runMailScript(current, template, email, email_action, event) {
var gr = new GlideRecord("sys_attachment")
gr.addQuery("table_sys_id", current.sys_id)
gr.addQuery("table_name", "incident")
gr.query()
while (gr.next()) {
email.addAttachment(gr)
}
})(current, template, email, email_action, event)
// Email Script to add CC recipients dynamically
;(function runMailScript(current, template, email, email_action, event) {
// Add all group members as CC
var group = current.assignment_group
if (!group.nil()) {
var members = new GlideRecord("sys_user_grmember")
members.addQuery("group", group)
members.query()
while (members.next()) {
var user = members.user.getRefRecord()
if (user.email) {
email.addAddress("cc", user.email, user.name)
}
}
}
})(current, template, email, email_action, event)
// Event Registration
// Name: x_myapp.incident.escalated
// Table: incident
// Description: Fired when incident is escalated to management
// Fired by: Business Rule
// In Business Rule (ES5 only!)
;(function executeRule(current, previous) {
// Check if escalation occurred
if (current.escalation > previous.escalation) {
// Fire custom event
gs.eventQueue(
"x_myapp.incident.escalated",
current,
current.escalation.getDisplayValue(), // parm1
current.assigned_to.name, // parm2
)
}
})(current, previous)
Notification: Escalation Alert
├── When to send
│ ├── Send when: Event is fired
│ └── Event name: x_myapp.incident.escalated
├── Who will receive
│ └── Users/Groups: Escalation Managers
└── What it will contain
├── Subject: Escalation: ${number} - ${event.parm1}
└── Message: Incident escalated. Assigned to: ${event.parm2}
| 类型 | 描述 | 示例 |
|---|---|---|
| 用户 | 特定用户 | ${assigned_to}, ${caller_id} |
| 组 | 用户组 | Service Desk, CAB |
| 组经理 | 组经理字段 | ${assignment_group.manager} |
| 事件参数 1/2 | 来自事件参数 | ${event.parm1} |
| 其他收件人 | 电子邮件地址 | 外部电子邮件 |
// Recipient Script (ES5 only!)
// Returns comma-separated list of emails or sys_ids
;(function getRecipients(current, event) {
var recipients = []
// Add the caller
if (!current.caller_id.nil()) {
recipients.push(current.caller_id.email.toString())
}
// Add VIP's manager
var caller = current.caller_id.getRefRecord()
if (caller.vip == true && !caller.manager.nil()) {
recipients.push(caller.manager.email.toString())
}
return recipients.join(",")
})(current, event)
用于多个匹配通知的优先级系统:
| 权重 | 用例 |
|---|---|
| 0 | 默认优先级 |
| 1-99 | 更高优先级(权重越低 = 优先级越高) |
| -1 到 -99 | 较低优先级 |
| 100+ | 很少使用 |
// Only highest weight notification sends if "Exclude subscribers" checked
// Weight 0 notification beats Weight 10 notification
Notification: Daily Incident Summary
├── Digest: Checked
├── Digest Interval: Daily
├── Digest Time: 08:00
└── Content: Summary of all incidents
// Summarize digest records
;(function runMailScript(current, template, email, email_action, event) {
var count = 0
var html = '<table border="1" cellpadding="5">'
html += "<tr><th>Number</th><th>Description</th><th>Priority</th></tr>"
// 'current' is a GlideRecord with all digest records
while (current.next()) {
count++
html += "<tr>"
html += "<td>" + current.number + "</td>"
html += "<td>" + current.short_description + "</td>"
html += "<td>" + current.priority.getDisplayValue() + "</td>"
html += "</tr>"
}
html += "</table>"
html += "<p>Total: " + count + " incidents</p>"
template.print(html)
})(current, template, email, email_action, event)
// System Properties for email
glide.email.smtp.active // Enable/disable outbound email
glide.email.smtp.host // SMTP server
glide.email.smtp.port // SMTP port (usually 25 or 587)
glide.email.default.sender // Default from address
glide.email.test.user // Test recipient (all emails go here)
// Background Script to test notification (ES5 only!)
var gr = new GlideRecord("incident")
gr.get("sys_id_here")
// Fire event to trigger notification
gs.eventQueue("incident.assigned", gr, gr.assigned_to.getDisplayValue(), gs.getUserDisplayName())
gs.info("Event queued for incident: " + gr.number)
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 邮件未发送 | 事件未触发 | 检查业务规则是否触发事件 |
| 收件人错误 | 脚本错误 | 调试收件人脚本 |
| 内容缺失 | 模板变量错误 | 检查字段名称 |
| 重复邮件 | 多个通知 | 检查权重和条件 |
| 邮件延迟 | 邮件作业计划 | 检查 sysauto_script |
每周安装
63
仓库
GitHub 星标
58
首次出现
2026年1月22日
安全审计
安装于
opencode57
github-copilot55
gemini-cli55
claude-code55
cursor55
codex55
ServiceNow notifications are triggered by events and send emails, SMS, or other alerts to users.
| Component | Table | Purpose |
|---|---|---|
| Notification | sysevent_email_action | Main notification record |
| Email Template | sysevent_email_template | Reusable email layouts |
| Event | sysevent | Triggers notifications |
| Event Registration | sysevent_register | Defines custom events |
| Email Script | sys_script_email | Dynamic content scripts |
Notification: Incident Assigned
├── When to send
│ ├── Table: incident
│ ├── When: Record inserted or updated
│ └── Conditions: assigned_to changes AND is not empty
├── Who will receive
│ ├── Users: ${assigned_to}
│ └── Groups: (optional)
├── What it will contain
│ ├── Subject: Incident ${number} assigned to you
│ ├── Message: HTML body with ${field} references
│ └── Email Template: (optional)
└── Advanced
├── Weight: 0 (priority)
└── Send to event creator: false
// Simple field conditions
assigned_to CHANGES
priority = 1
state = 6 // Resolved
// Script condition (ES5 only!)
// Returns true to send, false to skip
(function() {
// Only notify for VIP callers
var caller = current.caller_id.getRefRecord();
return caller.vip == true;
})()
// Advanced condition with multiple checks
(function() {
// Don't notify on bulk updates
if (current.sys_mod_count > 100) return false;
// Only for production CIs
var ci = current.cmdb_ci.getRefRecord();
return ci.used_for == 'Production';
})()
<!-- Field references -->
${number}
<!-- Direct field value -->
${caller_id.name}
<!-- Dot-walked reference -->
${opened_at.display_value}
<!-- Display value -->
<!-- Special variables -->
${URI}
<!-- Link to record -->
${URI_REF}
<!-- Reference link -->
${mail_script:script_name}
<!-- Include email script -->
<!-- Conditional content -->
${mailto:assigned_to}
<!-- Mailto link -->
<html>
<body style="font-family: Arial, sans-serif;">
<h2>Incident ${number} - ${short_description}</h2>
<table border="0" cellpadding="5">
<tr>
<td><strong>Priority:</strong></td>
<td>${priority}</td>
</tr>
<tr>
<td><strong>Caller:</strong></td>
<td>${caller_id.name}</td>
</tr>
<tr>
<td><strong>Assigned to:</strong></td>
<td>${assigned_to.name}</td>
</tr>
<tr>
<td><strong>Description:</strong></td>
<td>${description}</td>
</tr>
</table>
<p>
<a href="${URI}">View Incident</a>
</p>
${mail_script:incident_history}
</body>
</html>
// Email Script: incident_history
// Table: incident
// Script (ES5 only!):
;(function runMailScript(current, template, email, email_action, event) {
// Build activity history
var html = "<h3>Recent Activity</h3><ul>"
var history = new GlideRecord("sys_journal_field")
history.addQuery("element_id", current.sys_id)
history.addQuery("name", "incident")
history.orderByDesc("sys_created_on")
history.setLimit(5)
history.query()
while (history.next()) {
html += "<li><strong>" + history.sys_created_on.getDisplayValue() + "</strong>: "
html += history.value.substring(0, 200) + "</li>"
}
html += "</ul>"
template.print(html)
})(current, template, email, email_action, event)
// Add attachments from the record to the email
;(function runMailScript(current, template, email, email_action, event) {
var gr = new GlideRecord("sys_attachment")
gr.addQuery("table_sys_id", current.sys_id)
gr.addQuery("table_name", "incident")
gr.query()
while (gr.next()) {
email.addAttachment(gr)
}
})(current, template, email, email_action, event)
// Email Script to add CC recipients dynamically
;(function runMailScript(current, template, email, email_action, event) {
// Add all group members as CC
var group = current.assignment_group
if (!group.nil()) {
var members = new GlideRecord("sys_user_grmember")
members.addQuery("group", group)
members.query()
while (members.next()) {
var user = members.user.getRefRecord()
if (user.email) {
email.addAddress("cc", user.email, user.name)
}
}
}
})(current, template, email, email_action, event)
// Event Registration
// Name: x_myapp.incident.escalated
// Table: incident
// Description: Fired when incident is escalated to management
// Fired by: Business Rule
// In Business Rule (ES5 only!)
;(function executeRule(current, previous) {
// Check if escalation occurred
if (current.escalation > previous.escalation) {
// Fire custom event
gs.eventQueue(
"x_myapp.incident.escalated",
current,
current.escalation.getDisplayValue(), // parm1
current.assigned_to.name, // parm2
)
}
})(current, previous)
Notification: Escalation Alert
├── When to send
│ ├── Send when: Event is fired
│ └── Event name: x_myapp.incident.escalated
├── Who will receive
│ └── Users/Groups: Escalation Managers
└── What it will contain
├── Subject: Escalation: ${number} - ${event.parm1}
└── Message: Incident escalated. Assigned to: ${event.parm2}
| Type | Description | Example |
|---|---|---|
| Users | Specific users | ${assigned_to}, ${caller_id} |
| Groups | User groups | Service Desk, CAB |
| Group Managers | Group manager field | ${assignment_group.manager} |
| Event Parm 1/2 | From event parameters | ${event.parm1} |
| Additional Recipients | Email addresses | External emails |
// Recipient Script (ES5 only!)
// Returns comma-separated list of emails or sys_ids
;(function getRecipients(current, event) {
var recipients = []
// Add the caller
if (!current.caller_id.nil()) {
recipients.push(current.caller_id.email.toString())
}
// Add VIP's manager
var caller = current.caller_id.getRefRecord()
if (caller.vip == true && !caller.manager.nil()) {
recipients.push(caller.manager.email.toString())
}
return recipients.join(",")
})(current, event)
Priority system for multiple matching notifications:
| Weight | Use Case |
|---|---|
| 0 | Default priority |
| 1-99 | Higher priority (lower weight = higher priority) |
| -1 to -99 | Lower priority |
| 100+ | Rarely used |
// Only highest weight notification sends if "Exclude subscribers" checked
// Weight 0 notification beats Weight 10 notification
Notification: Daily Incident Summary
├── Digest: Checked
├── Digest Interval: Daily
├── Digest Time: 08:00
└── Content: Summary of all incidents
// Summarize digest records
;(function runMailScript(current, template, email, email_action, event) {
var count = 0
var html = '<table border="1" cellpadding="5">'
html += "<tr><th>Number</th><th>Description</th><th>Priority</th></tr>"
// 'current' is a GlideRecord with all digest records
while (current.next()) {
count++
html += "<tr>"
html += "<td>" + current.number + "</td>"
html += "<td>" + current.short_description + "</td>"
html += "<td>" + current.priority.getDisplayValue() + "</td>"
html += "</tr>"
}
html += "</table>"
html += "<p>Total: " + count + " incidents</p>"
template.print(html)
})(current, template, email, email_action, event)
// System Properties for email
glide.email.smtp.active // Enable/disable outbound email
glide.email.smtp.host // SMTP server
glide.email.smtp.port // SMTP port (usually 25 or 587)
glide.email.default.sender // Default from address
glide.email.test.user // Test recipient (all emails go here)
// Background Script to test notification (ES5 only!)
var gr = new GlideRecord("incident")
gr.get("sys_id_here")
// Fire event to trigger notification
gs.eventQueue("incident.assigned", gr, gr.assigned_to.getDisplayValue(), gs.getUserDisplayName())
gs.info("Event queued for incident: " + gr.number)
| Issue | Cause | Solution |
|---|---|---|
| Email not sent | Event not fired | Check business rule fires event |
| Wrong recipients | Script error | Debug recipient script |
| Missing content | Template variable wrong | Check field names |
| Duplicate emails | Multiple notifications | Check weights and conditions |
| Delayed emails | Email job schedule | Check sysauto_script |
Weekly Installs
63
Repository
GitHub Stars
58
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode57
github-copilot55
gemini-cli55
claude-code55
cursor55
codex55
GitHub Actions 官方文档查询助手 - 精准解答 CI/CD 工作流问题
47,200 周安装