diff --git a/hardcoded_variables.py b/hardcoded_variables.py index 7f3afaf..f57bb00 100644 --- a/hardcoded_variables.py +++ b/hardcoded_variables.py @@ -4,6 +4,8 @@ homeserver_url = "matrix.example.org" base_url = "example.org" access_token = "" +# ipinfo.io token +ipinfo_token = "" # rdlist specific rdlist_bot_username = "mod_team" ########################################################################### diff --git a/moderation_tool.py b/moderation_tool.py index 7bea639..66b0136 100755 --- a/moderation_tool.py +++ b/moderation_tool.py @@ -32,7 +32,7 @@ if length_access_token == 0: pass_token = False while pass_token == False: - menu_input = input('\nPlease select one of the following options:\n#### User Account Commands ####\n1) Deactivate a user account.\n2) Deactivate multiple user accounts.\n3) Create a user account.\n4) Create multiple user accounts.\n5) Reset a users password.\n6) Whois user account.\n7) Whois multiple user accounts.\n8) List room memberships of user.\n9) Promote a user to server admin.\n10) List all user accounts.\n11) Quarantine all media a users uploaded.\n#### Room Commands ####\n12) List details of a room.\n13) Export the state events of a target room.\n14) List rooms in public directory.\n15) Remove a room from the public directory.\n16) Remove multiple rooms from the public directory.\n17) Redact a room event. (Like abusive avatars or display names.) \n18) List/Download all media in a room.\n19) Download media from multiple rooms.\n20) Quarantine all media in a room.\n21) Shutdown a room.\n22) Shutdown multiple rooms.\n23) Delete a room.\n24) Delete multiple rooms.\n25) Purge the event history of a room to a specific timestamp.\n26) Purge the event history of multiple rooms to a specific timestamp.\n#### Server Commands ####\n27) Delete and block a specific media. (Like an abusive avatar.) \n28) Purge remote media repository up to a certain date.\n29) Prepare database for copying events of multiple rooms.\n#### rdlist ####\n30) Block all rooms with specific rdlist tags.\n34) Block all rooms with recommended rdlist tags.\n(\'q\' or \'e\') Exit.\n\n') + menu_input = input('\nPlease select one of the following options:\n#### User Account Commands ####\n1) Deactivate a user account.\n2) Deactivate multiple user accounts.\n3) Create a user account.\n4) Create multiple user accounts.\n5) Reset a users password.\n6) Whois user account.\n7) Whois multiple user accounts.\n8) Query user account.\n9) Query multiple user accounts.\n10) List room memberships of user.\n11) Promote a user to server admin.\n12) List all user accounts.\n13) Quarantine all media a users uploaded.\n#### Room Commands ####\n14) List details of a room.\n15) Export the state events of a target room.\n16) List rooms in public directory.\n17) Remove a room from the public directory.\n18) Remove multiple rooms from the public directory.\n19) Redact a room event. (Like abusive avatars or display names.) \n20) List/Download all media in a room.\n21) Download media from multiple rooms.\n22) Quarantine all media in a room.\n23) Shutdown a room.\n24) Shutdown multiple rooms.\n25) Delete a room.\n26) Delete multiple rooms.\n27) Purge the event history of a room to a specific timestamp.\n28) Purge the event history of multiple rooms to a specific timestamp.\n#### Server Commands ####\n29) Delete and block a specific media. (Like an abusive avatar.) \n30) Purge remote media repository up to a certain date.\n31) Prepare database for copying events of multiple rooms.\n#### rdlist ####\n32) Block all rooms with specific rdlist tags.\n34) Block all rooms with recommended rdlist tags.\n#### ipinfo.io ####\n40) Analyse a users country of origin.\n41) Analyse multiple users country of origin.\n(\'q\' or \'e\') Exit.\n\n') if menu_input == "1": user_commands.deactivate_account('') elif menu_input == "2": @@ -41,60 +41,68 @@ while pass_token == False: user_commands.create_account('','') elif menu_input == "4": user_commands.create_multiple_accounts() - elif menu_input == "6": + elif menu_input == "5": user_commands.reset_password('','') elif menu_input == "6": user_commands.whois_account('') elif menu_input == "7": user_commands.whois_multiple_accounts() elif menu_input == "8": - user_commands.list_joined_rooms('') + user_commands.query_account() elif menu_input == "9": - user_commands.set_user_server_admin('') + user_commands.query_multiple_accounts() elif menu_input == "10": - user_commands.list_accounts() + user_commands.list_joined_rooms('') elif menu_input == "11": - user_commands.quarantine_users_media() + user_commands.set_user_server_admin('') elif menu_input == "12": - room_commands.list_room_details('') + user_commands.list_accounts() elif menu_input == "13": - room_commands.export_room_state('') + user_commands.quarantine_users_media() elif menu_input == "14": - room_commands.list_directory_rooms() + room_commands.list_room_details('') elif menu_input == "15": - room_commands.remove_room_from_directory('') + room_commands.export_room_state('') elif menu_input == "16": - room_commands.remove_multiple_rooms_from_directory() + room_commands.list_directory_rooms() elif menu_input == "17": - room_commands.redact_room_event() + room_commands.remove_room_from_directory('') elif menu_input == "18": - room_commands.list_and_download_media_in_room('','','','./') + room_commands.remove_multiple_rooms_from_directory() elif menu_input == "19": - room_commands.download_media_from_multiple_rooms() + room_commands.redact_room_event() elif menu_input == "20": - room_commands.quarantine_media_in_room() + room_commands.list_and_download_media_in_room('','','','./') elif menu_input == "21": - room_commands.shutdown_room('','','','','','') + room_commands.download_media_from_multiple_rooms() elif menu_input == "22": - room_commands.shutdown_multiple_rooms() + room_commands.quarantine_media_in_room() elif menu_input == "23": - room_commands.delete_room('') + room_commands.shutdown_room('','','','','','') elif menu_input == "24": - room_commands.delete_multiple_rooms() + room_commands.shutdown_multiple_rooms() elif menu_input == "25": - room_commands.purge_room_to_timestamp('','') + room_commands.delete_room('') elif menu_input == "26": - room_commands.purge_multiple_rooms_to_timestamp() + room_commands.delete_multiple_rooms() elif menu_input == "27": - server_commands.delete_block_media() + room_commands.purge_room_to_timestamp('','') elif menu_input == "28": - server_commands.purge_remote_media_repo() + room_commands.purge_multiple_rooms_to_timestamp() elif menu_input == "29": - server_commands.prepare_database_copy_of_multiple_rooms() + server_commands.delete_block_media() elif menu_input == "30": + server_commands.purge_remote_media_repo() + elif menu_input == "31": + server_commands.prepare_database_copy_of_multiple_rooms() + elif menu_input == "32": rdlist_commands.block_all_rooms_with_rdlist_tags(False,'','','','','') elif menu_input == "34": rdlist_commands.block_recommended_rdlist_tags() + elif menu_input == "40": + user_commands.analyse_account_ip('') + elif menu_input == "41": + user_commands.analyse_multiple_account_ips() elif menu_input == "q" or menu_input == "Q" or menu_input == "e" or menu_input == "E": print("\nExiting...\n") pass_token = True diff --git a/user_commands.py b/user_commands.py index 028321a..798d965 100644 --- a/user_commands.py +++ b/user_commands.py @@ -1,9 +1,11 @@ +import os import requests import json import time import csv import hardcoded_variables +import socket def parse_username(username): tail_end = ':' + hardcoded_variables.base_url @@ -183,7 +185,7 @@ def set_user_server_admin(preset_username): # Example: # $ curl -kX POST -H 'Content-Type: application/json' -d '{"admin": "true"}' https://matrix.perthchat.org/_synapse/admin/v2/users/@dogpoo:perthchat.org?access_token=ACCESS_TOKEN -def whois_account(preset_username): +def whois_account(preset_username, output_file=None): if preset_username == '': username = input("\nPlease enter the username you wish to whois: ") elif preset_username != '': @@ -196,10 +198,17 @@ def whois_account(preset_username): print("\n" + url + "\n") response = requests.get(url, verify=True) + output_text = "" if response.status_code == 200: - print(response.text + "\n") + output_text = response.text + "\n" else: - print(f"Error retrieving account info: {response.status_code}, {response.text}\n") + output_text = f"Error retrieving account info: {response.status_code}, {response.text}\n" + + if output_file: + with open(output_file, 'a') as f: + f.write(output_text) + else: + print(output_text) return response.text @@ -207,24 +216,110 @@ def whois_account(preset_username): # $ curl -kXGET https://matrix.perthchat.org/_matrix/client/r0/admin/whois/@PC-Admin:perthchat.org?access_token=ACCESS_TOKEN def whois_multiple_accounts(): - print("Whois multiple user accounts selected") - user_list_location = input("\nPlease enter the path of the file containing a newline seperated list of Matrix usernames: ") - with open(user_list_location, newline='') as f: - reader = csv.reader(f) - data = list(reader) - print(len(data)) - whois_confirmation = input("\n" + str(data) + "\n\nAre you sure you want to whois all of these users? y/n?\n") - if whois_confirmation == "y" or whois_confirmation == "Y" or whois_confirmation == "yes" or whois_confirmation == "Yes": - x = 0 - while x <= (len(data) - 1): - print(data[x][0]) - query_account(data[x][0]) - list_joined_rooms(data[x][0]) - x += 1 - #print(x) - time.sleep(1) - if whois_confirmation == "n" or whois_confirmation == "N" or whois_confirmation == "no" or whois_confirmation == "No": - print("\nExiting...\n") + print("Whois multiple user accounts selected") + user_list_location = input("\nPlease enter the path of the file containing a newline seperated list of Matrix usernames: ") + with open(user_list_location, newline='') as f: + reader = csv.reader(f) + data = list(reader) + print(len(data)) + + print("\n" + str(data)) + + output_file = None + if len(data) > 10: + file_confirmation = input("\nThere are more than 10 users. Would you like to save the output to a file? y/n?\n") + if file_confirmation.lower() in ("y", "yes"): + output_file = input("\nPlease enter the desired output file path:\n") + + whois_confirmation = input("\n\nAre you sure you want to whois all of these users? y/n?\n") + + if whois_confirmation.lower() in ("y", "yes"): + x = 0 + while x <= (len(data) - 1): + output = whois_account(data[x][0]) + + # if output file is specified, append to file + if output_file: + with open(output_file, 'a') as f: + f.write(output + "\n") + x += 1 + time.sleep(1) + + if whois_confirmation.lower() in ("n", "no"): + print("\nExiting...\n") + + if output_file and os.path.isfile(output_file): + print(f"Output saved to {output_file}") + +def is_valid_ipv4(ip): + try: + socket.inet_pton(socket.AF_INET, ip) + except socket.error: # not a valid address + return False + return True + +def analyse_account_ip(preset_username): + if not preset_username: + preset_username = input("\nPlease enter a username to analyse their country of origin: ") + user_info = whois_account(preset_username=preset_username) + + data = json.loads(user_info) + + user_id = data['user_id'] + #print(f'user_id: {user_id}') + device_data = data['devices'] + #print(f'device_data: {device_data}') + + countries = [] + for device_id, device_info in device_data.items(): + for session in device_info['sessions']: + for connection in session['connections']: + ip = connection['ip'] + if is_valid_ipv4(ip): + res = requests.get(f"https://ipinfo.io/{ip}", + headers={"Authorization": f"Bearer {hardcoded_variables.ipinfo_token}"}) + if res.status_code == 200: + country = res.json().get('country') + countries.append(country) + + print(f"User: {user_id} from Countries: {countries}") + return(f"User: {user_id} from Countries: {countries}") + +def analyse_multiple_account_ips(): + print("Analyse multiple user IPs selected") + user_list_location = input("\nPlease enter the path of the file containing a newline seperated list of Matrix usernames: ") + with open(user_list_location, newline='') as f: + reader = csv.reader(f) + data = list(reader) + print(len(data)) + + print("\n" + str(data)) + + output_file = None + if len(data) > 10: + file_confirmation = input("\nThere are more than 10 users. Would you like to save the output to a file? y/n?\n") + if file_confirmation.lower() in ("y", "yes"): + output_file = input("\nPlease enter the desired output file path:\n") + + analyse_confirmation = input("\n\nAre you sure you want to analyse the IP of all of these users? y/n?\n") + + if analyse_confirmation.lower() in ("y", "yes"): + x = 0 + while x <= (len(data) - 1): + output = analyse_account_ip(data[x][0]) + + # if output file is specified, append to file + if output_file: + with open(output_file, 'a') as f: + f.write(output + "\n") + x += 1 + time.sleep(1) + + if analyse_confirmation.lower() in ("n", "no"): + print("\nExiting...\n") + + if output_file and os.path.isfile(output_file): + print(f"Output saved to {output_file}") def list_joined_rooms(preset_username): if preset_username == '': @@ -299,6 +394,42 @@ def query_account(preset_username): # Example: # $ curl -kX GET https://matrix.perthchat.org/_synapse/admin/v2/users/@billybob:perthchat.org?access_token=ACCESS_TOKEN +def query_multiple_accounts(): + print("Query multiple user accounts selected") + user_list_location = input("\nPlease enter the path of the file containing a newline seperated list of Matrix usernames: ") + with open(user_list_location, newline='') as f: + reader = csv.reader(f) + data = list(reader) + print(len(data)) + + print("\n" + str(data)) + + output_file = None + if len(data) > 10: + file_confirmation = input("\nThere are more than 10 users. Would you like to save the output to a file? y/n?\n") + if file_confirmation.lower() in ("y", "yes"): + output_file = input("\nPlease enter the desired output file path:\n") + + query_confirmation = input("\n\nAre you sure you want to query all of these users? y/n?\n") + + if query_confirmation.lower() in ("y", "yes"): + x = 0 + while x <= (len(data) - 1): + output = query_account(data[x][0]) + + # if output file is specified, append to file + if output_file: + with open(output_file, 'a') as f: + f.write(output + "\n") + x += 1 + time.sleep(1) + + if query_confirmation.lower() in ("n", "no"): + print("\nExiting...\n") + + if output_file and os.path.isfile(output_file): + print(f"Output saved to {output_file}") + def quarantine_users_media(): username = input("\nPlease enter the username of the user who's media you want to quarantine: ") username = parse_username(username)