#!/usr/bin/env bash
# this script will use the api:
# https://matrix-org.github.io/synapse/latest/admin_api/purge_history_api.html
#
# It will purge all messages in a list of rooms up to a cetrain event
###################################################################################################
# define your domain and admin user
###################################################################################################
# add this user as admin in your home server:
DOMAIN = yourserver.tld
# add this user as admin in your home server:
ADMIN = " @you_admin_username: $DOMAIN "
API_URL = " $DOMAIN :8008/_matrix/client/r0 "
###################################################################################################
#choose the rooms to prune old messages from (add a free comment at the end)
###################################################################################################
# the room_id's you can get e.g. from your Riot clients "View Source" button on each message
ROOMS_ARRAY = (
'!DgvjtOljKujDBrxyHk:matrix.org#riot:matrix.org'
'!QtykxKocfZaZOUrTwp:matrix.org#Matrix HQ'
)
# ALTERNATIVELY:
# you can select all the rooms that are not encrypted and loop over the result:
# SELECT room_id FROM rooms WHERE room_id NOT IN (SELECT DISTINCT room_id FROM events WHERE type ='m.room.encrypted')
# or
# select all rooms with at least 100 members:
# SELECT q.room_id FROM (select count(*) as numberofusers, room_id FROM current_state_events WHERE type ='m.room.member'
# GROUP BY room_id) AS q LEFT JOIN room_aliases a ON q.room_id=a.room_id WHERE q.numberofusers > 100 ORDER BY numberofusers desc
###################################################################################################
# evaluate the EVENT_ID before which should be pruned
###################################################################################################
# choose a time before which the messages should be pruned:
TIME = '12 months ago'
# ALTERNATIVELY:
# a certain time:
# TIME='2016-08-31 23:59:59'
# creates a timestamp from the given time string:
UNIX_TIMESTAMP = $( date +%s%3N --date= 'TZ="UTC+2" ' " $TIME " )
# ALTERNATIVELY:
# prune all messages that are older than 1000 messages ago:
# LAST_MESSAGES=1000
# SQL_GET_EVENT="SELECT event_id from events WHERE type='m.room.message' AND room_id ='$ROOM' ORDER BY received_ts DESC LIMIT 1 offset $(($LAST_MESSAGES - 1))"
# ALTERNATIVELY:
# select the EVENT_ID manually:
#EVENT_ID='$1471814088343495zpPNI:matrix.org' # an example event from 21st of Aug 2016 by Matthew
###################################################################################################
# make the admin user a server admin in the database with
###################################################################################################
# psql -A -t --dbname=synapse -c "UPDATE users SET admin=1 WHERE name LIKE '$ADMIN'"
###################################################################################################
# database function
###################################################################################################
sql ( ) {
# for sqlite3:
#sqlite3 homeserver.db "pragma busy_timeout=20000;$1" | awk '{print $2}'
# for postgres:
psql -A -t --dbname= synapse -c " $1 " | grep -v 'Pager'
}
###################################################################################################
# get an access token
###################################################################################################
# for example externally by watching Riot in your browser's network inspector
# or internally on the server locally, use this:
TOKEN = $( sql " SELECT token FROM access_tokens WHERE user_id=' $ADMIN ' ORDER BY id DESC LIMIT 1 " )
AUTH = " Authorization: Bearer $TOKEN "
###################################################################################################
# check, if your TOKEN works. For example this works:
###################################################################################################
# $ curl --header "$AUTH" "$API_URL/rooms/$ROOM/state/m.room.power_levels"
###################################################################################################
# finally start pruning the room:
###################################################################################################
# this will really delete local events, so the messages in the room really
# disappear unless they are restored by remote federation. This is because
# we pass {"delete_local_events":true} to the curl invocation below.
for ROOM in " ${ ROOMS_ARRAY [@] } " ; do
echo " ########################################### $( date) ################# "
echo " pruning room: $ROOM ... "
ROOM = ${ ROOM %#* }
#set -x
echo "check for alias in db..."
# for postgres:
sql " SELECT * FROM room_aliases WHERE room_id=' $ROOM ' "
echo "get event..."
# for postgres:
EVENT_ID = $( sql " SELECT event_id FROM events WHERE type='m.room.message' AND received_ts<' $UNIX_TIMESTAMP ' AND room_id=' $ROOM ' ORDER BY received_ts DESC LIMIT 1; " )
if [ " $EVENT_ID " = = "" ] ; then
echo " no event $TIME "
else
echo " event: $EVENT_ID "
SLEEP = 2
set -x
# call purge
OUT = $( curl --header " $AUTH " -s -d '{"delete_local_events":true}' POST " $API_URL /admin/purge_history/ $ROOM / $EVENT_ID " )
PURGE_ID = $( echo " $OUT " | grep purge_id| cut -d'"' -f4 )
if [ " $PURGE_ID " = = "" ] ; then
# probably the history purge is already in progress for $ROOM
: "continuing with next room"
else
while : ; do
# get status of purge and sleep longer each time if still active
sleep $SLEEP
STATUS = $( curl --header " $AUTH " -s GET " $API_URL /admin/purge_history_status/ $PURGE_ID " | grep status| cut -d'"' -f4)
: " $ROOM --> Status: $STATUS "
[ [ " $STATUS " = = "active" ] ] || break
SLEEP = $(( SLEEP + 1 ))
done
fi
set +x
sleep 1
fi
done
###################################################################################################
# additionally
###################################################################################################
# to benefit from pruning large amounts of data, you need to call VACUUM to free the unused space.
# This can take a very long time (hours) and the client have to be stopped while you do so:
# $ synctl stop
# $ sqlite3 -line homeserver.db "vacuum;"
# $ synctl start
# This could be set, so you don't need to prune every time after deleting some rows:
# $ sqlite3 homeserver.db "PRAGMA auto_vacuum = FULL;"
# be cautious, it could make the database somewhat slow if there are a lot of deletions
exit