mirror of
https://github.com/PC-Admin/matrix-moderation-tool.git
synced 2024-12-19 07:00:27 -05:00
move ipinfo functions to seperate file, add rudimentary user reporting function
This commit is contained in:
parent
94740aceb0
commit
3ad8469b5f
46
README.md
46
README.md
@ -88,21 +88,23 @@ To do:
|
|||||||
5) Add more automated rdlist function with sane defaults - DONE
|
5) Add more automated rdlist function with sane defaults - DONE
|
||||||
6) Add fully automated (should just return a web link and decryption password) reporting functions for users:
|
6) Add fully automated (should just return a web link and decryption password) reporting functions for users:
|
||||||
- Description of why the report was made (what happened)
|
- Description of why the report was made (what happened)
|
||||||
- User's ID
|
- User's ID - DONE
|
||||||
- Whois Data
|
- Whois Data - DONE
|
||||||
- Account Data
|
- Account Data - DONE
|
||||||
- Query Data
|
- Query Data - DONE
|
||||||
- Pushers List
|
- Pushers List - DONE
|
||||||
|
- IPs + ipinfo Data - DONE
|
||||||
- List of the rooms the user is participating in, divided into 1:1 conversations and larger rooms
|
- List of the rooms the user is participating in, divided into 1:1 conversations and larger rooms
|
||||||
- The content of the messages they've sent (if they were sent to rooms your server is participating in)
|
- Any other usernames associated with that IP
|
||||||
- Copies of any media they've sent?
|
- Timestamp for when illegal material was accessed
|
||||||
- Description of report format and contents (to guide the reader)
|
- Description of report format and contents (to guide the reader)
|
||||||
7) Add a room report function to create a properly formatted report for rdlist
|
7) Add a room report function to create a properly formatted report for rdlist
|
||||||
8) Add a function to extract a users email or 3PID
|
8) Add a function to extract a users email or 3PID
|
||||||
9) Do room shutdowns in parallel?
|
9) Do room shutdowns in parallel?
|
||||||
10) Add function for probing the support email of another server automatically
|
10) Add function for probing the support email of another server automatically
|
||||||
11) Automated incident report email to other server owners for more scalable coordination
|
11) Automated incident report email to other server owners for more scalable coordination
|
||||||
12) Automated public room joining and reminder if reporting email is not available
|
12) Automated public room joining and reminder if reporting email is not available?
|
||||||
|
|
||||||
|
|
||||||
***
|
***
|
||||||
## rdlist Functionality
|
## rdlist Functionality
|
||||||
@ -161,3 +163,31 @@ The room was not found.
|
|||||||
```
|
```
|
||||||
|
|
||||||
Note that this script before shutting these rooms down will save the state events to the "./state_events" folder, please keep this data as it's important for law enforcement.
|
Note that this script before shutting these rooms down will save the state events to the "./state_events" folder, please keep this data as it's important for law enforcement.
|
||||||
|
|
||||||
|
|
||||||
|
***
|
||||||
|
## One-touch Reporting
|
||||||
|
|
||||||
|
WARNING: This section is under heavy development and shouldn't be used by anyone!!!
|
||||||
|
|
||||||
|
This script can automatically generate reports about user accounts for law enforcement.
|
||||||
|
|
||||||
|
It collects as much data about the target user account as possible, then packages it into an encrypted ZIP file that can be shared:
|
||||||
|
```
|
||||||
|
|
||||||
|
Please enter a number from the above menu, or enter 'q' or 'e' to exit.
|
||||||
|
|
||||||
|
70
|
||||||
|
|
||||||
|
Please enter the username to automatically generate a report: michael
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Report generated successfully on user: "michael"
|
||||||
|
|
||||||
|
You can send this .zip file and password when reporting a user to law enforcement.
|
||||||
|
|
||||||
|
Password: RwiFrw9zouhVO7Dy9kW7
|
||||||
|
Encrypted .zip file location: ./reports/michael_2023-07-23_02-21-56.zip.aes
|
||||||
|
Encrypted .zip file size: 0.503927 MB
|
||||||
|
```
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# These values can be hard coded for easier usage: #
|
# These values can be hard coded for easier usage: #
|
||||||
homeserver_url = "matrix.example.org"
|
homeserver_url = "matrix.example.org" # Your homeserver URL
|
||||||
base_url = "example.org"
|
base_url = "example.org" # Your base URL (appears in usernames)
|
||||||
access_token = ""
|
access_token = "" # Your homeserver admin access token
|
||||||
# ipinfo.io token
|
# ipinfo.io token
|
||||||
ipinfo_token = ""
|
ipinfo_token = "" # Leave blank to disable ipinfo.io lookups
|
||||||
# rdlist specific
|
# rdlist specific
|
||||||
rdlist_bot_username = "mod_team"
|
rdlist_bot_username = "mod_team" # The username to perform automated room shutdowns
|
||||||
rdlist_recommended_tags = ['hub_room_links', 'hub_room_trade', 'preban', 'degen_misc', 'beastiality', 'degen_porn', 'gore', 'snuff', 'degen_larp', 'hub_room_sussy', 'bot_spam', 'cfm', 'jailbait', 'bot_porn', 'toddlercon', 'loli', 'csam', 'tfm', 'degen_meet', 'stylized_3d_loli', '3d_loli']
|
rdlist_recommended_tags = ['hub_room_links', 'hub_room_trade', 'preban', 'degen_misc', 'beastiality', 'degen_porn', 'gore', 'snuff', 'degen_larp', 'hub_room_sussy', 'bot_spam', 'cfm', 'jailbait', 'bot_porn', 'toddlercon', 'loli', 'csam', 'tfm', 'degen_meet', 'stylized_3d_loli', '3d_loli']
|
||||||
|
# report generator
|
||||||
|
report_folder = "./reports" # Reports folder name
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
77
ipinfo_commands.py
Normal file
77
ipinfo_commands.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
import hardcoded_variables
|
||||||
|
import user_commands
|
||||||
|
|
||||||
|
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: ")
|
||||||
|
data = user_commands.whois_account(preset_username=preset_username)
|
||||||
|
|
||||||
|
user_id = data['user_id']
|
||||||
|
device_data = data['devices']
|
||||||
|
|
||||||
|
ip_info = {}
|
||||||
|
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) and len(hardcoded_variables.ipinfo_token) > 0:
|
||||||
|
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')
|
||||||
|
ip_info[ip] = country
|
||||||
|
|
||||||
|
if len(hardcoded_variables.ipinfo_token) == 0:
|
||||||
|
return {"user_id": user_id, "ip_info": "IPINFO DISABLED"}
|
||||||
|
else:
|
||||||
|
return {"user_id": user_id, "ip_info": ip_info}
|
||||||
|
|
||||||
|
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}")
|
@ -2,7 +2,9 @@
|
|||||||
import user_commands
|
import user_commands
|
||||||
import room_commands
|
import room_commands
|
||||||
import server_commands
|
import server_commands
|
||||||
|
import ipinfo_commands
|
||||||
import rdlist_commands
|
import rdlist_commands
|
||||||
|
import report_commands
|
||||||
import hardcoded_variables
|
import hardcoded_variables
|
||||||
|
|
||||||
# check if homeserver url is hard coded, if not set it
|
# check if homeserver url is hard coded, if not set it
|
||||||
@ -51,9 +53,9 @@ while pass_token == False:
|
|||||||
print("17) Set rate limit of a user account.")
|
print("17) Set rate limit of a user account.")
|
||||||
print("18) Delete rate limit of a user account.")
|
print("18) Delete rate limit of a user account.")
|
||||||
print("19) Check if user account exists.")
|
print("19) Check if user account exists.")
|
||||||
print("\n#### Server Commands ####")
|
print("\n#### Server Commands ####\t\t\t\t\t#### Report Generation ####")
|
||||||
print("40) Delete and block a specific media.")
|
print("40) Delete and block a specific media.\t\t\t\t70) Generate user report.")
|
||||||
print("41) Purge remote media repository up to a certain date.")
|
print("41) Purge remote media repository up to a certain date.\t\t71) Decrypt user report .zip file.")
|
||||||
print("42) Prepare database for copying events of multiple rooms.")
|
print("42) Prepare database for copying events of multiple rooms.")
|
||||||
print("\n#### rdlist ####")
|
print("\n#### rdlist ####")
|
||||||
print("50) Block all rooms with specific rdlist tags.")
|
print("50) Block all rooms with specific rdlist tags.")
|
||||||
@ -142,9 +144,13 @@ while pass_token == False:
|
|||||||
elif menu_input == "51":
|
elif menu_input == "51":
|
||||||
rdlist_commands.block_recommended_rdlist_tags()
|
rdlist_commands.block_recommended_rdlist_tags()
|
||||||
elif menu_input == "60":
|
elif menu_input == "60":
|
||||||
user_commands.analyse_account_ip('')
|
ipinfo_commands.analyse_account_ip('')
|
||||||
elif menu_input == "61":
|
elif menu_input == "61":
|
||||||
user_commands.analyse_multiple_account_ips()
|
ipinfo_commands.analyse_multiple_account_ips()
|
||||||
|
elif menu_input == "70":
|
||||||
|
report_commands.generate_user_report('')
|
||||||
|
elif menu_input == "71":
|
||||||
|
report_commands.decrypt_zip_file()
|
||||||
elif menu_input == "q" or menu_input == "Q" or menu_input == "e" or menu_input == "E":
|
elif menu_input == "q" or menu_input == "Q" or menu_input == "e" or menu_input == "E":
|
||||||
print("\nExiting...\n")
|
print("\nExiting...\n")
|
||||||
pass_token = True
|
pass_token = True
|
||||||
|
166
report_commands.py
Normal file
166
report_commands.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import datetime
|
||||||
|
import zipfile
|
||||||
|
import pyAesCrypt
|
||||||
|
import user_commands
|
||||||
|
import room_commands
|
||||||
|
import ipinfo_commands
|
||||||
|
import hardcoded_variables
|
||||||
|
|
||||||
|
# For testing the Report Generator, set this to True
|
||||||
|
testing_mode = False
|
||||||
|
|
||||||
|
def get_report_folder():
|
||||||
|
# Get report_folder from hardcoded_variables
|
||||||
|
report_folder = hardcoded_variables.report_folder
|
||||||
|
|
||||||
|
# If report_folder ends with a slash, remove it
|
||||||
|
if report_folder.endswith(os.sep):
|
||||||
|
report_folder = report_folder[:-1]
|
||||||
|
|
||||||
|
return report_folder
|
||||||
|
|
||||||
|
def encrypt_user_folder(user_report_folder, username):
|
||||||
|
# Generate a strong random password
|
||||||
|
strong_password = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(20))
|
||||||
|
|
||||||
|
# Get parent directory of user_report_folder
|
||||||
|
parent_directory = os.path.dirname(os.path.abspath(user_report_folder))
|
||||||
|
|
||||||
|
# Create the name of the .zip file including timestamp
|
||||||
|
zip_file_name = os.path.join(parent_directory, username + "_" + datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + ".zip")
|
||||||
|
|
||||||
|
# Create a .zip file of the specified folder
|
||||||
|
with zipfile.ZipFile(zip_file_name, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||||
|
for root, dirs, files in os.walk(user_report_folder):
|
||||||
|
for file in files:
|
||||||
|
zip_file.write(os.path.join(root, file), arcname=os.path.relpath(os.path.join(root, file), user_report_folder))
|
||||||
|
|
||||||
|
# Buffer size - 64K
|
||||||
|
bufferSize = 64 * 1024
|
||||||
|
|
||||||
|
# Encrypt the .zip file
|
||||||
|
pyAesCrypt.encryptFile(zip_file_name, zip_file_name + ".aes", strong_password, bufferSize)
|
||||||
|
|
||||||
|
# Delete the original zip file
|
||||||
|
os.remove(zip_file_name)
|
||||||
|
|
||||||
|
# You can return the password if you need to use it later, or you can directly print it here
|
||||||
|
return strong_password, zip_file_name + ".aes"
|
||||||
|
|
||||||
|
def generate_user_report(preset_username):
|
||||||
|
if len(preset_username) == 0:
|
||||||
|
username = input("\nPlease enter the username to automatically generate a report: ")
|
||||||
|
username = user_commands.parse_username(username)
|
||||||
|
else:
|
||||||
|
username = user_commands.parse_username(preset_username)
|
||||||
|
|
||||||
|
# Check if user exists
|
||||||
|
if user_commands.check_user_account_exists(username) == True:
|
||||||
|
print("\nUser exists, continuing with report generation.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# If report_folder ends in a slash, remove it
|
||||||
|
report_folder = get_report_folder()
|
||||||
|
|
||||||
|
# Create report folders
|
||||||
|
user_report_folder = report_folder + "/" + username + "/"
|
||||||
|
|
||||||
|
if os.path.exists(report_folder) == False:
|
||||||
|
os.mkdir(report_folder)
|
||||||
|
|
||||||
|
if os.path.exists(user_report_folder) == False:
|
||||||
|
os.mkdir(user_report_folder)
|
||||||
|
|
||||||
|
# Get user account data and write to ./report/username/account_data.json
|
||||||
|
account_data = user_commands.collect_account_data(username)
|
||||||
|
account_data_file = open(user_report_folder + "account_data.json", "w")
|
||||||
|
account_data_file.write(json.dumps(account_data, indent=4, sort_keys=True))
|
||||||
|
account_data_file.close()
|
||||||
|
|
||||||
|
# Get user pushers and write to ./report/username/pushers.json
|
||||||
|
pushers_data = user_commands.list_account_pushers(username)
|
||||||
|
pushers_file = open(user_report_folder + "pushers.json", "w")
|
||||||
|
pushers_file.write(json.dumps(pushers_data, indent=4, sort_keys=True))
|
||||||
|
pushers_file.close()
|
||||||
|
|
||||||
|
# Get whois data and write to ./report/username/whois.json
|
||||||
|
whois_data = user_commands.whois_account(username)
|
||||||
|
whois_file = open(user_report_folder + "whois.json", "w")
|
||||||
|
whois_file.write(json.dumps(whois_data, indent=4, sort_keys=True))
|
||||||
|
whois_file.close()
|
||||||
|
|
||||||
|
# Get query data and write to ./report/username/query.json
|
||||||
|
query_data = user_commands.query_account(username)
|
||||||
|
query_file = open(user_report_folder + "query.json", "w")
|
||||||
|
query_file.write(json.dumps(query_data, indent=4, sort_keys=True))
|
||||||
|
query_file.close()
|
||||||
|
|
||||||
|
# Get user joined rooms and write to ./report/username/joined_rooms.json
|
||||||
|
joined_rooms_dict = user_commands.list_joined_rooms(username)
|
||||||
|
joined_rooms_file = open(user_report_folder + "joined_rooms.json", "w")
|
||||||
|
joined_rooms_file.write(json.dumps(joined_rooms_dict, indent=4, sort_keys=True))
|
||||||
|
joined_rooms_file.close()
|
||||||
|
|
||||||
|
# Get user ipinfo and write to ./report/username/ipinfo.json
|
||||||
|
ipinfo = ipinfo_commands.analyse_account_ip(username)
|
||||||
|
ipinfo_file = open(user_report_folder + "ipinfo.json", "w")
|
||||||
|
ipinfo_file.write(json.dumps(ipinfo, indent=4, sort_keys=True))
|
||||||
|
ipinfo_file.close()
|
||||||
|
|
||||||
|
# For each room the user is in, get the room state and write to ./report/username/room_states/
|
||||||
|
room_states_folder = user_report_folder + "room_states/"
|
||||||
|
if os.path.exists(room_states_folder) == False:
|
||||||
|
os.mkdir(room_states_folder)
|
||||||
|
|
||||||
|
room_list = joined_rooms_dict.get('joined_rooms', [])
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for room in room_list:
|
||||||
|
count += 1
|
||||||
|
room = room.split(" ")[0]
|
||||||
|
room_commands.export_room_state(room, room_states_folder)
|
||||||
|
if count > 4 and testing_mode == True:
|
||||||
|
break
|
||||||
|
|
||||||
|
# For each room the user is in, get the room details and write to ./report/username/room_details/
|
||||||
|
room_details_folder = user_report_folder + "room_details/"
|
||||||
|
if os.path.exists(room_details_folder) == False:
|
||||||
|
os.mkdir(room_details_folder)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for room in room_list:
|
||||||
|
count += 1
|
||||||
|
room = room.split(" ")[0]
|
||||||
|
room_details = room_commands.list_room_details(room)
|
||||||
|
room_details_file = open(room_details_folder + room + ".json", "w")
|
||||||
|
room_details_file.write(str(room_details))
|
||||||
|
room_details_file.close()
|
||||||
|
if count > 4 and testing_mode == True:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Generate a random password, then encrypt the ./report/username/ folder to a timestamped .zip file
|
||||||
|
strong_password, encrypted_zip_file_name = encrypt_user_folder(user_report_folder, username)
|
||||||
|
|
||||||
|
# Measure the size of the encrypted .zip file in MB
|
||||||
|
encrypted_zip_file_size = os.path.getsize(encrypted_zip_file_name) / 1000000
|
||||||
|
|
||||||
|
# Print the password and the encrypted .zip file name
|
||||||
|
print("\nReport generated successfully on user: \"" + username + "\"\n\nYou can send this .zip file and password when reporting a user to law enforcement.")
|
||||||
|
print("\nPassword: " + strong_password)
|
||||||
|
print("Encrypted .zip file location: " + encrypted_zip_file_name)
|
||||||
|
print("Encrypted .zip file size: " + str(encrypted_zip_file_size) + " MB\n")
|
||||||
|
|
||||||
|
def decrypt_zip_file():
|
||||||
|
# Ask user for the location of the encrypted .zip file
|
||||||
|
encrypted_zip_file_name = input("\nPlease enter the location of the encrypted .zip file: ")
|
||||||
|
# Ask user for the password
|
||||||
|
strong_password = input("Please enter the password: ")
|
||||||
|
# Decrypt the ZIP file into the same location as the encrypted ZIP file
|
||||||
|
pyAesCrypt.decryptFile(encrypted_zip_file_name, encrypted_zip_file_name[:-4], strong_password, 64 * 1024)
|
||||||
|
# Print the location of the decrypted ZIP file
|
||||||
|
print("\nDecrypted .zip file location: " + encrypted_zip_file_name[:-4] + "\n")
|
@ -24,21 +24,28 @@ def list_room_details(preset_internal_ID):
|
|||||||
print("\n" + url + "\n")
|
print("\n" + url + "\n")
|
||||||
response = requests.get(url, headers=headers, verify=True)
|
response = requests.get(url, headers=headers, verify=True)
|
||||||
|
|
||||||
print(response.text)
|
room_details_dict = json.loads(response.text)
|
||||||
|
print(json.dumps(room_details_dict, indent=4, sort_keys=True))
|
||||||
|
|
||||||
|
return room_details_dict
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
# $ curl -kXGET 'https://matrix.perthchat.org/_synapse/admin/v1/rooms/!OeqILBxiHahidSQQoC:matrix.org?access_token=ACCESS_TOKEN'
|
# $ curl -kXGET 'https://matrix.perthchat.org/_synapse/admin/v1/rooms/!OeqILBxiHahidSQQoC:matrix.org?access_token=ACCESS_TOKEN'
|
||||||
|
|
||||||
def export_room_state(preset_internal_ID):
|
def export_room_state(preset_internal_ID, preset_directory):
|
||||||
# record the current directory location
|
# record the current directory location
|
||||||
current_directory = os.getcwd()
|
current_directory = os.getcwd()
|
||||||
|
|
||||||
if preset_internal_ID == '':
|
if preset_internal_ID == '':
|
||||||
internal_ID = input("\nEnter the internal id of the room with which to export the 'state' of (Example: !OLkDvaYjpNrvmwnwdj:matrix.org): ")
|
internal_ID = input("\nEnter the internal id of the room with which to export the 'state' of (Example: !OLkDvaYjpNrvmwnwdj:matrix.org): ")
|
||||||
elif preset_internal_ID != '':
|
elif preset_internal_ID != '':
|
||||||
internal_ID = preset_internal_ID
|
internal_ID = preset_internal_ID
|
||||||
|
|
||||||
room_dir = os.path.join(current_directory, "state_events")
|
if preset_directory == '':
|
||||||
|
room_dir = os.path.join(current_directory, "state_events")
|
||||||
|
elif preset_directory != '':
|
||||||
|
room_dir = preset_directory
|
||||||
|
|
||||||
os.makedirs(room_dir, exist_ok=True)
|
os.makedirs(room_dir, exist_ok=True)
|
||||||
|
|
||||||
unix_time = int(time.time())
|
unix_time = int(time.time())
|
||||||
@ -52,8 +59,9 @@ def export_room_state(preset_internal_ID):
|
|||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
f.write(response.text)
|
f.write(response.text)
|
||||||
|
|
||||||
print(response.text)
|
state_events_dict = json.loads(response.text)
|
||||||
return(response.text)
|
|
||||||
|
return state_events_dict
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
# $ curl -kXGET 'https://matrix.perthchat.org/_synapse/admin/v1/rooms/!OeqILBxiHahidSQQoC:matrix.org/state?access_token=ACCESS_TOKEN'
|
# $ curl -kXGET 'https://matrix.perthchat.org/_synapse/admin/v1/rooms/!OeqILBxiHahidSQQoC:matrix.org/state?access_token=ACCESS_TOKEN'
|
||||||
@ -71,7 +79,8 @@ def list_directory_rooms():
|
|||||||
|
|
||||||
output = output.replace('\"room_id\":\"','\n')
|
output = output.replace('\"room_id\":\"','\n')
|
||||||
output = output.replace('\",\"name','\n\",\"name')
|
output = output.replace('\",\"name','\n\",\"name')
|
||||||
print(output)
|
|
||||||
|
print(json.dumps(output, indent=4, sort_keys=True))
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
# $ curl -kXGET https://matrix.perthchat.org/_matrix/client/r0/publicRooms?access_token=ACCESS_TOKEN
|
# $ curl -kXGET https://matrix.perthchat.org/_matrix/client/r0/publicRooms?access_token=ACCESS_TOKEN
|
||||||
@ -244,7 +253,7 @@ def shutdown_room(preset_internal_ID,preset_user_ID,preset_new_room_name,preset_
|
|||||||
new_room_name = input("\nPlease enter the room name of the muted violation room your users will be sent to: ")
|
new_room_name = input("\nPlease enter the room name of the muted violation room your users will be sent to: ")
|
||||||
elif preset_new_room_name != '':
|
elif preset_new_room_name != '':
|
||||||
new_room_name = preset_new_room_name
|
new_room_name = preset_new_room_name
|
||||||
if preset_message == '':
|
if preset_message == '':
|
||||||
message = input("\nPlease enter the shutdown message that will be displayed to users: ")
|
message = input("\nPlease enter the shutdown message that will be displayed to users: ")
|
||||||
elif preset_message != '':
|
elif preset_message != '':
|
||||||
message = preset_message
|
message = preset_message
|
||||||
@ -351,7 +360,7 @@ def shutdown_multiple_rooms():
|
|||||||
preset_block_choice = input("\n Do you want to block these rooms? (This prevents your server users re-entering the room.) y/n? ")
|
preset_block_choice = input("\n Do you want to block these rooms? (This prevents your server users re-entering the room.) y/n? ")
|
||||||
# Get the directory of the current script
|
# Get the directory of the current script
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
room_list_data = []
|
room_list_data = []
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
print("Processing file: " + file)
|
print("Processing file: " + file)
|
||||||
# Change the current working directory
|
# Change the current working directory
|
||||||
@ -453,11 +462,11 @@ def purge_room_to_timestamp(preset_internal_ID, preset_timestamp):
|
|||||||
timestamp = input("\nEnter the epoch timestamp in microseconds (Example: 1661058683000): ")
|
timestamp = input("\nEnter the epoch timestamp in microseconds (Example: 1661058683000): ")
|
||||||
else:
|
else:
|
||||||
timestamp = preset_timestamp
|
timestamp = preset_timestamp
|
||||||
|
|
||||||
headers = {"Authorization": "Bearer " + hardcoded_variables.access_token, "Content-Type": "application/json"}
|
headers = {"Authorization": "Bearer " + hardcoded_variables.access_token, "Content-Type": "application/json"}
|
||||||
data = {"delete_local_events": False, "purge_up_to_ts": int(timestamp)}
|
data = {"delete_local_events": False, "purge_up_to_ts": int(timestamp)}
|
||||||
url = f'https://{hardcoded_variables.homeserver_url}/_synapse/admin/v1/purge_history/{internal_ID}'
|
url = f'https://{hardcoded_variables.homeserver_url}/_synapse/admin/v1/purge_history/{internal_ID}'
|
||||||
|
|
||||||
response = requests.post(url, headers=headers, data=json.dumps(data))
|
response = requests.post(url, headers=headers, data=json.dumps(data))
|
||||||
print("\n", response.text, "\n")
|
print("\n", response.text, "\n")
|
||||||
|
|
||||||
@ -475,7 +484,7 @@ def purge_room_to_timestamp(preset_internal_ID, preset_timestamp):
|
|||||||
time.sleep(sleep_time)
|
time.sleep(sleep_time)
|
||||||
count += 1
|
count += 1
|
||||||
sleep_time *= 2
|
sleep_time *= 2
|
||||||
|
|
||||||
url_status = f'https://{hardcoded_variables.homeserver_url}/_synapse/admin/v1/purge_history_status/{purge_id}'
|
url_status = f'https://{hardcoded_variables.homeserver_url}/_synapse/admin/v1/purge_history_status/{purge_id}'
|
||||||
response = requests.get(url_status, headers=headers)
|
response = requests.get(url_status, headers=headers)
|
||||||
response_json = response.json()
|
response_json = response.json()
|
||||||
|
@ -210,7 +210,9 @@ def whois_account(preset_username, output_file=None):
|
|||||||
else:
|
else:
|
||||||
print(output_text)
|
print(output_text)
|
||||||
|
|
||||||
return response.text
|
whois_account_dict = json.loads(response.text)
|
||||||
|
|
||||||
|
return whois_account_dict
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
# $ curl -kXGET https://matrix.perthchat.org/_matrix/client/r0/admin/whois/@dogpoo:perthchat.org?access_token=ACCESS_TOKEN
|
# $ curl -kXGET https://matrix.perthchat.org/_matrix/client/r0/admin/whois/@dogpoo:perthchat.org?access_token=ACCESS_TOKEN
|
||||||
@ -251,76 +253,6 @@ def whois_multiple_accounts():
|
|||||||
if output_file and os.path.isfile(output_file):
|
if output_file and os.path.isfile(output_file):
|
||||||
print(f"Output saved to {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):
|
def list_joined_rooms(preset_username):
|
||||||
if preset_username == '':
|
if preset_username == '':
|
||||||
username = input("\nPlease enter the username you wish to query: ")
|
username = input("\nPlease enter the username you wish to query: ")
|
||||||
@ -338,6 +270,10 @@ def list_joined_rooms(preset_username):
|
|||||||
else:
|
else:
|
||||||
print(f"Error querying joined rooms: {response.status_code}, {response.text}")
|
print(f"Error querying joined rooms: {response.status_code}, {response.text}")
|
||||||
|
|
||||||
|
joined_rooms_dict = json.loads(response.text)
|
||||||
|
|
||||||
|
return joined_rooms_dict
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
# $ curl -kXGET https://matrix.perthchat.org/_synapse/admin/v1/users/@dogpoo:perthchat.org/joined_rooms?access_token=ACCESS_TOKEN
|
# $ curl -kXGET https://matrix.perthchat.org/_synapse/admin/v1/users/@dogpoo:perthchat.org/joined_rooms?access_token=ACCESS_TOKEN
|
||||||
|
|
||||||
@ -358,7 +294,7 @@ def list_accounts():
|
|||||||
number_of_users = len(users)
|
number_of_users = len(users)
|
||||||
print("\nTotal amount of users: " + str(number_of_users))
|
print("\nTotal amount of users: " + str(number_of_users))
|
||||||
|
|
||||||
if number_of_users < 100:
|
if number_of_users < 100:
|
||||||
print(users)
|
print(users)
|
||||||
else:
|
else:
|
||||||
accounts_output_file = input("\nThere are too many users to list here, please specify a filename to print this data too: ")
|
accounts_output_file = input("\nThere are too many users to list here, please specify a filename to print this data too: ")
|
||||||
@ -389,7 +325,9 @@ def query_account(preset_username):
|
|||||||
else:
|
else:
|
||||||
print(f"Error querying account: {response.status_code}, {response.text}")
|
print(f"Error querying account: {response.status_code}, {response.text}")
|
||||||
|
|
||||||
return response.text
|
query_account_dict = json.loads(response.text)
|
||||||
|
|
||||||
|
return query_account_dict
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
# $ curl -kX GET https://matrix.perthchat.org/_synapse/admin/v2/users/@billybob:perthchat.org?access_token=ACCESS_TOKEN
|
# $ curl -kX GET https://matrix.perthchat.org/_synapse/admin/v2/users/@billybob:perthchat.org?access_token=ACCESS_TOKEN
|
||||||
@ -435,7 +373,7 @@ def quarantine_users_media():
|
|||||||
username = parse_username(username)
|
username = parse_username(username)
|
||||||
|
|
||||||
url = f"https://{hardcoded_variables.homeserver_url}/_synapse/admin/v1/user/@{username}:{hardcoded_variables.base_url}/media/quarantine?access_token={hardcoded_variables.access_token}"
|
url = f"https://{hardcoded_variables.homeserver_url}/_synapse/admin/v1/user/@{username}:{hardcoded_variables.base_url}/media/quarantine?access_token={hardcoded_variables.access_token}"
|
||||||
|
|
||||||
print("\n" + url + "\n")
|
print("\n" + url + "\n")
|
||||||
response = requests.post(url, verify=True)
|
response = requests.post(url, verify=True)
|
||||||
|
|
||||||
@ -464,7 +402,9 @@ def collect_account_data(preset_username):
|
|||||||
else:
|
else:
|
||||||
print(f"Error querying account: {response.status_code}, {response.text}")
|
print(f"Error querying account: {response.status_code}, {response.text}")
|
||||||
|
|
||||||
return response.text
|
account_data_dict = json.loads(response.text)
|
||||||
|
|
||||||
|
return account_data_dict
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
# $ curl -X GET https://matrix.perthchat.org/_synapse/admin/v1/users/@dogpoo:perthchat.org/accountdata?access_token=ACCESS_TOKEN
|
# $ curl -X GET https://matrix.perthchat.org/_synapse/admin/v1/users/@dogpoo:perthchat.org/accountdata?access_token=ACCESS_TOKEN
|
||||||
@ -486,7 +426,9 @@ def list_account_pushers(preset_username):
|
|||||||
else:
|
else:
|
||||||
print(f"Error querying account: {response.status_code}, {response.text}")
|
print(f"Error querying account: {response.status_code}, {response.text}")
|
||||||
|
|
||||||
return response.text
|
pusher_data_dict = json.loads(response.text)
|
||||||
|
|
||||||
|
return pusher_data_dict
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
# $ curl -X GET https://matrix.perthchat.org/_synapse/admin/v1/users/@dogpoo:perthchat.org/pushers
|
# $ curl -X GET https://matrix.perthchat.org/_synapse/admin/v1/users/@dogpoo:perthchat.org/pushers
|
||||||
@ -569,12 +511,12 @@ def check_user_account_exists(preset_username):
|
|||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
print("User ID is available.")
|
print("User ID is available.")
|
||||||
|
return True
|
||||||
elif response.status_code == 400:
|
elif response.status_code == 400:
|
||||||
print(f"User ID already taken.")
|
print(f"User ID already taken.")
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
print(f"Error querying account: {response.status_code}, {response.text}")
|
print(f"Error querying account: {response.status_code}, {response.text}")
|
||||||
|
|
||||||
return response.text
|
|
||||||
|
|
||||||
# Example:
|
# Example:
|
||||||
# $ curl -X GET /_synapse/admin/v1/username_available?username=dogpoo&access_token=ACCESS_TOKEN
|
# $ curl -X GET /_synapse/admin/v1/username_available?username=dogpoo&access_token=ACCESS_TOKEN
|
||||||
|
Loading…
Reference in New Issue
Block a user