Files
matrix-docker-ansible-deploy/roles/custom/matrix-bot-meowlnir/templates/bin/meowlnir-create-management-room.j2
T
Slavi PantaleevandClaude Fable 5 74e50df931 Add request timeouts to matrix-bot-meowlnir helper scripts
Without a timeout, a request against an API which accepts connections
but never answers (e.g. Meowlnir stuck retrying against a homeserver
that rejects its appservice token) hangs the playbook forever instead
of failing with a usable error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 08:57:56 +03:00

92 lines
3.3 KiB
Django/Jinja
Executable File

#!/bin/sh
# Creates a management room for a Meowlnir bot, with the given users able to command the bot there.
#
# The room is created by the bot itself, impersonated through the appservice token, so no human account's credentials are needed.
#
# The `trusted_private_chat` preset is what gives the invited users their standing, and it does the right thing on both old and new room versions: on rooms supporting MSC4289 every invitee becomes an additional creator, and on older ones each is given power level 100.
# Either way there is nothing for us to adjust afterwards.
#
# Usage: meowlnir-create-management-room <bot_localpart> <initial_manager_mxid>...
#
# Prints the created room's ID on success.
set -eu
CONFIG_FILE='{{ matrix_bot_meowlnir_config_path }}/config.yaml'
CONTAINER_NAME='matrix-bot-meowlnir'
HOMESERVER_ADDRESS='{{ matrix_bot_meowlnir_config_homeserver_address }}'
HOMESERVER_DOMAIN='{{ matrix_bot_meowlnir_config_homeserver_domain }}'
ROOM_NAME='{{ matrix_bot_meowlnir_management_room_name }}'
ROOM_TOPIC='{{ matrix_bot_meowlnir_management_room_topic | trim }}'
ENCRYPTED='{{ 'true' if matrix_bot_meowlnir_config_encryption_enable else 'false' }}'
REQUEST_TIMEOUT='{{ matrix_bot_meowlnir_api_request_timeout_seconds }}'
if [ $# -lt 2 ]; then
echo "Usage: $(basename "$0") <bot_localpart> <initial_manager_mxid>..." >&2
exit 2
fi
bot_localpart="$1"
shift
bot_mxid="@$bot_localpart:$HOMESERVER_DOMAIN"
as_token="$(awk '$1 == "as_token:" { print $2; exit }' "$CONFIG_FILE" | sed 's/^"//; s/"$//')"
if [ -z "$as_token" ]; then
echo "Could not read as_token from $CONFIG_FILE" >&2
exit 1
fi
urlencode() {
printf '%s' "$1" | sed 's/%/%25/g; s/!/%21/g; s/:/%3A/g; s/@/%40/g; s/\$/%24/g; s/\//%2F/g'
}
jq_run() {
{{ devture_systemd_docker_base_host_command_docker }} exec -i "$CONTAINER_NAME" jq "$@"
}
if [ "$ENCRYPTED" = 'true' ]; then
initial_state='[{"type": "m.room.encryption", "state_key": "", "content": {"algorithm": "m.megolm.v1.aes-sha2"}}]'
else
initial_state='[]'
fi
# Matrix user IDs cannot contain newlines, so splitting on them is safe here.
invitees="$(printf '%s\n' "$@" | jq_run -R -s 'split("\n") | map(select(length > 0))')"
create_body="$(jq_run -n \
--arg name "$ROOM_NAME" \
--arg topic "$ROOM_TOPIC" \
--argjson invitees "$invitees" \
--argjson initial_state "$initial_state" \
'{preset: "trusted_private_chat", name: $name, topic: $topic, invite: $invitees, initial_state: $initial_state}')"
user_id_param="$(urlencode "$bot_mxid")"
# Runs curl inside the container, because the homeserver is only reachable over the container network.
# Prints the body, with the HTTP status code on the final line.
response="$({{ devture_systemd_docker_base_host_command_docker }} exec "$CONTAINER_NAME" \
curl -sS --max-time "$REQUEST_TIMEOUT" -X POST \
-H "Authorization: Bearer $as_token" \
-H 'Content-Type: application/json' \
-d "$create_body" \
-w '\n%{http_code}' \
"$HOMESERVER_ADDRESS/_matrix/client/v3/createRoom?user_id=$user_id_param")"
status="$(printf '%s\n' "$response" | tail -n 1)"
if [ "$status" != '200' ]; then
echo "Creating the management room failed with HTTP $status:" >&2
printf '%s\n' "$response" | sed '$d' >&2
exit 1
fi
room_id="$(printf '%s\n' "$response" | sed '$d' | jq_run -r '.room_id')"
if [ -z "$room_id" ] || [ "$room_id" = 'null' ]; then
echo 'The homeserver did not return a room ID' >&2
exit 1
fi
printf '%s\n' "$room_id"