From d6fd2f97e3da9de045fce387699ba5deae730a11 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Wed, 13 Dec 2023 13:18:16 +0100 Subject: [PATCH] Add deletion of local user media Unfortunately, the database only stores the orginating server, and not the specific user id for remote media, so we can only delete media from our users with the new -u option. For more, we need to change the database, or dig much deeper. Also, this will not work for E2E encrypted chats, I think. --- cleanmedia | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/cleanmedia b/cleanmedia index 4794420..c74ab89 100755 --- a/cleanmedia +++ b/cleanmedia @@ -123,6 +123,23 @@ class MediaRepository: # creation_ts is ms since the epoch, so convert to seconds return File(self, row[0], row[1] // 1000, row[2]) + def get_local_user_media(self, user_id: str) -> List[File]: + """Return all media created by a local user + + :params: + :user_id: (`str`) of form "@user:servername.com" + :returns: `List[File]` + """ + with self.conn.cursor() as cur: + sql_str = "SELECT media_id, creation_ts, base64hash from mediaapi_media_repository WHERE user_id = %s;" + cur.execute(sql_str, (user_id,)) + files = [] + for row in cur.fetchall(): + # creation_ts is ms since the epoch, so convert to seconds + f = File(self, row[0], row[1] // 1000, row[2]) + files.append(f) + return files + def get_all_media(self, local: bool = False) -> List[File]: """Return List[File] of remote media or ALL media if local==True""" with self.conn.cursor() as cur: @@ -241,11 +258,13 @@ def parse_options() -> argparse.Namespace: parser.add_argument('-c', '--config', default="config.yaml", help="location of the dendrite.yaml config file.") parser.add_argument('-m', '--mxid', dest="mxid", help="Just delete media . (no cleanup otherwise)") + parser.add_argument('-u', '--userid', dest="userid", + help="Delete all media by local user '\\@user:domain.com'. (ie, a user on hour homeserver. no cleanup otherwise)") parser.add_argument('-t', '--days', dest="days", default="30", type=int, help="Keep remote media for days.") parser.add_argument('-l', '--local', action='store_true', - help="Also include local (ie, from *our* users) media files when purging.") + help="Also purge local (ie, from *our* users) media.") parser.add_argument('-n', '--dryrun', action='store_true', help="Dry run (don't actually modify any files).") parser.add_argument('-q', '--quiet', action='store_true', help="Reduce output verbosity.") @@ -266,11 +285,27 @@ if __name__ == '__main__': if args.mxid: # Just clean a single media + logging.info("Attempting to delete media '%s'", args.mxid) file = mr.get_single_media(args.mxid) if file: logging.info("Found media with id '%s'", args.mxid) if not args.dryrun: file.delete() + elif args.userid: + logging.info("Attempting to delete media by user '%s'", args.userid) + files = mr.get_local_user_media(args.userid) + num_deleted = 0 + for file in files: + num_deleted += 1 + if args.dryrun: # the great pretender + logging.info(f"Pretending to delete file id {file.media_id} on path {file.fullpath}.") + else: + file.delete() + info_str = "Deleted %d files during the run." + if args.dryrun: + info_str = "%d files would have been deleted during the run." + logging.info(info_str, num_deleted) + else: # main clean out... # Sanity checks mr.sanity_check_thumbnails() # warn in case of superfluous thumbnails