#!/bin/sh # MM Shell bot # By 0xDEADCADE # Config MM="${HOME}/go/bin/mm" MMDIR="mm" CMDDIR="bin" HOMESERVER="https://matrix.example.org" USERNAME="user" MMCONF="${PWD}/mm.yml" PREFIX="\\" TIMEOUT=60 # Ensure access to ADMIN env var to subprocesses export ADMIN="@deadcade:deadca.de" # Create mm directory mkdir -p "${MMDIR}" # Ensure real paths instead of relative MMDIR=$(readlink -nf "${MMDIR}") CMDDIR=$(readlink -nf "${CMDDIR}") # Generate mm config if [ ! -f "${MMCONF}" ]; then # No config was found, create a new one PASSWORD=$(pass "${HOMESERVER}/${USERNAME}") echo "server: ${HOMESERVER} username: ${USERNAME} password: ${PASSWORD} certificate: '' directory: ${MMDIR} login: userid: '' deviceid: '' accesstoken: '' " > "${MMCONF}" unset PASSWORD fi # Start mm cd "${MMDIR}" || exit $MM -c "${MMCONF}" 2>> "${MMDIR}/log" 1>> "${MMDIR}/out" & MMPID="${!}" # Ensure mm is started sleep 3 # Remove password from file (otherwise password would be stored in plaintext) grep -vF "password" "${MMCONF}" > /tmp/mm.yml cp /tmp/mm.yml "${MMCONF}" rm /tmp/mm.yml # Get some variables from created config HOMESERVER=$(echo "${HOMESERVER}" | cut -d "/" -f 3) USERID=$(grep "userid" "${MMCONF}" | awk '{ print $2 }' | cut -d "'" -f 2) # Ensure log in was successful cd "${MMDIR}/${HOMESERVER}/${USERID}" || exit # Separate function to execute the command, for async support execute() { CMD=$1 # Grab args without command ARGS=$(printf '%s' "$MESSAGE" | tail -c"+$((${#PREFIX} + ${#CMD} + 2))") # Execute command, keeping every Matrix command argument as a shell command argument # shellcheck disable=SC2086 OUT=$(timeout "$((TIMEOUT + 5))" timeout -s INT "${TIMEOUT}" "${CMDDIR}/${CMD}" ${ARGS} 2>&1) if [ "$OUT" != "" ]; then # Send back output printf '%s' "${OUT}" > "${INFILE}" fi } # Process a message message() { while read -r MSG; do cd "${MMDIR}/${HOMESERVER}/${USERID}" || exit # Give detail about message to subprocess FILE="!"$(echo "${MSG}" | cut -d "!" -f 2) MESSAGE=$(cat "${FILE}") ROOMID=$(echo "${FILE}" | cut -d "/" -f 1) SENDER=$(echo "${FILE}" | cut -d "/" -f 2) INFILE="${MMDIR}/${HOMESERVER}/${USERID}/${ROOMID}/in" export FILE MESSAGE ROOMID SENDER INFILE # If this message is a command case "${MESSAGE}" in "${PREFIX}"*) cd "${CMDDIR}" || exit for CMD in *; do # Check if the command is valid, either with args or exact if [ "${MESSAGE}" = "${PREFIX}${CMD}" ]; then execute "${CMD}" & fi case "${MESSAGE}" in "${PREFIX}${CMD} "*) execute "${CMD}" & ;; esac done esac done } # Ensure ctrl+c continues the script trap true INT # Give new messages to message function tail -n0 -f "${MMDIR}/out" | message # Kill the mm instance after ctrl+c kill "${MMPID}"