refactor: replace old bot code with Matrix infra configs and scripts

- Remove obsolete Python bot (Wordle, commands, callbacks, welcome)
- Add hookshot/ — all 11 webhook transformation functions + deploy.sh
- Add cinny/ — config.json and dev-update.sh (nightly dev branch build)
- Add landing/ — matrix.lotusguild.org landing page HTML
- Add systemd/ — livekit-server, draupnir, cinny cron unit files
- Add draupnir/ — production config (access token redacted)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 10:36:51 -04:00
parent e6b1030b04
commit 0e275d725e
31 changed files with 1148 additions and 5087 deletions

11
hookshot/bazarr.js Normal file
View File

@@ -0,0 +1,11 @@
var title = data.title || 'Bazarr';
var msg = data.message || data.body || '';
var type = (data.type || 'info').toLowerCase();
var emoji = type === 'success' ? '✅' : (type === 'warning' ? '⚠️' : (type === 'failure' ? '❌' : '📝'));
var lines = [emoji + ' ' + title];
var htmlParts = ['<b>' + emoji + ' ' + title + '</b>'];
if (msg) {
var msgLines = msg.split(/\r?\n/).filter(function(l){ return l.trim(); });
for (var i = 0; i < msgLines.length; i++) { lines.push(msgLines[i]); htmlParts.push(msgLines[i]); }
}
result = { version: 'v2', plain: lines.join('\n'), html: htmlParts.join('<br>'), msgtype: 'm.notice' };

54
hookshot/deploy.sh Normal file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Deploy hookshot transformation functions to Matrix room state.
# Each .js file in this directory maps to a webhook by the same name (case-sensitive).
#
# Usage:
# ./deploy.sh # deploy all hooks
# ./deploy.sh proxmox.js # deploy one hook
#
# Requirements:
# MATRIX_TOKEN - access token with power level >= 50 in the target room
# MATRIX_SERVER - homeserver URL (default: https://matrix.lotusguild.org)
# MATRIX_ROOM - room ID where hooks are registered
MATRIX_SERVER="${MATRIX_SERVER:-https://matrix.lotusguild.org}"
MATRIX_ROOM="${MATRIX_ROOM:-!GttT4QYd1wlGlkHU3qTmq_P3gbyYKKeSSN6R7TPcJHg}"
if [ -z "$MATRIX_TOKEN" ]; then
echo "Error: MATRIX_TOKEN is not set." >&2
exit 1
fi
DIR="$(cd "$(dirname "$0")" && pwd)"
deploy_hook() {
local file="$1"
local filename="$(basename "$file" .js)"
# Capitalize first letter to match state_key (e.g. proxmox -> Proxmox)
local state_key="$(echo "$filename" | sed 's/\b\(.\)/\u\1/g' | sed 's/-\(.\)/\u\1/g')"
local fn="$(cat "$file")"
local encoded_room="$(python3 -c "import urllib.parse; print(urllib.parse.quote('$MATRIX_ROOM'))")"
local encoded_key="$(python3 -c "import urllib.parse; print(urllib.parse.quote('$state_key'))")"
local response
response=$(curl -sf -X PUT \
"$MATRIX_SERVER/_matrix/client/v3/rooms/$encoded_room/state/uk.half-shot.matrix-hookshot.generic.hook/$encoded_key" \
-H "Authorization: Bearer $MATRIX_TOKEN" \
-H "Content-Type: application/json" \
--data-binary "$(python3 -c "import json; print(json.dumps({'name': '$state_key', 'transformationFunction': open('$file').read()}))")" \
2>&1)
if echo "$response" | python3 -c "import json,sys; d=json.load(sys.stdin); exit(0 if 'event_id' in d else 1)" 2>/dev/null; then
echo "$state_key"
else
echo "$state_key: $response"
fi
}
if [ -n "$1" ]; then
deploy_hook "$DIR/$1"
else
for f in "$DIR"/*.js; do
deploy_hook "$f"
done
fi

32
hookshot/grafana.js Normal file
View File

@@ -0,0 +1,32 @@
var status = data.status || 'unknown';
var emoji = status === 'firing' ? '🔴' : (status === 'resolved' ? '🟢' : '🟡');
var title = data.title || ('[' + status.toUpperCase() + '] Grafana Alert');
var alerts = data.alerts || [];
var lines = [emoji + ' ' + title];
var htmlParts = ['<b>' + emoji + ' ' + title + '</b>'];
for (var i = 0; i < alerts.length && i < 5; i++) {
var a = alerts[i];
var labels = a.labels || {};
var ann = a.annotations || {};
var name = labels.alertname || '';
var summary = ann.summary || ann.description || '';
var instance = labels.instance || labels.job || '';
var severity = labels.severity || '';
var aStatus = a.status || '';
var genURL = a.generatorURL || '';
var aEmoji = aStatus === 'resolved' ? '🟢' : '🔴';
if (alerts.length > 1) {
var alertLine = aEmoji + ' ' + (name || 'Alert') + (severity ? ' [' + severity + ']' : '') + (instance ? ' \u2014 ' + instance : '') + (summary ? ': ' + summary : '');
lines.push(alertLine);
htmlParts.push(alertLine + (genURL ? ' <a href="' + genURL + '">\u2197</a>' : ''));
} else {
if (name && name !== title) { lines.push('Alert: ' + name); htmlParts.push('Alert: ' + name); }
if (severity) { lines.push('Severity: ' + severity); htmlParts.push('Severity: ' + severity); }
if (instance) { lines.push('Instance: ' + instance); htmlParts.push('Instance: ' + instance); }
if (summary) { lines.push(summary); htmlParts.push(summary); }
if (genURL) { lines.push('View: ' + genURL); htmlParts.push('<a href="' + genURL + '">View in Grafana</a>'); }
}
}
if (alerts.length === 0 && data.message) { lines.push(data.message); htmlParts.push(data.message); }
if (alerts.length > 5) { var more = '(+' + (alerts.length - 5) + ' more alerts)'; lines.push(more); htmlParts.push('<i>' + more + '</i>'); }
result = { version: 'v2', plain: lines.join('\n'), html: htmlParts.join('<br>'), msgtype: 'm.notice' };

37
hookshot/lidarr.js Normal file
View File

@@ -0,0 +1,37 @@
var ev = data.eventType || 'Unknown';
if (ev === 'Test') {
result = { version: 'v2', plain: '🧪 Lidarr: Connection test successful', msgtype: 'm.notice' };
} else {
var artist = (data.artist && data.artist.name) || 'Unknown Artist';
var albums = data.albums || (data.album ? [data.album] : []);
var albumStr = albums.map(function(a){ return a.title || ''; }).filter(Boolean).join(', ');
var quality = (data.release && data.release.quality) || (data.trackFiles && data.trackFiles[0] && data.trackFiles[0].quality) || '';
var releaseGroup = (data.release && data.release.releaseGroup) || '';
var client = data.downloadClient || '';
var upgrade = data.isUpgrade ? ' \u2191upgrade' : '';
var healthMsg = data.message || '';
var healthWiki = data.wikiUrl || '';
var prevVer = data.previousVersion || '';
var newVer = data.newVersion || '';
var emojiMap = { 'Grab':'📥','Download':'✅','Rename':'✏️','ArtistAdd':'','ArtistDelete':'🗑️','AlbumAdd':'','AlbumDelete':'🗑️','TrackFileDelete':'🗑️','HealthIssue':'⚠️','HealthRestored':'💚','ApplicationUpdate':'🔄' };
var emoji = emojiMap[ev] || '🎵';
var plain, html;
if (ev === 'HealthIssue' || ev === 'HealthRestored') {
plain = emoji + ' Lidarr ' + ev + ': ' + healthMsg + (healthWiki ? '\n' + healthWiki : '');
html = '<b>' + emoji + ' Lidarr ' + ev + '</b>: ' + healthMsg + (healthWiki ? '<br><a href="' + healthWiki + '">Wiki</a>' : '');
} else if (ev === 'ApplicationUpdate') {
plain = emoji + ' Lidarr updated: ' + prevVer + ' \u2192 ' + newVer;
html = '<b>' + emoji + ' Lidarr updated</b>: ' + prevVer + ' \u2192 ' + newVer;
} else if (ev === 'ArtistAdd' || ev === 'ArtistDelete') {
plain = emoji + ' Lidarr ' + ev + ': ' + artist;
html = '<b>' + emoji + ' Lidarr ' + ev + '</b>: ' + artist;
} else {
var albumPart = albumStr ? ' \u2014 ' + albumStr : '';
var qualPart = quality ? ' [' + quality + ']' : '';
var groupPart = releaseGroup ? ' {' + releaseGroup + '}' : '';
var clientPart = client ? ' via ' + client : '';
plain = emoji + ' Lidarr ' + ev + ': ' + artist + albumPart + qualPart + groupPart + upgrade + clientPart;
html = '<b>' + emoji + ' Lidarr ' + ev + '</b>: ' + artist + (albumStr ? ' \u2014 <i>' + albumStr + '</i>' : '') + qualPart + groupPart + upgrade + clientPart;
}
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.notice' };
}

21
hookshot/owncast.js Normal file
View File

@@ -0,0 +1,21 @@
var evtype = data.type || 'EVENT';
var ed = data.eventData || {};
var streamName = ed.name || ed.streamerName || '';
var title = ed.streamTitle || ed.title || '';
var viewers = ed.viewerCount !== undefined ? String(ed.viewerCount) : (ed.viewers !== undefined ? String(ed.viewers) : '');
var url = ed.externalURL || ed.url || ed.serverURL || '';
var chatUser = (ed.user && (ed.user.displayName || ed.user.username)) || '';
var chatMsg = ed.body || '';
var emoji, label;
if (evtype === 'STREAM_STARTED') { emoji = '🔴'; label = 'Now Live'; }
else if (evtype === 'STREAM_STOPPED') { emoji = '⚫'; label = 'Stream Ended'; }
else if (evtype === 'USER_JOINED') { emoji = '👤'; label = 'Viewer Joined'; }
else if (evtype === 'CHAT') { emoji = '💬'; label = 'Chat'; }
else { emoji = '📡'; label = evtype.replace(/_/g, ' '); }
var lines = [emoji + ' ' + label + (streamName ? ' \u2014 ' + streamName : '')];
var htmlParts = ['<b>' + emoji + ' ' + label + '</b>' + (streamName ? ': ' + streamName : '')];
if (title) { lines.push(title); htmlParts.push('<i>' + title + '</i>'); }
if (viewers) { lines.push(viewers + ' viewers'); htmlParts.push(viewers + ' viewers'); }
if (chatUser && chatMsg) { lines.push(chatUser + ': ' + chatMsg); htmlParts.push('<b>' + chatUser + '</b>: ' + chatMsg); }
if (url && evtype === 'STREAM_STARTED') { lines.push(url); htmlParts.push('<a href="' + url + '">' + url + '</a>'); }
result = { version: 'v2', plain: lines.join('\n'), html: htmlParts.join('<br>'), msgtype: 'm.notice' };

18
hookshot/proxmox.js Normal file
View File

@@ -0,0 +1,18 @@
var embed = (data.embeds && data.embeds[0]) || {};
var title = embed.title || 'Proxmox Notification';
var description = embed.description || '';
var fields = embed.fields || [];
var fieldMap = {};
fields.forEach(function(f) { fieldMap[f.name] = f.value; });
var severity = (fieldMap['Severity'] || 'info').toLowerCase();
var node = fieldMap['Node'] || '';
var type = fieldMap['Type'] || '';
var vmid = fieldMap['VM/CT ID'] || '';
var emoji = severity === 'error' ? '🔴' : (severity === 'warning' ? '🟡' : '');
var lines = [emoji + ' ' + title];
var htmlParts = ['<b>' + emoji + ' ' + title + '</b>'];
if (node) { lines.push('\uD83D\uDDA5\uFE0F Node: ' + node); htmlParts.push('\uD83D\uDDA5\uFE0F Node: <b>' + node + '</b>'); }
if (type) { lines.push('\uD83D\uDCCB Type: ' + type); htmlParts.push('\uD83D\uDCCB Type: ' + type); }
if (vmid && vmid !== 'N/A') { lines.push('\uD83D\uDD32 VM/CT: ' + vmid); htmlParts.push('\uD83D\uDD32 VM/CT: ' + vmid); }
if (description) { lines.push(description); htmlParts.push(description); }
result = { version: 'v2', plain: lines.join('\n'), html: htmlParts.join('<br>'), msgtype: 'm.notice' };

35
hookshot/radarr.js Normal file
View File

@@ -0,0 +1,35 @@
var ev = data.eventType || 'Unknown';
if (ev === 'Test') {
result = { version: 'v2', plain: '🧪 Radarr: Connection test successful', msgtype: 'm.notice' };
} else {
var m = data.movie || {};
var movie = m.title ? m.title + (m.year ? ' (' + m.year + ')' : '') : 'Unknown Movie';
var quality = (data.release && data.release.quality) || (data.movieFile && data.movieFile.quality) || '';
var releaseGroup = (data.release && data.release.releaseGroup) || (data.movieFile && data.movieFile.releaseGroup) || '';
var client = data.downloadClient || '';
var upgrade = data.isUpgrade ? ' \u2191upgrade' : '';
var healthMsg = data.message || '';
var healthWiki = data.wikiUrl || '';
var prevVer = data.previousVersion || '';
var newVer = data.newVersion || '';
var emojiMap = { 'Grab':'📥','Download':'✅','Rename':'✏️','MovieAdded':'','MovieDelete':'🗑️','MovieFileDelete':'🗑️','HealthIssue':'⚠️','HealthRestored':'💚','ApplicationUpdate':'🔄','ManualInteractionRequired':'🔔' };
var emoji = emojiMap[ev] || '🎬';
var plain, html;
if (ev === 'HealthIssue' || ev === 'HealthRestored') {
plain = emoji + ' Radarr ' + ev + ': ' + healthMsg + (healthWiki ? '\n' + healthWiki : '');
html = '<b>' + emoji + ' Radarr ' + ev + '</b>: ' + healthMsg + (healthWiki ? '<br><a href="' + healthWiki + '">Wiki</a>' : '');
} else if (ev === 'ApplicationUpdate') {
plain = emoji + ' Radarr updated: ' + prevVer + ' \u2192 ' + newVer;
html = '<b>' + emoji + ' Radarr updated</b>: ' + prevVer + ' \u2192 ' + newVer;
} else if (ev === 'MovieAdded' || ev === 'MovieDelete') {
plain = emoji + ' Radarr ' + ev + ': ' + movie;
html = '<b>' + emoji + ' Radarr ' + ev + '</b>: ' + movie;
} else {
var qualPart = quality ? ' [' + quality + ']' : '';
var groupPart = releaseGroup ? ' {' + releaseGroup + '}' : '';
var clientPart = client ? ' via ' + client : '';
plain = emoji + ' Radarr ' + ev + ': ' + movie + qualPart + groupPart + upgrade + clientPart;
html = '<b>' + emoji + ' Radarr ' + ev + '</b>: ' + movie + qualPart + groupPart + upgrade + clientPart;
}
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.notice' };
}

37
hookshot/readarr.js Normal file
View File

@@ -0,0 +1,37 @@
var ev = data.eventType || 'Unknown';
if (ev === 'Test') {
result = { version: 'v2', plain: '🧪 Readarr: Connection test successful', msgtype: 'm.notice' };
} else {
var author = (data.author && data.author.name) || 'Unknown Author';
var books = data.books || (data.book ? [data.book] : []);
var bookStr = books.map(function(b){ return b.title || ''; }).filter(Boolean).join(', ');
var quality = (data.release && data.release.quality) || (data.bookFile && data.bookFile.quality) || '';
var releaseGroup = (data.release && data.release.releaseGroup) || (data.bookFile && data.bookFile.releaseGroup) || '';
var client = data.downloadClient || '';
var upgrade = data.isUpgrade ? ' \u2191upgrade' : '';
var healthMsg = data.message || '';
var healthWiki = data.wikiUrl || '';
var prevVer = data.previousVersion || '';
var newVer = data.newVersion || '';
var emojiMap = { 'Grab':'📥','Download':'✅','Rename':'✏️','AuthorAdd':'','AuthorDelete':'🗑️','BookAdd':'','BookDelete':'🗑️','BookFileDelete':'🗑️','HealthIssue':'⚠️','HealthRestored':'💚','ApplicationUpdate':'🔄' };
var emoji = emojiMap[ev] || '📚';
var plain, html;
if (ev === 'HealthIssue' || ev === 'HealthRestored') {
plain = emoji + ' Readarr ' + ev + ': ' + healthMsg + (healthWiki ? '\n' + healthWiki : '');
html = '<b>' + emoji + ' Readarr ' + ev + '</b>: ' + healthMsg + (healthWiki ? '<br><a href="' + healthWiki + '">Wiki</a>' : '');
} else if (ev === 'ApplicationUpdate') {
plain = emoji + ' Readarr updated: ' + prevVer + ' \u2192 ' + newVer;
html = '<b>' + emoji + ' Readarr updated</b>: ' + prevVer + ' \u2192 ' + newVer;
} else if (ev === 'AuthorAdd' || ev === 'AuthorDelete') {
plain = emoji + ' Readarr ' + ev + ': ' + author;
html = '<b>' + emoji + ' Readarr ' + ev + '</b>: ' + author;
} else {
var titlePart = bookStr ? bookStr + ' by ' + author : author;
var qualPart = quality ? ' [' + quality + ']' : '';
var groupPart = releaseGroup ? ' {' + releaseGroup + '}' : '';
var clientPart = client ? ' via ' + client : '';
plain = emoji + ' Readarr ' + ev + ': ' + titlePart + qualPart + groupPart + upgrade + clientPart;
html = '<b>' + emoji + ' Readarr ' + ev + '</b>: ' + (bookStr ? '<i>' + bookStr + '</i> by ' + author : author) + qualPart + groupPart + upgrade + clientPart;
}
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.notice' };
}

29
hookshot/seerr.js Normal file
View File

@@ -0,0 +1,29 @@
var evtype = data.notification_type || data.event || 'Notification';
var subject = data.subject || evtype;
var message = data.message || '';
var media = data.media || {};
var request = data.request || {};
var issue = data.issue || {};
var comment = data.comment || {};
var user = request.requestedBy_username || request.requestedBy_email || (data.account && data.account.username) || '';
var mt = media.media_type || '';
var status4k = media.status4k || 0;
var typeEmoji = mt === 'movie' ? '🎬' : (mt === 'tv' ? '📺' : '📋');
var statusEmoji;
if (evtype.indexOf('PENDING') >= 0) statusEmoji = '🟡';
else if (evtype.indexOf('APPROVED') >= 0 || evtype.indexOf('AVAILABLE') >= 0) statusEmoji = '✅';
else if (evtype.indexOf('DECLINED') >= 0 || evtype.indexOf('FAILED') >= 0) statusEmoji = '❌';
else if (evtype.indexOf('ISSUE') >= 0) statusEmoji = '⚠️';
else if (evtype.indexOf('COMMENT') >= 0) statusEmoji = '💬';
else statusEmoji = typeEmoji;
var lines = [statusEmoji + ' ' + subject];
var htmlParts = ['<b>' + statusEmoji + ' ' + subject + '</b>'];
if (user) { lines.push('Requested by: ' + user); htmlParts.push('Requested by: ' + user); }
if (message && message !== subject) { lines.push(message); htmlParts.push(message); }
if (status4k) { lines.push('4K request'); htmlParts.push('4K request'); }
if (issue.issue_type) {
var issueLine = 'Issue: ' + issue.issue_type + (issue.message ? ' \u2014 ' + issue.message : '');
lines.push(issueLine); htmlParts.push(issueLine);
}
if (comment.message) { lines.push('Comment: ' + comment.message); htmlParts.push('Comment: ' + comment.message); }
result = { version: 'v2', plain: lines.join('\n'), html: htmlParts.join('<br>'), msgtype: 'm.notice' };

41
hookshot/sonarr.js Normal file
View File

@@ -0,0 +1,41 @@
var ev = data.eventType || 'Unknown';
if (ev === 'Test') {
result = { version: 'v2', plain: '🧪 Sonarr: Connection test successful', msgtype: 'm.notice' };
} else {
var series = (data.series && data.series.title) || 'Unknown Series';
var network = (data.series && data.series.network) || '';
var eps = data.episodes || [];
var epStrs = eps.map(function(ep) {
var s = 'S' + ('0'+(ep.seasonNumber||0)).slice(-2) + 'E' + ('0'+(ep.episodeNumber||0)).slice(-2);
return ep.title ? s + ' \u2013 ' + ep.title : s;
});
var quality = (data.release && data.release.quality) || (data.episodeFile && data.episodeFile.quality) || '';
var releaseGroup = (data.release && data.release.releaseGroup) || (data.episodeFile && data.episodeFile.releaseGroup) || '';
var client = data.downloadClient || '';
var upgrade = data.isUpgrade ? ' \u2191upgrade' : '';
var healthMsg = data.message || '';
var healthWiki = data.wikiUrl || '';
var prevVer = data.previousVersion || '';
var newVer = data.newVersion || '';
var emojiMap = { 'Grab':'📥','Download':'✅','Rename':'✏️','SeriesAdd':'','SeriesDelete':'🗑️','EpisodeFileDelete':'🗑️','HealthIssue':'⚠️','HealthRestored':'💚','ApplicationUpdate':'🔄','ManualInteractionRequired':'🔔' };
var emoji = emojiMap[ev] || '📺';
var plain, html;
if (ev === 'HealthIssue' || ev === 'HealthRestored') {
plain = emoji + ' Sonarr ' + ev + ': ' + healthMsg + (healthWiki ? '\n' + healthWiki : '');
html = '<b>' + emoji + ' Sonarr ' + ev + '</b>: ' + healthMsg + (healthWiki ? '<br><a href="' + healthWiki + '">Wiki</a>' : '');
} else if (ev === 'ApplicationUpdate') {
plain = emoji + ' Sonarr updated: ' + prevVer + ' \u2192 ' + newVer;
html = '<b>' + emoji + ' Sonarr updated</b>: ' + prevVer + ' \u2192 ' + newVer;
} else if (ev === 'SeriesAdd' || ev === 'SeriesDelete' || ev === 'Rename') {
plain = emoji + ' Sonarr ' + ev + ': ' + series + (network ? ' (' + network + ')' : '');
html = '<b>' + emoji + ' Sonarr ' + ev + '</b>: ' + series + (network ? ' (' + network + ')' : '');
} else {
var epPart = epStrs.length ? ' \u2014 ' + epStrs.join(', ') : '';
var qualPart = quality ? ' [' + quality + ']' : '';
var groupPart = releaseGroup ? ' {' + releaseGroup + '}' : '';
var clientPart = client ? ' via ' + client : '';
plain = emoji + ' Sonarr ' + ev + ': ' + series + epPart + qualPart + groupPart + upgrade + clientPart;
html = '<b>' + emoji + ' Sonarr ' + ev + '</b>: ' + series + (epStrs.length ? ' \u2014 <i>' + epStrs.join(', ') + '</i>' : '') + qualPart + groupPart + upgrade + clientPart;
}
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.notice' };
}

View File

@@ -0,0 +1,31 @@
var id = data.ticket_id || '?';
var title = data.title || 'Untitled';
var priority = parseInt(data.priority) || 4;
var category = data.category || 'General';
var type = data.type || 'Issue';
var source = data.source || '';
var url = data.url || '';
var trigger = data.trigger || 'manual';
var notifyUsers = data.notify_users || [];
var priorityEmojis = ['', '🔴', '🟠', '🔵', '🟢', '⚫'];
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;
var tLabel = trigger === 'automated' ? 'Automated' : 'Manual';
var meta = pLabel + ' \u00b7 ' + category + ' \u00b7 ' + type + (source ? ' \u00b7 ' + source : '') + ' [' + tLabel + ']';
var mentionPlain = '';
var mentionHtml = '';
if (notifyUsers.length > 0) {
var pParts = [], hParts = [];
for (var i = 0; i < notifyUsers.length; i++) {
var uid = notifyUsers[i];
var disp = uid.replace(/^@/, '').split(':')[0];
pParts.push(uid);
hParts.push('<a href="https://matrix.to/#/' + uid + '">' + disp + '</a>');
}
mentionPlain = '\n' + pParts.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;
result = { version: 'v2', plain: plain, html: html, msgtype: 'm.text' };

18
hookshot/uptime-kuma.js Normal file
View File

@@ -0,0 +1,18 @@
var monitor = data.monitor || {};
var hb = data.heartbeat || {};
var name = monitor.name || monitor.url || 'Monitor';
var url = monitor.url || '';
var status = hb.status;
var emoji = status === 1 ? '🟢' : (status === 0 ? '🔴' : '🟡');
var state = status === 1 ? 'UP' : (status === 0 ? 'DOWN' : 'Unknown');
var reason = hb.msg || '';
var ping = (hb.ping !== undefined && hb.ping !== null) ? hb.ping + 'ms' : '';
var duration = hb.duration ? hb.duration + 's' : '';
if (reason === 'OK' || reason === '200 - OK' || reason === '200') reason = '';
var lines = [emoji + ' ' + name + ' is ' + state];
var htmlParts = ['<b>' + emoji + ' ' + name + ' is ' + state + '</b>'];
if (url) { lines.push(url); htmlParts.push('<a href="' + url + '">' + url + '</a>'); }
if (reason) { lines.push('Reason: ' + reason); htmlParts.push('Reason: ' + reason); }
if (ping && status === 1) { lines.push('Ping: ' + ping); htmlParts.push('Ping: ' + ping); }
if (duration && status === 0) { lines.push('Down for: ' + duration); htmlParts.push('Down for: ' + duration); }
result = { version: 'v2', plain: lines.join('\n'), html: htmlParts.join('<br>'), msgtype: 'm.notice' };