feat(hookshot): expand tinker-tickets transform for all event types

Previously only handled ticket_created. Now handles:
- status_changed: shows old → new status with actor name
- comment_added: shows author + 200-char preview (opt-in via MATRIX_NOTIFY_COMMENTS)
- mention: targeted notification when @username used in comment
- assigned: shows new assignee + actor (opt-in via MATRIX_NOTIFY_ASSIGNMENTS)

Unknown events fall back to a debug line rather than being silently dropped.
Avatar updated to ticket emoji via Synapse admin API (mxc already applied live).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:50:30 -04:00
parent 7078c467d8
commit 69449803fd
+68 -19
View File
@@ -1,31 +1,80 @@
var id = data.ticket_id || '?'; // Tinker Tickets hookshot transform — handles all event types emitted by NotificationHelper
var title = data.title || 'Untitled'; // result must be assigned without var/let/const (QuickJS sandbox)
var priority = parseInt(data.priority) || 4;
var category = data.category || 'General'; var event = data.event || 'ticket_created';
var type = data.type || 'Issue'; var id = data.ticket_id || '?';
var source = data.source || ''; var title = data.title || 'Untitled';
var url = data.url || ''; var url = data.url || '';
var trigger = data.trigger || 'manual';
var notifyUsers = data.notify_users || []; var notifyUsers = data.notify_users || [];
var priorityEmojis = ['', '🔴', '🟠', '🔵', '🟢', '⚫'];
var priorityEmojis = ['', '\ud83d\udd34', '\ud83d\udfe0', '\ud83d\udd35', '\ud83d\udfe2', '\u26ab'];
var priorityLabels = ['', 'P1 Critical', 'P2 High', 'P3 Medium', 'P4 Low', 'P5 Info']; var priorityLabels = ['', 'P1 Critical', 'P2 High', 'P3 Medium', 'P4 Low', 'P5 Info'];
var emoji = (priority >= 1 && priority <= 5) ? priorityEmojis[priority] : '⚫';
var pLabel = (priority >= 1 && priority <= 5) ? priorityLabels[priority] : 'P' + priority; // Build mention strings
var tLabel = trigger === 'automated' ? 'Automated' : 'Manual';
var meta = pLabel + ' \u00b7 ' + category + ' \u00b7 ' + type + (source ? ' \u00b7 ' + source : '') + ' [' + tLabel + ']';
var mentionPlain = ''; var mentionPlain = '';
var mentionHtml = ''; var mentionHtml = '';
if (notifyUsers.length > 0) { if (notifyUsers.length > 0) {
var pParts = [], hParts = []; var pParts = [], hParts = [];
for (var i = 0; i < notifyUsers.length; i++) { for (var i = 0; i < notifyUsers.length; i++) {
var uid = notifyUsers[i]; var uid = notifyUsers[i];
var disp = uid.replace(/^@/, '').split(':')[0]; var disp = uid.replace(/^@/, '').split(':')[0];
pParts.push(uid); pParts.push(uid);
hParts.push('<a href="https://matrix.to/#/' + uid + '">' + disp + '</a>'); hParts.push('<a href="https://matrix.to/#/' + uid + '">' + disp + '</a>');
} }
mentionPlain = '\n' + pParts.join(' '); mentionPlain = '\n' + pParts.join(' ');
mentionHtml = '<br>' + hParts.join(' '); mentionHtml = '<br>' + hParts.join(' ');
} }
var plain = emoji + ' New Ticket #' + id + ': ' + title + '\n' + meta + (url ? '\n' + url : '') + mentionPlain;
var html = '<b>' + emoji + ' <a href="' + url + '">#' + id + '</a>: ' + title + '</b><br>' + meta + mentionHtml; var ticketLink = url ? ('<a href="' + url + '">#' + id + '</a>') : ('#' + id);
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.text' };
var plain, html;
if (event === 'ticket_created') {
var priority = parseInt(data.priority) || 4;
var category = data.category || 'General';
var type = data.type || 'Issue';
var source = data.source || '';
var trigger = data.trigger || 'manual';
var emoji = (priority >= 1 && priority <= 5) ? priorityEmojis[priority] : '\u26ab';
var pLabel = (priority >= 1 && priority <= 5) ? priorityLabels[priority] : 'P' + priority;
var tLabel = trigger === 'automated' ? 'Automated' : 'Manual';
var meta = pLabel + ' \u00b7 ' + category + ' \u00b7 ' + type + (source ? ' \u00b7 ' + source : '') + ' [' + tLabel + ']';
plain = emoji + ' New Ticket #' + id + ': ' + title + '\n' + meta + (url ? '\n' + url : '') + mentionPlain;
html = '<b>' + emoji + ' ' + ticketLink + ': ' + title + '</b><br>' + meta + mentionHtml;
} else if (event === 'status_changed') {
var oldStatus = data.old_status || '?';
var newStatus = data.new_status || '?';
var changedBy = data.changed_by ? (' by ' + data.changed_by) : '';
plain = '\ud83d\udd04 Status changed' + changedBy + ': ' + oldStatus + ' \u2192 ' + newStatus + '\n#' + id + ': ' + title + (url ? '\n' + url : '') + mentionPlain;
html = '\ud83d\udd04 Status changed' + changedBy + ': <b>' + oldStatus + ' \u2192 ' + newStatus + '</b><br>' + ticketLink + ': ' + title + mentionHtml;
} else if (event === 'comment_added') {
var author = data.author || 'Someone';
var preview = data.preview || '';
plain = '\ud83d\udcac ' + author + ' commented on #' + id + ': ' + title + (preview ? '\n"' + preview + '"' : '') + (url ? '\n' + url : '') + mentionPlain;
html = '\ud83d\udcac <b>' + author + '</b> commented on ' + ticketLink + ': ' + title + (preview ? '<br><i>"' + preview + '"</i>' : '') + mentionHtml;
} else if (event === 'mention') {
var author = data.author || 'Someone';
var preview = data.preview || '';
plain = '\ud83d\udce3 ' + author + ' mentioned you in #' + id + ': ' + title + (preview ? '\n"' + preview + '"' : '') + (url ? '\n' + url : '') + mentionPlain;
html = '\ud83d\udce3 <b>' + author + '</b> mentioned you in ' + ticketLink + ': ' + title + (preview ? '<br><i>"' + preview + '"</i>' : '') + mentionHtml;
} else if (event === 'assigned') {
var assignee = data.assignee || 'someone';
var changedBy = data.changed_by ? (' by ' + data.changed_by) : '';
plain = '\ud83d\udc64 Ticket #' + id + ' assigned to ' + assignee + changedBy + ': ' + title + (url ? '\n' + url : '') + mentionPlain;
html = '\ud83d\udc64 ' + ticketLink + ' assigned to <b>' + assignee + '</b>' + changedBy + ': ' + title + mentionHtml;
} else {
plain = '[tinker-tickets] event: ' + event + ' ticket #' + id;
html = '[tinker-tickets] event: <code>' + event + '</code> ticket #' + id;
}
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.text' };