First commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.venv/*
|
||||
.vscode/*
|
||||
profiles/*
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 urbnywrt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
@@ -0,0 +1,348 @@
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import psutil
|
||||
import subprocess
|
||||
|
||||
# ANSI escape codes for text color and formatting
|
||||
RESET = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
RED = '\033[91m' # Для повторяющихся цифр
|
||||
GREEN = '\033[92m' # Для возрастающих последовательностей
|
||||
YELLOW = '\033[93m' # Для убывающих последовательностей
|
||||
BLUE = '\033[94m' # Для палиндромов
|
||||
MAGENTA = '\033[95m' # Для специальных чисел (228, 1488, 322)
|
||||
|
||||
def find_ad_anynet_id(file_path):
|
||||
"""Ищет строку ad.anynet.id в файле."""
|
||||
try:
|
||||
with open(file_path, 'r') as file:
|
||||
for line in file:
|
||||
if line.startswith('ad.anynet.id='):
|
||||
return line.strip()
|
||||
except Exception as e:
|
||||
print(f"Error reading file {file_path}: {e}")
|
||||
return None
|
||||
|
||||
def get_file_content(file_path):
|
||||
"""Читает содержимое файла."""
|
||||
try:
|
||||
with open(file_path, 'r') as file:
|
||||
return file.read()
|
||||
except Exception as e:
|
||||
print(f"Error reading file {file_path}: {e}")
|
||||
return ""
|
||||
|
||||
def is_beautiful_id(id_str):
|
||||
"""Определяет, является ли ID 'красивым'."""
|
||||
# Проверка на повторяющиеся цифры (например, 1111)
|
||||
for i in range(len(id_str) - 3):
|
||||
if id_str[i] == id_str[i+1] == id_str[i+2] == id_str[i+3]:
|
||||
# Находим все вхождения повторяющейся цифры
|
||||
repeated_digit = id_str[i]
|
||||
part = ''
|
||||
j = i
|
||||
while j < len(id_str) and id_str[j] == repeated_digit:
|
||||
part += id_str[j]
|
||||
j += 1
|
||||
return {'type': 'repeated', 'part': part}
|
||||
|
||||
# Проверка на возрастающую последовательность (например, 1234)
|
||||
if ''.join(sorted(id_str)) == id_str and len(set(id_str)) == len(id_str):
|
||||
return {'type': 'ascending', 'part': id_str}
|
||||
|
||||
# Проверка на убывающую последовательность (например, 4321)
|
||||
if ''.join(sorted(id_str, reverse=True)) == id_str and len(set(id_str)) == len(id_str):
|
||||
return {'type': 'descending', 'part': id_str}
|
||||
|
||||
# Проверка на палиндром (симметричные числа, например, 1221)
|
||||
if id_str == id_str[::-1]:
|
||||
return {'type': 'palindrome', 'part': id_str}
|
||||
|
||||
# Проверка на наличие чисел 228, 1488, 322
|
||||
special_numbers = ['228', '1488', '322']
|
||||
for number in special_numbers:
|
||||
if number in id_str:
|
||||
return {'type': 'special_number', 'part': number}
|
||||
|
||||
# Временно закомментировано условие про короткие ID
|
||||
# if len(id_str) <= 9:
|
||||
# return {'type': 'short', 'part': id_str}
|
||||
|
||||
return None
|
||||
|
||||
def parse_profiles(profiles_dir):
|
||||
"""Парсит профили и ищет 'красивые' ID."""
|
||||
profiles = {}
|
||||
beautiful_profiles = {}
|
||||
|
||||
# Проверяем, существует ли директория
|
||||
if not os.path.exists(profiles_dir):
|
||||
print(f"Profiles directory '{profiles_dir}' does not exist!")
|
||||
return profiles, beautiful_profiles
|
||||
|
||||
# Проверяем содержимое директории
|
||||
dir_contents = os.listdir(profiles_dir)
|
||||
if not dir_contents:
|
||||
print(f"Profiles directory '{profiles_dir}' is empty!")
|
||||
return profiles, beautiful_profiles
|
||||
|
||||
print(f"Contents of profiles directory: {dir_contents}")
|
||||
|
||||
for profile_name in dir_contents:
|
||||
profile_path = os.path.join(profiles_dir, profile_name)
|
||||
if not os.path.isdir(profile_path):
|
||||
print(f"Skipping non-directory item: {profile_name}")
|
||||
continue
|
||||
|
||||
# Учитываем новую структуру: profiles/ADx/AnyDesk/system.conf
|
||||
anydesk_dir = os.path.join(profile_path, "AnyDesk")
|
||||
system_conf_path = os.path.join(anydesk_dir, "system.conf")
|
||||
|
||||
if not os.path.exists(system_conf_path):
|
||||
print(f"system.conf not found in profile: {profile_name}")
|
||||
continue
|
||||
|
||||
print(f"Parsing profile: {profile_name}")
|
||||
ad_anynet_id = find_ad_anynet_id(system_conf_path)
|
||||
if ad_anynet_id:
|
||||
id_value = ad_anynet_id.split('=')[1]
|
||||
profiles[profile_name] = {
|
||||
'id': id_value,
|
||||
'config_path': system_conf_path
|
||||
}
|
||||
beauty_info = is_beautiful_id(id_value)
|
||||
if beauty_info:
|
||||
beautiful_profiles[profile_name] = {'id': id_value, 'beauty_info': beauty_info}
|
||||
else:
|
||||
print(f"No ad.anynet.id found in system.conf for profile: {profile_name}")
|
||||
|
||||
return profiles, beautiful_profiles
|
||||
|
||||
|
||||
def highlight_beautiful_id(id_str, beauty_info):
|
||||
"""Подсвечивает 'красивую' часть ID."""
|
||||
part = beauty_info['part']
|
||||
type_ = beauty_info['type']
|
||||
|
||||
highlighted_id = id_str
|
||||
|
||||
if type_ == 'repeated':
|
||||
# Находим все вхождения повторяющейся части и подсвечиваем их красным
|
||||
start_index = 0
|
||||
while True:
|
||||
start_index = highlighted_id.find(part, start_index)
|
||||
if start_index == -1:
|
||||
break
|
||||
end_index = start_index + len(part)
|
||||
highlighted_part = RED + BOLD + part + RESET
|
||||
highlighted_id = highlighted_id[:start_index] + highlighted_part + highlighted_id[end_index:]
|
||||
start_index += len(highlighted_part) # Учитываем длину подсвеченной части
|
||||
|
||||
elif type_ == 'ascending':
|
||||
# Подсвечиваем возрастающие последовательности зеленым
|
||||
start_index = highlighted_id.find(part)
|
||||
if start_index != -1:
|
||||
end_index = start_index + len(part)
|
||||
highlighted_part = GREEN + BOLD + part + RESET
|
||||
highlighted_id = highlighted_id[:start_index] + highlighted_part + highlighted_id[end_index:]
|
||||
|
||||
elif type_ == 'descending':
|
||||
# Подсвечиваем убывающие последовательности желтым
|
||||
start_index = highlighted_id.find(part)
|
||||
if start_index != -1:
|
||||
end_index = start_index + len(part)
|
||||
highlighted_part = YELLOW + BOLD + part + RESET
|
||||
highlighted_id = highlighted_id[:start_index] + highlighted_part + highlighted_id[end_index:]
|
||||
|
||||
elif type_ == 'palindrome':
|
||||
# Подсвечиваем палиндромы синим
|
||||
start_index = highlighted_id.find(part)
|
||||
if start_index != -1:
|
||||
end_index = start_index + len(part)
|
||||
highlighted_part = BLUE + BOLD + part + RESET
|
||||
highlighted_id = highlighted_id[:start_index] + highlighted_part + highlighted_id[end_index:]
|
||||
|
||||
elif type_ == 'special_number':
|
||||
# Подсвечиваем специальные числа магентой
|
||||
start_index = highlighted_id.find(part)
|
||||
if start_index != -1:
|
||||
end_index = start_index + len(part)
|
||||
highlighted_part = MAGENTA + BOLD + part + RESET
|
||||
highlighted_id = highlighted_id[:start_index] + highlighted_part + highlighted_id[end_index:]
|
||||
|
||||
return highlighted_id
|
||||
|
||||
def kill_process(process_name):
|
||||
"""Убивает процесс по имени."""
|
||||
try:
|
||||
for proc in psutil.process_iter(['name']):
|
||||
if proc.info['name'] == process_name:
|
||||
proc.kill()
|
||||
except Exception as e:
|
||||
pass # Игнорируем ошибки при завершении процесса
|
||||
|
||||
def generate_profiles(output_dir, anydesk_exe_path, num_ids, timeout_seconds):
|
||||
"""Генерирует профили AnyDesk."""
|
||||
appdata_dir = os.getenv('APPDATA')
|
||||
anydesk_config_dir = os.path.join(appdata_dir, "AnyDesk")
|
||||
|
||||
# Определяем следующий доступный номер профиля
|
||||
existing_profiles = [name for name in os.listdir(output_dir) if name.startswith("AD") and name[2:].isdigit()]
|
||||
next_profile_number = max([int(name[2:]) for name in existing_profiles], default=0) + 1
|
||||
|
||||
# Прогресс-бар
|
||||
print("Generating profiles:")
|
||||
for i in range(next_profile_number, next_profile_number + num_ids):
|
||||
progress = int((i - next_profile_number + 1) / num_ids * 100)
|
||||
bar = f"[{'#' * (progress // 2):<50}] {progress}%"
|
||||
print(f"\r{bar}", end="")
|
||||
|
||||
profile_name = f"AD{i}"
|
||||
profile_dir = os.path.join(output_dir, profile_name)
|
||||
anydesk_subdir = os.path.join(profile_dir, "AnyDesk")
|
||||
os.makedirs(anydesk_subdir, exist_ok=True)
|
||||
|
||||
# Убиваем процесс AnyDesk
|
||||
kill_process("AnyDesk.exe")
|
||||
|
||||
# Запускаем AnyDesk
|
||||
subprocess.Popen([anydesk_exe_path], shell=True)
|
||||
time.sleep(timeout_seconds)
|
||||
|
||||
# Копируем конфигурацию с обработкой ошибок
|
||||
try:
|
||||
if os.path.exists(anydesk_config_dir):
|
||||
shutil.copytree(anydesk_config_dir, anydesk_subdir, dirs_exist_ok=True)
|
||||
except Exception as e:
|
||||
pass
|
||||
#print(f"\nError copying files for profile {profile_name}: {e}")
|
||||
|
||||
# Убиваем процесс AnyDesk снова
|
||||
kill_process("AnyDesk.exe")
|
||||
if os.path.exists(anydesk_config_dir):
|
||||
shutil.rmtree(anydesk_config_dir, ignore_errors=True)
|
||||
|
||||
print("\nAll profiles generated successfully!")
|
||||
|
||||
def apply_profile(profiles_dir, profile_name):
|
||||
"""Копирует конфигурацию профиля в папку AnyDesk пользователя и запускает AnyDesk."""
|
||||
appdata_dir = os.getenv('APPDATA')
|
||||
anydesk_config_dir = os.path.join(appdata_dir, "AnyDesk")
|
||||
|
||||
# Удаляем текущую конфигурацию AnyDesk
|
||||
if os.path.exists(anydesk_config_dir):
|
||||
shutil.rmtree(anydesk_config_dir, ignore_errors=True)
|
||||
|
||||
# Находим путь к конфигурации выбранного профиля
|
||||
profile_path = os.path.join(profiles_dir, profile_name)
|
||||
anydesk_subdir = os.path.join(profile_path, "AnyDesk")
|
||||
|
||||
if not os.path.exists(anydesk_subdir):
|
||||
print(f"Profile '{profile_name}' does not exist or is invalid!")
|
||||
return
|
||||
|
||||
# Копируем конфигурацию профиля в папку AnyDesk
|
||||
try:
|
||||
shutil.copytree(anydesk_subdir, anydesk_config_dir, dirs_exist_ok=True)
|
||||
print(f"Profile '{profile_name}' applied successfully!")
|
||||
except Exception as e:
|
||||
print(f"Error applying profile '{profile_name}': {e}")
|
||||
return
|
||||
|
||||
# Запускаем AnyDesk
|
||||
anydesk_exe_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "AnyDesk.exe")
|
||||
if not os.path.exists(anydesk_exe_path):
|
||||
print(f"Error: AnyDesk.exe not found at {anydesk_exe_path}")
|
||||
return
|
||||
|
||||
subprocess.Popen([anydesk_exe_path], shell=True)
|
||||
print("AnyDesk started with the selected profile.")
|
||||
|
||||
def main_menu():
|
||||
# Определяем директорию для профилей
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
profiles_dir = os.path.join(script_dir, "profiles")
|
||||
os.makedirs(profiles_dir, exist_ok=True)
|
||||
|
||||
# Путь к AnyDesk.exe по умолчанию
|
||||
default_anydesk_path = os.path.join(script_dir, "AnyDesk.exe")
|
||||
|
||||
# Парсим профили один раз для использования в меню
|
||||
profiles, beautiful_profiles = parse_profiles(profiles_dir)
|
||||
|
||||
while True:
|
||||
print("\nMain Menu:")
|
||||
print("1. Parse Profiles")
|
||||
print("2. Generate Profiles")
|
||||
print("3. View Beautiful IDs")
|
||||
print("4. Get Configuration by ID")
|
||||
print("5. Apply Profile to AnyDesk")
|
||||
print("6. Exit")
|
||||
choice = input("Enter your choice: ")
|
||||
|
||||
if choice == '1':
|
||||
profiles, beautiful_profiles = parse_profiles(profiles_dir)
|
||||
print("Profiles parsed successfully!")
|
||||
print("Found IDs:")
|
||||
for profile_name, profile_data in profiles.items():
|
||||
print(f"{profile_name}: {profile_data['id']}")
|
||||
|
||||
elif choice == '2':
|
||||
anydesk_exe_path = input(f"Enter the path to AnyDesk.exe (default: {default_anydesk_path}): ").strip()
|
||||
if not anydesk_exe_path:
|
||||
anydesk_exe_path = default_anydesk_path # Используем путь по умолчанию
|
||||
|
||||
num_ids = int(input("Enter the number of IDs to generate: "))
|
||||
timeout_seconds = int(input("Enter timeout duration (in seconds): "))
|
||||
|
||||
generate_profiles(profiles_dir, anydesk_exe_path, num_ids, timeout_seconds)
|
||||
print("Profiles generated successfully!")
|
||||
|
||||
# Обновляем список профилей после генерации
|
||||
profiles, beautiful_profiles = parse_profiles(profiles_dir)
|
||||
print("Updated Found IDs:")
|
||||
for profile_name, profile_data in profiles.items():
|
||||
print(f"{profile_name}: {profile_data['id']}")
|
||||
|
||||
elif choice == '3':
|
||||
print("\nBeautiful IDs:")
|
||||
if beautiful_profiles:
|
||||
for profile_name, data in beautiful_profiles.items():
|
||||
id_str = data['id']
|
||||
beauty_info = data['beauty_info']
|
||||
highlighted_id = highlight_beautiful_id(id_str, beauty_info)
|
||||
print(f"{profile_name}: {highlighted_id}")
|
||||
else:
|
||||
print("No beautiful IDs found.")
|
||||
|
||||
elif choice == '4':
|
||||
user_input = input("Enter ID to get configuration (or 'exit' to cancel): ")
|
||||
if user_input.lower() == 'exit':
|
||||
continue
|
||||
|
||||
found = False
|
||||
for profile_name, profile_data in profiles.items():
|
||||
if profile_data['id'] == user_input:
|
||||
print(f"Configuration for profile {profile_name}:")
|
||||
config_content = get_file_content(profile_data['config_path'])
|
||||
print(config_content)
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
print("ID not found.")
|
||||
|
||||
elif choice == '5':
|
||||
profile_name = input("Enter the profile name to apply (e.g., AD1): ")
|
||||
apply_profile(profiles_dir, profile_name)
|
||||
|
||||
elif choice == '6':
|
||||
print("Exiting...")
|
||||
break
|
||||
|
||||
else:
|
||||
print("Invalid choice. Please try again.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main_menu()
|
||||
@@ -0,0 +1,71 @@
|
||||
# Генератор и парсер профилей AnyDesk
|
||||
|
||||

|
||||

|
||||
|
||||
**Генератор и парсер профилей AnyDesk** — это инструмент для автоматической генерации, анализа и управления профилями AnyDesk.
|
||||
Скрипт позволяет создавать новые профили, находить "красивые" ID, просматривать конфигурации и применять выбранные профили в AnyDesk.
|
||||
|
||||
---
|
||||
|
||||
## Основные возможности
|
||||
|
||||
1. **Генерация профилей AnyDesk**:
|
||||
|
||||
- Создание новых профилей с уникальными ID.
|
||||
- Настройка времени ожидания для каждого профиля.
|
||||
2. **Поиск "красивых" ID**:
|
||||
|
||||
- Повторяющиеся цифры (например, `55555`).
|
||||
- Возрастающие (`1234`) и убывающие (`4321`) последовательности.
|
||||
- Палиндромы (например, `123321`).
|
||||
- Специальные числа: `228`, `1488`, `322`.
|
||||
3. **Просмотр конфигураций**:
|
||||
|
||||
- Возможность просмотра содержимого файла `system.conf` по конкретному ID.
|
||||
4. **Применение профиля в AnyDesk**:
|
||||
|
||||
- Копирование выбранной конфигурации в папку AnyDesk пользователя.
|
||||
- Запуск AnyDesk с новым профилем.
|
||||
5. **Подсветка "красивых" ID**:
|
||||
|
||||
- Различные цвета для разных типов "красивых" ID:
|
||||
- Повторяющиеся цифры: **красный**.
|
||||
- Возрастающие последовательности: **зеленый**.
|
||||
- Убывающие последовательности: **желтый**.
|
||||
- Палиндромы: **синий**.
|
||||
- Специальные числа: **магента**.
|
||||
|
||||
---
|
||||
|
||||
## Установка
|
||||
|
||||
1. **Python 3.6+**:
|
||||
|
||||
- Убедитесь, что Python установлен на вашем компьютере. Вы можете скачать его [здесь](https://www.python.org/downloads/).
|
||||
2. **Библиотеки**:
|
||||
|
||||
- Установите необходимую библиотеку `psutil` с помощью команды:
|
||||
|
||||
```bash
|
||||
pip install psutil
|
||||
```
|
||||
3. **AnyDesk**:
|
||||
|
||||
- Скрипт использует исполняемый файл `AnyDesk.exe`. Убедитесь, что он находится в одной папке со скриптом или укажите путь к нему.
|
||||
|
||||
---
|
||||
|
||||
## Использование
|
||||
|
||||
1. **Запуск программы**:
|
||||
Запустите скрипт в терминале:
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
1. **Главное меню**:
|
||||
```
|
||||
@@ -0,0 +1,107 @@
|
||||
# AnyDesk Profile Generator and Parser
|
||||
|
||||

|
||||

|
||||
|
||||
**AnyDesk Profile Generator and Parser** is a tool for automatically generating, analyzing, and managing AnyDesk profiles. The script allows you to create new profiles, find "beautiful" IDs, view configurations, and apply selected profiles in AnyDesk.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
1. **Generate AnyDesk Profiles**:
|
||||
|
||||
- Create new profiles with unique IDs.
|
||||
- Configure timeout for each profile generation.
|
||||
2. **Find "Beautiful" IDs**:
|
||||
|
||||
- Repeating digits (e.g., `55555`).
|
||||
- Ascending (`1234`) and descending (`4321`) sequences.
|
||||
- Palindromes (e.g., `123321`).
|
||||
- Special numbers: `228`, `1488`, `322`.
|
||||
3. **View Configurations**:
|
||||
|
||||
- View the contents of the `system.conf` file by a specific ID.
|
||||
4. **Apply a Profile to AnyDesk**:
|
||||
|
||||
- Copy the selected configuration to the user's AnyDesk folder.
|
||||
- Launch AnyDesk with the new profile.
|
||||
5. **Highlight "Beautiful" IDs**:
|
||||
|
||||
- Different colors for different types of "beautiful" IDs:
|
||||
- Repeating digits: **red**.
|
||||
- Ascending sequences: **green**.
|
||||
- Descending sequences: **yellow**.
|
||||
- Palindromes: **blue**.
|
||||
- Special numbers: **magenta**.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Python 3.6+**:
|
||||
|
||||
- Make sure Python is installed on your computer. You can download it [here](https://www.python.org/downloads/).
|
||||
2. **Libraries**:
|
||||
|
||||
- Install the required library `psutil` using the following command:
|
||||
|
||||
```bash
|
||||
pip install psutil
|
||||
```
|
||||
3. **AnyDesk**:
|
||||
|
||||
- The script uses the `AnyDesk.exe` executable file. Ensure it is located in the same folder as the script or specify its path.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Run the Program**:
|
||||
Start the script in the terminal:
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
2. **Main Menu** :
|
||||
After launching, you will see the main menu:
|
||||

|
||||
|
||||
## Notes on Color Display
|
||||
|
||||
* **Color Support**
|
||||
* The script uses ANSI escape codes for colored output.
|
||||
* For correct color display, it is recommended to use Windows Terminal or any modern terminal emulator that supports ANSI colors.
|
||||
* If you are using the default Windows Command Prompt (cmd.exe), colors may not display correctly due to limited ANSI support.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
1. "Beautiful" IDs:
|
||||

|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
project/
|
||||
│
|
||||
├── main.py # Main script
|
||||
├── profiles/ # Folder for storing profiles
|
||||
│ ├── AD1/
|
||||
│ │ └── AnyDesk/
|
||||
│ │ └── system.conf
|
||||
│ ├── AD2/
|
||||
│ │ └── AnyDesk/
|
||||
│ │ └── system.conf
|
||||
│ └── ...
|
||||
└── README.md # Documentation
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is distributed under the [MIT License](LICENSE). You can freely use, modify, and distribute this code.
|
||||
|
||||
## Author
|
||||
|
||||
Author: **urbnywrt**
|
||||
GitHub: [urbnywrt](https://github.com/urbnywrt)
|
||||
Reference in New Issue
Block a user