-
Notifications
You must be signed in to change notification settings - Fork 19
Matrix Notification Plugin #106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| # Matrix Notification Plugin | ||
|
|
||
| This [BunkerWeb](https://www.bunkerweb.io/?utm_campaign=self&utm_source=github) plugin will automatically send attack notifications to a Matrix room of your choice. | ||
|
|
||
| # Table of contents | ||
|
|
||
| - [Matrix Notification Plugin](#matrix-notification-plugin) | ||
| - [Table of contents](#table-of-contents) | ||
| - [Prerequisites](#prerequisites) | ||
| - [Setup](#setup) | ||
| - [Docker](#docker) | ||
| - [Swarm](#swarm) | ||
| - [Kubernetes](#kubernetes) | ||
| - [Settings](#settings) | ||
| - [TODO](#todo) | ||
|
|
||
| # Prerequisites | ||
|
|
||
| Please read the [plugins section](https://docs.bunkerweb.io/latest/plugins/?utm_campaign=self&utm_source=github) of the BunkerWeb documentation first. | ||
|
|
||
| You will need: | ||
| - A Matrix server URL (e.g., `https://matrix.org`). | ||
| - A valid access token for the Matrix user you want to sent notifications from. | ||
| - A room ID where notifications will be sent to. The matrix user has to be Member of that room. | ||
|
|
||
| Please refer to your homeserver's docs if you need help setting these up. | ||
|
|
||
| # Setup | ||
|
|
||
| See the [plugins section](https://docs.bunkerweb.io/latest/plugins/?utm_campaign=self&utm_source=github) of the BunkerWeb documentation for the installation procedure depending on your integration. | ||
|
|
||
| There is no additional service setup required beyond configuring the plugin itself. | ||
|
|
||
| ## Docker | ||
|
|
||
| ```yaml | ||
| version: '3' | ||
|
|
||
| services: | ||
|
|
||
| bunkerweb: | ||
| image: bunkerity/bunkerweb:1.5.10 | ||
| ... | ||
| environment: | ||
| - USE_MATRIX=yes | ||
| - MATRIX_BASE_URL=https://matrix.org | ||
| - MATRIX_ROOM_ID=!yourRoomID:matrix.org | ||
| - MATRIX_ACCESS_TOKEN=your-access-token | ||
| ... | ||
| ``` | ||
|
Comment on lines
+34
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the examples to the 1.6 image series. Lines 42 and 60 still pin As per coding guidelines, Also applies to: 52-68 🤖 Prompt for AI Agents |
||
|
|
||
| ## Swarm | ||
|
|
||
| ```yaml | ||
| version: '3' | ||
|
|
||
| services: | ||
|
|
||
| mybunker: | ||
| image: bunkerity/bunkerweb:1.5.10 | ||
| .. | ||
| environment: | ||
| - USE_MATRIX=yes | ||
| - MATRIX_BASE_URL=https://matrix.org | ||
| - MATRIX_ROOM_ID=!yourRoomID:matrix.org | ||
| - MATRIX_ACCESS_TOKEN=your-access-token | ||
| ... | ||
| ``` | ||
|
|
||
| ## Kubernetes | ||
|
|
||
| ```yaml | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: Ingress | ||
| metadata: | ||
| name: ingress | ||
| annotations: | ||
| bunkerweb.io/USE_MATRIX: "yes" | ||
| bunkerweb.io/MATRIX_BASE_URL: "https://matrix.org" | ||
| bunkerweb.io/MATRIX_ROOM_ID: "!yourRoomID:matrix.org" | ||
| bunkerweb.io/MATRIX_ACCESS_TOKEN: "your-access-token" | ||
| ``` | ||
|
|
||
| # Settings | ||
|
|
||
| | Setting | Default | Context | Multiple | Description | | ||
| | -------------------- | ---------------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------- | | ||
| | `USE_MATRIX` | `no` | multisite | no | Enable sending alerts to a Matrix room. | | ||
| | `MATRIX_BASE_URL` | `https://matrix.org` | global | no | Base URL of the Matrix server. | | ||
| | `MATRIX_ROOM_ID` | `!yourRoomID:matrix.org` | global | no | Room ID of the Matrix room to send notifications to. | | ||
| | `MATRIX_ACCESS_TOKEN` | ` ` | global | no | Access token to authenticate with the Matrix server. | | ||
| | `MATRIX_ANONYMIZE_IP` | `no` | global | no | Mask the IP address in notifications. | | ||
| | `MATRIX_INCLUDE_HEADERS` | `no` | global | no | Include request headers in notifications. | | ||
|
Comment on lines
+86
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regenerate the settings table from This table already drifts from As per coding guidelines, 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,210 @@ | ||||||||||||||
| local cjson = require("cjson") | ||||||||||||||
| local class = require("middleclass") | ||||||||||||||
| local http = require("resty.http") | ||||||||||||||
| local plugin = require("bunkerweb.plugin") | ||||||||||||||
| local utils = require("bunkerweb.utils") | ||||||||||||||
| local matrix_utils = require("matrix.utils") | ||||||||||||||
|
|
||||||||||||||
| local matrix = class("matrix", plugin) | ||||||||||||||
|
|
||||||||||||||
| local ngx = ngx | ||||||||||||||
| local ngx_req = ngx.req | ||||||||||||||
| local ERR = ngx.ERR | ||||||||||||||
| local INFO = ngx.INFO | ||||||||||||||
| local ngx_timer = ngx.timer | ||||||||||||||
| local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR | ||||||||||||||
| local HTTP_OK = ngx.HTTP_OK | ||||||||||||||
| local http_new = http.new | ||||||||||||||
| local has_variable = utils.has_variable | ||||||||||||||
| local get_variable = utils.get_variable | ||||||||||||||
| local get_reason = utils.get_reason | ||||||||||||||
| local get_country = utils.get_country | ||||||||||||||
| local get_asn = utils.get_asn | ||||||||||||||
| local get_asn_org = matrix_utils.get_asn_org | ||||||||||||||
| local tostring = tostring | ||||||||||||||
| local encode = cjson.encode | ||||||||||||||
|
|
||||||||||||||
| function matrix:initialize(ctx) | ||||||||||||||
| -- Call parent initialize | ||||||||||||||
| plugin.initialize(self, "matrix", ctx) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| function matrix:log(bypass_use_matrix) | ||||||||||||||
| -- Check if matrix is enabled | ||||||||||||||
| if not bypass_use_matrix then | ||||||||||||||
| if self.variables["USE_MATRIX"] ~= "yes" then | ||||||||||||||
| return self:ret(true, "matrix plugin not enabled") | ||||||||||||||
| end | ||||||||||||||
| end | ||||||||||||||
| -- Check if request is denied | ||||||||||||||
| local reason, reason_data = get_reason(self.ctx) | ||||||||||||||
| if reason == nil then | ||||||||||||||
| return self:ret(true, "request not denied") | ||||||||||||||
| end | ||||||||||||||
| -- Compute data | ||||||||||||||
| local request_host = ngx.var.host or "unknown host" | ||||||||||||||
| local remote_addr = self.ctx.bw.remote_addr | ||||||||||||||
| local request_method = self.ctx.bw.request_method | ||||||||||||||
| local country, err = get_country(self.ctx.bw.remote_addr) | ||||||||||||||
| if not country then | ||||||||||||||
| elf.logger:log(ERR, "can't get Country of IP " .. remote_addr .. " : " .. err) | ||||||||||||||
|
Comment on lines
+48
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the logger typo in the country lookup fallback. Line 50 calls Suggested fix- elf.logger:log(ERR, "can't get Country of IP " .. remote_addr .. " : " .. err)
+ self.logger:log(ERR, "can't get Country of IP " .. remote_addr .. " : " .. err)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| country = "Country unknown" | ||||||||||||||
| else | ||||||||||||||
| country = tostring(country) | ||||||||||||||
| end | ||||||||||||||
| local asn, err = get_asn(remote_addr) | ||||||||||||||
| if not asn then | ||||||||||||||
| self.logger:log(ERR, "can't get ASN of IP " .. remote_addr .. " : " .. err) | ||||||||||||||
| asn = "ASN unknown" | ||||||||||||||
| else | ||||||||||||||
| asn = "ASN " .. tostring(asn) | ||||||||||||||
| end | ||||||||||||||
| local asn_org, err = get_asn_org(remote_addr) | ||||||||||||||
| if not asn_org then | ||||||||||||||
| self.logger:log(ERR, "can't get Organization of IP " .. remote_addr .. " : " .. err) | ||||||||||||||
| asn_org = "AS Organization unknown" | ||||||||||||||
| else | ||||||||||||||
| asn_org = tostring(asn_org) | ||||||||||||||
| end | ||||||||||||||
| local data = {} | ||||||||||||||
| data["formatted_body"] = "<p>Denied " .. request_method .. " from <b>" .. remote_addr .. "</b> (" .. country .. " • \"<i>" .. asn_org .. "</i>\" • " .. asn .. ") to " .. request_host .. self.ctx.bw.uri .. "<br>" | ||||||||||||||
| data["formatted_body"] = data["formatted_body"] .. "Reason <b>" .. reason .. "</b> (" .. encode(reason_data or {}) .. ").</p>" | ||||||||||||||
| data["body"] = "Denied " .. request_method .. " from " .. remote_addr .. " (" .. country .. " • \"" .. asn_org .. "\" • " .. asn .. ") to " .. request_host .. self.ctx.bw.uri .. "\n" | ||||||||||||||
| data["body"] = data["body"] .. "Reason " .. reason .. " (" .. encode(reason_data or {}) .. ")." | ||||||||||||||
| -- Add headers if enabled | ||||||||||||||
| if self.variables["MATRIX_INCLUDE_HEADERS"] == "yes" then | ||||||||||||||
| local headers, err = ngx_req.get_headers() | ||||||||||||||
| if not headers then | ||||||||||||||
| data["formatted_body"] = data["formatted_body"] .. "error while getting headers: " .. err | ||||||||||||||
| data["body"] = data["body"] .. "\n error while getting headers: " .. err | ||||||||||||||
| else | ||||||||||||||
| data["formatted_body"] = data["formatted_body"] .. "<table><tr><th>Header</th><th>Value</th></tr>" | ||||||||||||||
| data["body"] = data["body"] .. "\n\n" | ||||||||||||||
| for header, value in pairs(headers) do | ||||||||||||||
| data["formatted_body"] = data["formatted_body"] .. "<tr><td>" .. header .. "</td><td>" .. value .. "</td></tr>" | ||||||||||||||
| data["body"] = data["body"] .. header .. ": " .. value .. "\n" | ||||||||||||||
|
Comment on lines
+70
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escape HTML and flatten duplicate headers before concatenation. Lines 70–80 inject request fields directly into formatted_body without sanitisation. Request-derived fields (request_method, remote_addr, request_host, URI, header names and values) can contain HTML special characters. Repeated HTTP headers from As per coding guidelines: validate and sanitise all request-derived input. 🧰 Tools🪛 Luacheck (1.2.0)[warning] 76-76: shadowing definition of variable 'err' on line 62 (W421) 🤖 Prompt for AI Agents
Comment on lines
+75
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not forward raw request headers to Matrix. With As per coding guidelines, 🧰 Tools🪛 Luacheck (1.2.0)[warning] 76-76: shadowing definition of variable 'err' on line 62 (W421) 🤖 Prompt for AI Agents |
||||||||||||||
| end | ||||||||||||||
| data["formatted_body"] = data["formatted_body"] .. "</table>" | ||||||||||||||
| end | ||||||||||||||
| end | ||||||||||||||
| -- Anonymize IP if enabled | ||||||||||||||
| if self.variables["MATRIX_ANONYMIZE_IP"] == "yes" then | ||||||||||||||
| remote_addr = string.gsub(remote_addr, "%d+%.%d+$", "xxx.xxx") | ||||||||||||||
| data["formatted_body"] = string.gsub(data["formatted_body"], self.ctx.bw.remote_addr, remote_addr) | ||||||||||||||
| data["body"] = string.gsub(data["body"], self.ctx.bw.remote_addr, remote_addr) | ||||||||||||||
| end | ||||||||||||||
| -- Send request | ||||||||||||||
| local hdr, err = ngx_timer.at(0, self.send, self, data) | ||||||||||||||
| if not hdr then | ||||||||||||||
| return self:ret(true, "can't create report timer: " .. err) | ||||||||||||||
| end | ||||||||||||||
| return self:ret(true, "scheduled timer") | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| -- luacheck: ignore 212 | ||||||||||||||
| function matrix.send(premature, self, data) | ||||||||||||||
| local httpc, err = http_new() | ||||||||||||||
| if not httpc then | ||||||||||||||
| self.logger:log(ERR, "can't instantiate http object : " .. err) | ||||||||||||||
| end | ||||||||||||||
| -- Prapare data | ||||||||||||||
| local base_url = self.variables["MATRIX_BASE_URL"] | ||||||||||||||
| local access_token = self.variables["MATRIX_ACCESS_TOKEN"] | ||||||||||||||
| local room_id = self.variables["MATRIX_ROOM_ID"] | ||||||||||||||
| local txn_id = tostring(os.time()) | ||||||||||||||
| local url = string.format("%s/_matrix/client/r0/rooms/%s/send/m.room.message/%s", base_url, room_id, txn_id) | ||||||||||||||
|
Comment on lines
+111
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cd /workspace && find . -name "matrix.lua" -type fRepository: bunkerity/bunkerweb-plugins Length of output: 131 🏁 Script executed: head -n 200 matrix/matrix.lua | tail -n +100Repository: bunkerity/bunkerweb-plugins Length of output: 3351 🌐 Web query:
💡 Result: The Matrix Client-Server API uses client-generated transaction IDs (txnId) in the URL path of HTTP PUT requests to ensure idempotency. This prevents duplicate processing when clients retry requests due to network issues or timeouts. Key aspects of the specification: - Purpose: Distinguishes new requests from retransmissions, making requests idempotent. - Client behavior: Generate a unique txnId for each new request (recommended: v4 UUIDs or timestamp + monotonically increasing integer). Change txnId after each request completes. - Server behavior: If the txnId and full HTTP path match a previous request, treat as retransmission: return the identical HTTP status code and response body as the original (e.g., 200 OK with the original event_id for room send endpoints). Ignore the duplicate request. - Scope: Per device and per HTTP endpoint. Different endpoints (e.g., /rooms/{roomId}/send/{eventType}/{txnId} vs /sendToDevice/{eventType}/{txnId}) are distinct even with same txnId. Scoped to access token/device: re-login with new device creates new scope; token refresh retains scope. - POST endpoints: Some allow POST without txnId; PUT with txnId strongly recommended where optional. - Changes: Clarified in ~v1.7 (MSC via Citations:
Use a collision-resistant transaction ID for Matrix idempotency keys. The Matrix Client-Server API treats transaction IDs (txnId) as idempotency keys: when the server receives a request with the same txnId and endpoint path, it returns the cached response (same status code and body) rather than reprocessing. Using Generate a unique ID per send instead. Options include:
Lines 114 and 178 both require fixes. 🤖 Prompt for AI Agents |
||||||||||||||
| local message_data = { | ||||||||||||||
| msgtype = "m.text", | ||||||||||||||
| body = data["body"], | ||||||||||||||
| format = "org.matrix.custom.html", | ||||||||||||||
| formatted_body = data["formatted_body"] | ||||||||||||||
| } | ||||||||||||||
| local post_data = cjson.encode(message_data) | ||||||||||||||
| -- Send request | ||||||||||||||
| local res, err_http = httpc:request_uri(url, { | ||||||||||||||
| method = "PUT", | ||||||||||||||
| body = post_data, | ||||||||||||||
| headers = { | ||||||||||||||
| ["Content-Type"] = "application/json", | ||||||||||||||
| ["Authorization"] = "Bearer " .. access_token -- Access Token im Header | ||||||||||||||
| } | ||||||||||||||
| }) | ||||||||||||||
|
Comment on lines
+124
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Locate the file and check its existence
find . -name "matrix.lua" -type fRepository: bunkerity/bunkerweb-plugins Length of output: 90 🏁 Script executed: # Read the relevant sections of the file
if [ -f "matrix/matrix.lua" ]; then
echo "=== Lines 124-131 ===" && sed -n '124,131p' matrix/matrix.lua
echo ""
echo "=== Lines 190-197 ===" && sed -n '190,197p' matrix/matrix.lua
echo ""
echo "=== Full file context (first check size) ===" && wc -l matrix/matrix.lua
fiRepository: bunkerity/bunkerweb-plugins Length of output: 662 🏁 Script executed: # Search for all httpc:request_uri calls to understand timeout usage
rg "httpc:request_uri" -A 5 matrix/Repository: bunkerity/bunkerweb-plugins Length of output: 723 🏁 Script executed: # Check how other plugins (coraza, virustotal, clamav) handle HTTP timeouts
rg "set_timeout|timeout" -A 2 -B 2 --type lua | head -100Repository: bunkerity/bunkerweb-plugins Length of output: 1683 🏁 Script executed: # Search for resty.http usage in other plugins to see timeout patterns
rg "httpc.*=" -A 10 --type lua | grep -A 10 "resty.http" | head -80Repository: bunkerity/bunkerweb-plugins Length of output: 53 🏁 Script executed: # Look for timeout patterns in virustotal.lua and coraza.lua specifically
for file in virustotal.lua coraza.lua clamav.lua; do
echo "=== $file ==="
if [ -f "*/$file" ]; then
find . -name "$file" -exec cat {} \; | grep -A 5 -B 5 "timeout\|request_uri" | head -50
fi
doneRepository: bunkerity/bunkerweb-plugins Length of output: 131 🏁 Script executed: # Search for httpc initialization and set_timeout calls
rg "set_timeout|connect_timeout|send_timeout|read_timeout" --type luaRepository: bunkerity/bunkerweb-plugins Length of output: 496 Add explicit HTTP timeouts to Matrix requests. Both 🤖 Prompt for AI Agents |
||||||||||||||
| httpc:close() | ||||||||||||||
| if not res then | ||||||||||||||
| self.logger:log(ERR, "error while sending request : " .. err_http) | ||||||||||||||
| end | ||||||||||||||
| if res.status < 200 or res.status > 299 then | ||||||||||||||
|
Comment on lines
+106
to
+136
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return immediately after HTTP client/request failures. In both send paths, if Also applies to: 185-203 🤖 Prompt for AI Agents |
||||||||||||||
| self.logger:log(ERR, "request returned status " .. tostring(res.status)) | ||||||||||||||
| return | ||||||||||||||
| end | ||||||||||||||
| self.logger:log(INFO, "request sent to matrix") | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| function matrix:log_default() | ||||||||||||||
| -- Check if matrix is activated | ||||||||||||||
| local check, err = has_variable("USE_MATRIX", "yes") | ||||||||||||||
| if check == nil then | ||||||||||||||
| return self:ret(false, "error while checking variable USE_MATRIX (" .. err .. ")") | ||||||||||||||
| end | ||||||||||||||
| if not check then | ||||||||||||||
| return self:ret(true, "matrix plugin not enabled") | ||||||||||||||
| end | ||||||||||||||
| -- Check if default server is disabled | ||||||||||||||
| check, err = get_variable("DISABLE_DEFAULT_SERVER", false) | ||||||||||||||
| if check == nil then | ||||||||||||||
| return self:ret(false, "error while getting variable DISABLE_DEFAULT_SERVER (" .. err .. ")") | ||||||||||||||
| end | ||||||||||||||
| if check ~= "yes" then | ||||||||||||||
| return self:ret(true, "default server not disabled") | ||||||||||||||
| end | ||||||||||||||
| -- Call log method | ||||||||||||||
| return self:log(true) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| function matrix:api() | ||||||||||||||
| if self.ctx.bw.uri == "/matrix/ping" and self.ctx.bw.request_method == "POST" then | ||||||||||||||
| -- Check matrix connection | ||||||||||||||
| local check, err = has_variable("USE_MATRIX", "yes") | ||||||||||||||
| if check == nil then | ||||||||||||||
| return self:ret(true, "error while checking variable USE_MATRIX (" .. err .. ")") | ||||||||||||||
| end | ||||||||||||||
| if not check then | ||||||||||||||
| return self:ret(true, "matrix plugin not enabled") | ||||||||||||||
| end | ||||||||||||||
| -- Prepare data | ||||||||||||||
| local base_url = self.variables["MATRIX_BASE_URL"] | ||||||||||||||
| local access_token = self.variables["MATRIX_ACCESS_TOKEN"] | ||||||||||||||
| local room_id = self.variables["MATRIX_ROOM_ID"] | ||||||||||||||
| local txn_id = tostring(os.time()) | ||||||||||||||
| local url = string.format("%s/_matrix/client/r0/rooms/%s/send/m.room.message/%s", base_url, room_id, txn_id) | ||||||||||||||
| local message_data = { | ||||||||||||||
| msgtype = "m.text", | ||||||||||||||
| body = "Test message from bunkerweb." | ||||||||||||||
| } | ||||||||||||||
| -- Send request | ||||||||||||||
| local httpc | ||||||||||||||
| httpc, err = http_new() | ||||||||||||||
| if not httpc then | ||||||||||||||
| self.logger:log(ERR, "can't instantiate http object : " .. err) | ||||||||||||||
| end | ||||||||||||||
| local res, err_http = httpc:request_uri(url, { | ||||||||||||||
| method = "PUT", | ||||||||||||||
| headers = { | ||||||||||||||
| ["Content-Type"] = "application/json", | ||||||||||||||
| ["Authorization"] = "Bearer " .. access_token | ||||||||||||||
| }, | ||||||||||||||
| body = encode(message_data), | ||||||||||||||
| }) | ||||||||||||||
| httpc:close() | ||||||||||||||
| if not res then | ||||||||||||||
| self.logger:log(ERR, "error while sending request : " .. err_http) | ||||||||||||||
| end | ||||||||||||||
| if res.status < 200 or res.status > 299 then | ||||||||||||||
| return self:ret(true, "request returned status " .. tostring(res.status), HTTP_INTERNAL_SERVER_ERROR) | ||||||||||||||
| end | ||||||||||||||
| return self:ret(true, "request sent to matrix", HTTP_OK) | ||||||||||||||
| end | ||||||||||||||
| return self:ret(false, "success") | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| return matrix | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "id": "matrix", | ||
| "name": "Matrix", | ||
| "description": "Send alerts to a Matrix room via the Matrix API.", | ||
| "version": "1.1", | ||
| "stream": "yes", | ||
| "settings": { | ||
| "USE_MATRIX": { | ||
| "context": "multisite", | ||
| "default": "no", | ||
| "help": "Enable sending alerts to a Matrix room.", | ||
| "id": "use-matrix", | ||
| "label": "Use Matrix", | ||
| "regex": "^(yes|no)$", | ||
| "type": "check" | ||
| }, | ||
| "MATRIX_BASE_URL": { | ||
| "context": "global", | ||
| "default": "https://matrix.org", | ||
| "help": "Base URL of the Matrix server (e.g., https://matrix.org).", | ||
| "id": "matrix-base-url", | ||
| "label": "Matrix Base URL", | ||
| "regex": "^.*$", | ||
| "type": "text" | ||
| }, | ||
| "MATRIX_ROOM_ID": { | ||
| "context": "global", | ||
| "default": "!yourRoomID:matrix.org", | ||
| "help": "Room ID of the Matrix room to send notifications to.", | ||
| "id": "matrix-room-id", | ||
| "label": "Matrix Room ID", | ||
| "regex": "^.*$", | ||
| "type": "text" | ||
| }, | ||
| "MATRIX_ACCESS_TOKEN": { | ||
| "context": "global", | ||
| "default": "", | ||
| "help": "Access token to authenticate with the Matrix server.", | ||
| "id": "matrix-access-token", | ||
| "label": "Matrix Access Token", | ||
| "regex": "^.*$", | ||
| "type": "password" | ||
| }, | ||
| "MATRIX_ANONYMIZE_IP": { | ||
| "context": "global", | ||
| "default": "no", | ||
| "help": "Mask the IP address in notifications.", | ||
| "id": "matrix-anonymize-ip", | ||
| "label": "Anonymize IP", | ||
| "regex": "^(yes|no)$", | ||
| "type": "check" | ||
| }, | ||
| "MATRIX_INCLUDE_HEADERS": { | ||
| "context": "global", | ||
| "default": "no", | ||
| "help": "Include request headers in notifications.", | ||
| "id": "matrix-include-headers", | ||
| "label": "Include Headers", | ||
| "regex": "^(yes|no)$", | ||
| "type": "check" | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the stale
TODOentry from the table of contents.Lines 5-15 link to
#todo, but the document has no matching section.As per coding guidelines,
Documentation should be concise, accurate, and written in British English: Keep the structure clear with a sensible heading hierarchy.🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 15-15: Link fragments should be valid
(MD051, link-fragments)
🤖 Prompt for AI Agents