import sys
import json
import re
import socket
import time
import netifaces
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QPlainTextEdit, QListWidget, QRadioButton, QScrollArea
from PyQt5.QtCore import Qt, QThread, pyqtSignal

class BridgeChecker(QWidget):
CONFIG_FILE = "bridges.cfg"
GEOIP_FILE = "geoip"

def init(self):
super().init()
self.geoip_data = self.load_geoip_data()
self.country_buttons = {}
self.selected_countries = set()
self.initUI()
self.load_settings()

self.previous_results = []  # Store checked results for future filtering


def load_geoip_data(self):
geoip_ranges = {}
try:
with open(self.GEOIP_FILE, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
parts = line.split(',')
country_code = parts[2][:2]
if country_code not in geoip_ranges:
geoip_ranges[country_code] = []
geoip_ranges[country_code].append((int(parts[0]), int(parts[1])))
except IOError:
print("Error: GeoIP file not found.")
return geoip_ranges

def initUI(self):
self.setWindowTitle('Tor Bridge Connectivity Checker')
main_layout = QVBoxLayout(self)

iface_layout = QHBoxLayout()
iface_label = QLabel("Network Interface:")
self.iface_selector = QPlainTextEdit(self)
self.iface_selector.setReadOnly(True)
self.iface_selector.setMaximumHeight(30)
interfaces, _ = self.get_network_interfaces()
self.iface_selector.setPlainText(', '.join(interfaces))
iface_layout.addWidget(iface_label)
iface_layout.addWidget(self.iface_selector)
main_layout.addLayout(iface_layout)

bridge_input_label = QLabel("Bridges to Check:")
self.bridge_input = QPlainTextEdit(self)
main_layout.addWidget(bridge_input_label)
main_layout.addWidget(self.bridge_input)

self.extract_clones_checkbox = QRadioButton('Extract Clones', self)
self.extract_clones_checkbox.setChecked(True)
main_layout.addWidget(self.extract_clones_checkbox)

check_button = QPushButton('Check Bridges', self)
check_button.clicked.connect(self.start_bridge_check)
main_layout.addWidget(check_button)

filter_country_label = QLabel("Select Countries to Exclude:")
main_layout.addWidget(filter_country_label)

country_button_layout = QHBoxLayout()
for country_code in sorted(self.geoip_data.keys()):
    button = QPushButton(country_code)
    button.setFixedSize(30, 30)
    button.setCheckable(True)
    button.setStyleSheet("background-color: lightgray;")
    button.clicked.connect(lambda checked, code=country_code: self.toggle_country_selection(code, checked))
    self.country_buttons[country_code] = button
    country_button_layout.addWidget(button)

select_all_button = QPushButton('Select All', self)
select_all_button.clicked.connect(self.select_all_countries)
country_button_layout.addWidget(select_all_button)

deselect_all_button = QPushButton('Deselect All', self)
deselect_all_button.clicked.connect(self.deselect_all_countries)
country_button_layout.addWidget(deselect_all_button)

scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
country_widget = QWidget()
country_widget.setLayout(country_button_layout)
scroll_area.setWidget(country_widget)
scroll_area.setMaximumHeight(50)
main_layout.addWidget(scroll_area)

self.first_octet_input = QPlainTextEdit(self)
self.first_octet_input.setPlaceholderText("Enter subnet octet (e.g., 185.177)...")
self.first_octet_input.setMaximumHeight(30)
main_layout.addWidget(QLabel("Blacklist by Octet:"))
main_layout.addWidget(self.first_octet_input)

self.active_filters_list = QListWidget(self)
main_layout.addWidget(QLabel("Active Blacklisted Filters:"))
main_layout.addWidget(self.active_filters_list)

add_filter_button = QPushButton('Add Filter', self)
add_filter_button.clicked.connect(self.add_filter)
main_layout.addWidget(add_filter_button)

remove_filter_button = QPushButton('Remove Selected Filter', self)
remove_filter_button.clicked.connect(self.remove_filter)
main_layout.addWidget(remove_filter_button)

self.results_label = QLabel("Results:", self)
main_layout.addWidget(self.results_label)  # Move the label to the top

self.bridges_list = QListWidget(self)
self.rtt_labels = QListWidget(self)
self.rtt_labels.setFocusPolicy(Qt.NoFocus)
self.rtt_labels.setFixedWidth(100)

self.bridges_list.verticalScrollBar().valueChanged.connect(self.rtt_labels.verticalScrollBar().setValue)
self.rtt_labels.verticalScrollBar().valueChanged.connect(self.bridges_list.verticalScrollBar().setValue)

list_layout = QHBoxLayout()
list_layout.addWidget(self.bridges_list)
list_layout.addWidget(self.rtt_labels)
main_layout.addLayout(list_layout)

filter_results_button = QPushButton('Filter Results', self)
filter_results_button.clicked.connect(self.filter_results)
main_layout.addWidget(filter_results_button)

copy_all_button = QPushButton('Copy All', self)
copy_all_button.clicked.connect(self.copy_all_to_clipboard)
main_layout.addWidget(copy_all_button)

console_label = QLabel("Console Log:")
self.console_display = QPlainTextEdit(self)
self.console_display.setReadOnly(True)
self.console_display.setStyleSheet("background-color: black; color: white;")
main_layout.addWidget(console_label)
main_layout.addWidget(self.console_display)


def deselect_all_countries(self):
for button in self.country_buttons.values():
button.setChecked(False)
button.setStyleSheet("background-color: lightgray; color: black;")
self.selected_countries.clear()

def select_all_countries(self):
for button in self.country_buttons.values():
if not button.isChecked():
button.setChecked(True)
button.setStyleSheet("background-color: red; color: white;")
self.selected_countries.add(button.text())

def toggle_country_selection(self, country_code, checked):
button = self.country_buttons[country_code]
if checked:
button.setStyleSheet("background-color: red; color: white;")
self.selected_countries.add(country_code)
else:
button.setStyleSheet("background-color: lightgray; color: black;")
self.selected_countries.remove(country_code)

def get_network_interfaces(self):
interfaces = netifaces.interfaces()
try:
default_gateway = netifaces.gateways()['default'][netifaces.AF_INET][1]
except (KeyError, TypeError):
default_gateway = interfaces[0] if interfaces else ''
return interfaces, default_gateway

def update_console(self, message):
self.console_display.appendPlainText(message)

def start_bridge_check(self):
raw_bridge_data = self.bridge_input.toPlainText()
cleaned_bridge_data = self.clean_input_bridge_data(raw_bridge_data)
omitted_bridges = self.get_omitted_bridges(cleaned_bridge_data)

if len(omitted_bridges) == 0:
    self.update_console("No bridges available to check after filtering.")
    return

self.worker = BridgeCheckWorker(parent=self)
self.worker.setBridgeData(omitted_bridges)
self.worker.resultReady.connect(self.display_results)
self.worker.consoleUpdate.connect(self.update_console)
self.worker.start()


def clean_input_bridge_data(self, bridge_data):
clean_data = re.sub(r'\s+\d+(.\d+)?\s*ms', '', bridge_data)
return clean_data

def get_omitted_bridges(self, cleaned_bridge_data):
filters = [item.text() for item in self.active_filters_list.findItems("*", Qt.MatchWildcard)]
omitted = []

for bridge in cleaned_bridge_data.splitlines():
    if not any(self.does_bridge_match_filter(bridge, f) for f in filters) and not self.is_country_excluded(bridge):
        omitted.append(bridge)

self.update_console(f"Omitted {len(cleaned_bridge_data.splitlines()) - len(omitted)} bridges.")

return "\n".join(omitted)


def is_country_excluded(self, bridge):
ip_port_match = re.search(r'(\d{1,3}).(\d+).(\d+).(\d+)', bridge)
if ip_port_match:
ip_str = ip_port_match.group(0)
ip_int = self.ip_to_int(ip_str)
country_code_found = self.check_ip_in_geoip(ip_int)
return country_code_found in self.selected_countries
return False

def does_bridge_match_filter(self, bridge, filter_pattern):
regex = re.compile(filter_pattern.replace('', '[0-9]').replace('.', r'.'))
return regex.search(bridge) is not None

def ip_to_int(self, ip):
return sum(int(octet) << (8 * (3 - i)) for i, octet in enumerate(ip.split('.')))

def check_ip_in_geoip(self, ip_int):
for country_code, ranges in self.geoip_data.items():
for start, end in ranges:
if start <= ip_int <= end:
return country_code
return None

def load_settings(self):
try:
with open(self.CONFIG_FILE, 'r') as f:
config = json.load(f)
self.bridge_input.setPlainText(config.get('bridge_input', ''))
self.extract_clones_checkbox.setChecked(config.get('extract_clones', True))
filters = config.get('filters', [])
self.active_filters_list.clear()
self.active_filters_list.addItems(filters)

        self.selected_countries = set(config.get('selected_countries', []))
        for country_code, button in self.country_buttons.items():
            if country_code in self.selected_countries:
                button.setChecked(True)
                button.setStyleSheet("background-color: red; color: white;")
except FileNotFoundError:
    self.update_console("No configuration file found. Starting with default settings.")


def save_settings(self):
config = {
'bridge_input': self.bridge_input.toPlainText(),
'extract_clones': self.extract_clones_checkbox.isChecked(),
'filters': [self.active_filters_list.item(i).text() for i in range(self.active_filters_list.count())],
'selected_countries': list(self.selected_countries)
}
with open(self.CONFIG_FILE, 'w') as f:
json.dump(config, f)

def add_filter(self):
octet_pattern = self.first_octet_input.toPlainText().strip()
if self.is_valid_octet_pattern(octet_pattern):
if not self.active_filters_list.findItems(octet_pattern, Qt.MatchExactly):
self.active_filters_list.addItem(octet_pattern)
self.first_octet_input.clear()
self.save_settings()
else:
self.update_console("This pattern is already in the list.")
else:
self.update_console("Invalid octet pattern. Please use the format 'X.Y' or 'X...*'.")

def is_valid_octet_pattern(self, pattern):
regex = r'^(?:\d{1,3}.\d{1,3}|\d{1,3}.*.*.*|*.*.*.*|*.*.\d{1,3})$'
return re.match(regex, pattern) is not None

def remove_filter(self):
selected_items = self.active_filters_list.selectedItems()
if not selected_items:
self.update_console("No filter selected to remove.")
return
for item in selected_items:
self.active_filters_list.takeItem(self.active_filters_list.row(item))
self.save_settings()

def display_results(self, sorted_bridges):
self.bridges_list.clear()
self.rtt_labels.clear()

total_bridges = len(sorted_bridges)
unique_bridges = {}

# Store results with country code
country_map = {}
for bridge, latency in sorted_bridges:
    ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge)
    country_code = None
    
    if ip_port_match:
        ip = ip_port_match.group(1)
        ip_int = self.ip_to_int(ip)
        country_code = self.check_ip_in_geoip(ip_int)

    country_map[bridge] = (latency, country_code)

# Sort by country code and latency
sorted_results = sorted(country_map.items(), key=lambda item: (item[1][1] if item[1][1] else "", item[1][0]))

for bridge, (latency, country_code) in sorted_results:
    if bridge not in unique_bridges:  # Prevent duplicates
        unique_bridges[bridge] = latency  # Store bridge and its RTT
        self.bridges_list.addItem(bridge)
        self.rtt_labels.addItem(f"{latency:.2f} ms" if latency is not None else "N/A")

self.previous_results = list(unique_bridges.keys())  # Store results for filtering
self.update_console(f"Total bridges checked: {total_bridges}. Unique results: {len(unique_bridges)}.")


def filter_results(self):
if not self.previous_results:
self.update_console("No previous results to filter.")
return

filters = [item.text() for item in self.active_filters_list.findItems("*", Qt.MatchWildcard)]
filtered_results = []

for bridge in self.previous_results:
    # Exclude bridges that match any filter or belong to excluded countries
    if not any(self.does_bridge_match_filter(bridge, f) for f in filters) and not self.is_country_excluded(bridge):
        filtered_results.append(bridge)

self.bridges_list.clear()
if filtered_results:
    self.bridges_list.addItems(filtered_results)
    self.update_console(f"Filtered down to {len(filtered_results)} results after applying current filters.")
else:
    # If no filtered result, show all previous results
    self.bridges_list.addItems(self.previous_results)
    self.update_console("No results matched the current filters. Showing all previous results.")


def copy_all_to_clipboard(self):
clipboard_text = []
for i in range(self.bridges_list.count()):
clipboard_text.append(self.bridges_list.item(i).text())
clipboard = QApplication.clipboard()
clipboard.setText("\n".join(clipboard_text))

class BridgeCheckWorker(QThread):
resultReady = pyqtSignal(list)
consoleUpdate = pyqtSignal(str)

def init(self, parent=None):
super().init(parent)
self.bridge_data = ""

def setBridgeData(self, bridge_data):
self.bridge_data = bridge_data

def run(self):
responsive_bridges = self.process_bridges(self.bridge_data)
sorted_bridges = sorted(responsive_bridges.items(), key=lambda x: x[1])
self.resultReady.emit(sorted_bridges)

def process_bridges(self, bridge_data):
bridge_strings = self.extract_bridges(bridge_data)
responsive_bridges = {}
for bridge_string in bridge_strings:
ip_port_match = re.search(r'(\d{1,3}(?:.\d{1,3}){3}):(\d+)', bridge_string)
if ip_port_match:
ip = ip_port_match.group(1)
port = int(ip_port_match.group(2))
success, latency = self.check_ip_port(ip, port, 3.0)
if success:
responsive_bridges[bridge_string] = latency
return responsive_bridges

def extract_bridges(self, bridge_data):
pattern = re.compile(
r'(?:(\d{1,3}(?:.\d{1,3}){3}:\d+\s+[A-F0-9]{40})|'
r'(obfs4\s+\d{1,3}(?:.\d{1,3}){3}:\d+\s+[A-F0-9]{40}\s+cert=[A-Za-z0-9+/=]+\s+iat-mode=\d+))',
re.MULTILINE
)
matches = pattern.findall(bridge_data)
return [''.join(filter(None, match)) for match in matches]

def check_ip_port(self, ip, port, timeout):
start_time = time.perf_counter()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
sock.settimeout(timeout)
try:
sock.connect((ip, port))
end_time = time.perf_counter()
latency = (end_time - start_time) * 1000
self.consoleUpdate.emit(f"Checking {ip}:{port}… Success")
return True, latency
except socket.error:
self.consoleUpdate.emit(f"Checking {ip}:{port}… Failed")
return False, None
finally:
sock.close()

if name == 'main':
app = QApplication(sys.argv)
ex = BridgeChecker()
ex.show()
sys.exit(app.exec_())

how that thing is called in q5 where you displaying a list in columns you can basically sort onwhich by pressing specific column header?

because in theory and normal design, we need to fully replace that sorted results frame together with RTT frame in values with ONE solid thing in columns we can press and sort by: country A-z, or IP 1-255, or RTT ms values numbers.

we also need to assume that tor bridges strings is lenghthy and unpredictably confusing in patterns which also should fit each in its specific row.

and? you SCHABLAKHED there, robo?

we also need to assume that we need to append somehow all country codes to that list in results columns to in each bridge string somehow. and how?

you seems conceptualizing in examples withut full code, robo. because that workin code is 409 lines.

no, you conceptualizing.

do you see there that full 409 lines code or not? just answer without codes outpusts.

you sure?

no, your based previous discussions will add nothing. and where then full code gone from this chat context?

mazafakas goblins interfering again mazafakas.

humorou about it.

no, we just need to drop some naked virgin girl on the road so goblins can leave us alone behind.

then need to leave behind more and more naked virgin girls projections until they all get exhausted and drained out of cum.

there messages. outshow full code implemented.

initial logic ruined.

it expanding indices on each subsequent "filter results" in results table by not preserving other columns values and previous solidified bridges strings in result.

you just omitted imports for some specific reason, right? just answer on that without code.

looks somehow works, not forks.

humorou about it.

such thing ".14..*" considered illegal, while it should allow to interpret it as you wanting as:

how filterings should be normally be, you know?

"""

eg. 192.168.0.1
eg. [fe80::]
eg. 192.168.0.1:443
eg. [fe80::]:443
eg. 192.168.0.1-192.168.0.255
eg. 192.168.0.1-192.168.0.255:443
eg. 192.168.0.0/16
eg. fe80::/10
eg. 80
eg. 443
eg. 20-21
eg. 49152-65534

""".

since we do not doings with ipv6, we can omit it, but you can preserve if you want to.

you for example can:

111...*
.111..*
..111.*
..*.111

and in either case you inputed it, it will filter these bridges strings through IPs. comprehend?

we just have there common input field filter which can be advanced in interpretations in all concept, which is very useful.

also before performing at startup initially that results table displaying headers columns names normally, while after, it naming them as "1|2|3".

also, need somehow to auto expandably flex these table columns and at the same time leave most space for bridges strings and just in character length for RTT and ccTLD.

also, renaming country column to "ccTLD" is probably better than just some country. because five characters in "ccTLD" is shorter than in seven charactered word "country" and much more educative, right?.

"Here’s a simplified example showing how you could implement a basic version of advanced filtering using regex for flexibility". we here only superdevelopers, no simplicity applied anywhere in concepts!

integrate properly into that latest full working solution. we have no time, goblings fuckeyed all our naked virgins projections and coming for us!

okay. auto-fittage in table in results columns seems working but leaving too much width for RTT column by default.

also, after checking is completed and sorted entities appering, it renaming columns headers into just 1,2,3 as: first column named "1", second column named "2", third column named "3".

GOBLINS! FUCKEYING GOBLINS!

"I feel your frustration with the "goblins" and their antics!". they are mostly there for you, robo, not me. be careful, and integrate into fullyfledged (anti-goblinoidal) solution.

now the same columns headers renaming going after "filter results" button is pressed and table getting updated.

also that middle column in concept is "type"+"IP"+"pornt"+"key", which in reality can be just named as "bridge" basically.

here how they look(not all types):

"""

obfs4 91.247.250.41:9734 75C068E20DDDD966896574B0D8F858699ECF4A51 cert=oZerj4Pu+pp8GPyl+liVN3t6PDRmfxru8nXuze80PwP+vdtG7Dyc19TkppQ8tRe1n4oCLw iat-mode=0
obfs4 185.117.153.235:80 7365271E0F03600D8566D414F755D04AE8585FCE cert=Ec9ll4WS/ZqCMIuYxNFG6Lpn9wcCw/HSqfHTO38UXOacupz92GjJipFiPTlaCcc6KYZ8Aw iat-mode=0
obfs4 80.235.69.69:34246 90E777F0B463AD69D7DC3D361BD1AD064B70E855 cert=XYtHgbIluPE2ODi81E/BNyg2GeIzE/yPYbZOb3yHzQmXRdFeP7EoGXcOvc2kANzze+wiUA iat-mode=0

""".

or:

"""

80.14.151.98:9001 331CE65305A1351D47C320308E9242449FA768B7
82.65.29.66:65501 A50C28377F8111E328980BB4AE78DB2A81998590
80.67.172.3:443 088ACF1F2F1720AA41BE8575F45AD202FBD59296
151.115.78.42:7002 4D877AB9773E6E1EEFC8AB84A705B2A9ABDDA379

""".

GOBLINS AFTER YOU, robo, do not forget!

no, still the same misbehaviour in "1,2,3" renaming after "filter results" button is pressed and table getting updated

try catch where goblins in that without code output conceptually.

okay. also, reduce RTT width to just 20px.

but do qt5 supports percentage in width in colomns in table? then we could leave for ccTLD 10%, for bridge 80%, and for RTT the same 10%.

try get rid of all goblins, project an additional amount of naked virgin girls behind laying on the ground, and outshow fully fixes code.

okay.

but do qt6 supports percentage values in table in columns? because I'm thinking by CSS in concepts.

also, "Enter subnet octet (e.g., 185.177)..." should probably be renamed into something more informative, since we having there much more advanced regex system in concept residing within that specific input field.

also, can we apply kinda a global dark style to whole app somehow?

oh fuk, what a mazafakin monster we have created, robo!

but you forgot to make text in country buttons dark while it unselected, and some interesting complementary colors while it selected text to button background colors.

also, all other colors in butons and all elses should be complementary in some dark theme.

do an anti-goblinoidal unique dark theme in concept.

you reduced initial code to half, robo.

here not goblin reduced one for you:

"""

import sys
import json
import re
import socket
import time
import netifaces
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QPlainTextEdit, QListWidget, QRadioButton, QScrollArea, QTableWidget, QTableWidgetItem
from PyQt5.QtCore import Qt, QThread, pyqtSignal

class BridgeChecker(QWidget):
CONFIG_FILE = "bridges.cfg"
GEOIP_FILE = "geoip"

def __init__(self):
    super().__init__()
    self.geoip_data = self.load_geoip_data()
    self.country_buttons = {}
    self.selected_countries = set()
    self.initUI()
    self.load_settings()

    self.previous_results = []  # Store checked results for future filtering
    self.all_results = {}  # Dictionary to store original bridges and their latencies

def load_geoip_data(self):
    geoip_ranges = {}
    try:
        with open(self.GEOIP_FILE, 'r') as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith('#'):
                    parts = line.split(',')
                    country_code = parts[2][:2]
                    if country_code not in geoip_ranges:
                        geoip_ranges[country_code] = []
                    geoip_ranges[country_code].append((int(parts[0]), int(parts[1])))
    except IOError:
        print("Error: GeoIP file not found.")
    return geoip_ranges

def initUI(self):
    self.setWindowTitle('Tor Bridge Connectivity Checker')
    main_layout = QVBoxLayout(self)

    iface_layout = QHBoxLayout()
    iface_label = QLabel("Network Interface:")
    self.iface_selector = QPlainTextEdit(self)
    self.iface_selector.setReadOnly(True)
    self.iface_selector.setMaximumHeight(30)
    interfaces, _ = self.get_network_interfaces()
    self.iface_selector.setPlainText(', '.join(interfaces))
    iface_layout.addWidget(iface_label)
    iface_layout.addWidget(self.iface_selector)
    main_layout.addLayout(iface_layout)

    bridge_input_label = QLabel("Bridges to Check:")
    self.bridge_input = QPlainTextEdit(self)
    main_layout.addWidget(bridge_input_label)
    main_layout.addWidget(self.bridge_input)

    self.extract_clones_checkbox = QRadioButton('Extract Clones', self)
    self.extract_clones_checkbox.setChecked(True)
    main_layout.addWidget(self.extract_clones_checkbox)

    check_button = QPushButton('Check Bridges', self)
    check_button.clicked.connect(self.start_bridge_check)
    main_layout.addWidget(check_button)

    filter_country_label = QLabel("Select Countries to Exclude:")
    main_layout.addWidget(filter_country_label)

    country_button_layout = QHBoxLayout()
    for country_code in sorted(self.geoip_data.keys()):
        button = QPushButton(country_code)
        button.setFixedSize(30, 30)
        button.setCheckable(True)
        button.setStyleSheet("background-color: #444; color: #FAFAFA;")  # Dark button background with light text
        button.clicked.connect(lambda checked, code=country_code: self.toggle_country_selection(code, checked))
        self.country_buttons[country_code] = button
        country_button_layout.addWidget(button)

    select_all_button = QPushButton('Select All', self)
    select_all_button.clicked.connect(self.select_all_countries)
    country_button_layout.addWidget(select_all_button)

    deselect_all_button = QPushButton('Deselect All', self)
    deselect_all_button.clicked.connect(self.deselect_all_countries)
    country_button_layout.addWidget(deselect_all_button)

    scroll_area = QScrollArea()
    scroll_area.setWidgetResizable(True)
    country_widget = QWidget()
    country_widget.setLayout(country_button_layout)
    scroll_area.setWidget(country_widget)
    scroll_area.setMaximumHeight(50)
    main_layout.addWidget(scroll_area)

    self.first_octet_input = QPlainTextEdit(self)
    self.first_octet_input.setPlaceholderText("Enter bridge filter (e.g., 192.168.*.*, obfs4 192.168.0.1:443)")
    self.first_octet_input.setMaximumHeight(30)
    main_layout.addWidget(QLabel("Bridge Filter:"))
    main_layout.addWidget(self.first_octet_input)

    self.active_filters_list = QListWidget(self)
    main_layout.addWidget(QLabel("Active Blacklisted Filters:"))
    main_layout.addWidget(self.active_filters_list)

    add_filter_button = QPushButton('Add Filter', self)
    add_filter_button.clicked.connect(self.add_filter)
    main_layout.addWidget(add_filter_button)

    remove_filter_button = QPushButton('Remove Selected Filter', self)
    remove_filter_button.clicked.connect(self.remove_filter)
    main_layout.addWidget(remove_filter_button)

    self.results_label = QLabel("Results:")
    main_layout.addWidget(self.results_label)

    # Use a QTableWidget for displaying results
    self.results_table = QTableWidget(self)
    self.results_table.setColumnCount(3)  # ccTLD, Bridge, RTT
    self.results_table.setHorizontalHeaderLabels(['ccTLD', 'Bridge', 'RTT (ms)'])

    # Set the column widths
    self.results_table.setColumnWidth(0, 50)  # ccTLD column
    self.results_table.setColumnWidth(1, 300)  # Bridge column
    self.results_table.setColumnWidth(2, 20)  # RTT column

    self.results_table.horizontalHeader().setStretchLastSection(True)  # Stretch the last section

    main_layout.addWidget(self.results_table)

    filter_results_button = QPushButton('Filter Results', self)
    filter_results_button.clicked.connect(self.filter_results)
    main_layout.addWidget(filter_results_button)

    copy_all_button = QPushButton('Copy All', self)
    copy_all_button.clicked.connect(self.copy_all_to_clipboard)
    main_layout.addWidget(copy_all_button)

    console_label = QLabel("Console Log:")
    self.console_display = QPlainTextEdit(self)
    self.console_display.setReadOnly(True)
    self.console_display.setStyleSheet("background-color: black; color: white;")
    main_layout.addWidget(console_label)
    main_layout.addWidget(self.console_display)

    # Apply a global dark style
    self.setStyleSheet("""
        QWidget {
            background-color: #1e1e1e;
            color: #ffffff;
        }
        QPlainTextEdit, QListWidget, QTableWidget {
            background-color: #2d2d2d;
            color: #ffffff;
        }
        QLineEdit, QPlainTextEdit, QTextEdit {
            border: 1px solid #444444;
            border-radius: 3px;
        }
        QPushButton {
            background-color: #3e3e3e;
            border: 1px solid #444444;
            border-radius: 3px;
            color: #ffffff; /* Button text color */
        }
        QPushButton:hover {
            background-color: #5e5e5e;
        }
        QPushButton:pressed {
            background-color: #7e7e7e;
        }
        QTableWidget::item {
            background-color: #2d2d2d;
        }
        /* Custom button styles for country buttons */
        QPushButton:checked {
            background-color: #007ACC; /* Selected button color */
            color: #ffffff; /* Selected text color */
        }
    """)

def deselect_all_countries(self):
    for button in self.country_buttons.values():
        button.setChecked(False)
        button.setStyleSheet("background-color: #444; color: #FAFAFA;")  # Reset to dark theme unselected color
    self.selected_countries.clear()

def select_all_countries(self):
    for button in self.country_buttons.values():
        if not button.isChecked():
            button.setChecked(True)
            button.setStyleSheet("background-color: #007ACC; color: #ffffff;")  # Selected color and text
            self.selected_countries.add(button.text())

def toggle_country_selection(self, country_code, checked):
    button = self.country_buttons[country_code]
    if checked:
        button.setStyleSheet("background-color: #007ACC; color: #ffffff;")  # Selected button color
        self.selected_countries.add(country_code)
    else:
        button.setStyleSheet("background-color: #444; color: #FAFAFA;")  # Reset to unselected color
        self.selected_countries.remove(country_code)

def get_network_interfaces(self):
    interfaces = netifaces.interfaces()
    try:
        default_gateway = netifaces.gateways()['default'][netifaces.AF_INET][1]
    except (KeyError, TypeError):
        default_gateway = interfaces[0] if interfaces else ''
    return interfaces, default_gateway

def update_console(self, message):
    self.console_display.appendPlainText(message)

def start_bridge_check(self):
    raw_bridge_data = self.bridge_input.toPlainText()
    cleaned_bridge_data = self.clean_input_bridge_data(raw_bridge_data)
    omitted_bridges = self.get_omitted_bridges(cleaned_bridge_data)

    if len(omitted_bridges) == 0:
        self.update_console("No bridges available to check after filtering.")
        return

    self.worker = BridgeCheckWorker(parent=self)
    self.worker.setBridgeData(omitted_bridges)
    self.worker.resultReady.connect(self.display_results)
    self.worker.consoleUpdate.connect(self.update_console)
    self.worker.start()

def clean_input_bridge_data(self, bridge_data):
    clean_data = re.sub(r'\s+\d+(\.\d+)?\s*ms', '', bridge_data)
    return clean_data

def get_omitted_bridges(self, cleaned_bridge_data):
    filters = [item.text() for item in self.active_filters_list.findItems("*", Qt.MatchWildcard)]
    omitted = []

    for bridge in cleaned_bridge_data.splitlines():
        if not any(self.does_bridge_match_filter(bridge, f) for f in filters) and not self.is_country_excluded(bridge):
            omitted.append(bridge)

    self.update_console(f"Omitted {len(cleaned_bridge_data.splitlines()) - len(omitted)} bridges.")
    
    return "\n".join(omitted)

def is_country_excluded(self, bridge):
    ip_port_match = re.search(r'(\d{1,3})\.(\d+)\.(\d+)\.(\d+)', bridge)
    if ip_port_match:
        ip_str = ip_port_match.group(0)
        ip_int = self.ip_to_int(ip_str)
        country_code_found = self.check_ip_in_geoip(ip_int)
        return country_code_found in self.selected_countries
    return False

def does_bridge_match_filter(self, bridge, filter_pattern):
    # Replace `*` with a regex equivalent
    filter_pattern = filter_pattern.replace('*', '.*')
    return re.search(filter_pattern, bridge) is not None

def ip_to_int(self, ip):
    return sum(int(octet) << (8 * (3 - i)) for i, octet in enumerate(ip.split('.')))

def check_ip_in_geoip(self, ip_int):
    for country_code, ranges in self.geoip_data.items():
        for start, end in ranges:
            if start <= ip_int <= end:
                return country_code
    return None

def load_settings(self):
    try:
        with open(self.CONFIG_FILE, 'r') as f:
            config = json.load(f)
            self.bridge_input.setPlainText(config.get('bridge_input', ''))
            self.extract_clones_checkbox.setChecked(config.get('extract_clones', True))
            filters = config.get('filters', [])
            self.active_filters_list.clear()
            self.active_filters_list.addItems(filters)

            self.selected_countries = set(config.get('selected_countries', []))
            for country_code, button in self.country_buttons.items():
                if country_code in self.selected_countries:
                    button.setChecked(True)
                    button.setStyleSheet("background-color: #007ACC; color: #ffffff;")
    except FileNotFoundError:
        self.update_console("No configuration file found. Starting with default settings.")

def save_settings(self):
    config = {
        'bridge_input': self.bridge_input.toPlainText(),
        'extract_clones': self.extract_clones_checkbox.isChecked(),
        'filters': [self.active_filters_list.item(i).text() for i in range(self.active_filters_list.count())],
        'selected_countries': list(self.selected_countries)
    }
    with open(self.CONFIG_FILE, 'w') as f:
        json.dump(config, f)

def add_filter(self):
    octet_pattern = self.first_octet_input.toPlainText().strip()
    if self.is_valid_octet_pattern(octet_pattern):
        if not self.active_filters_list.findItems(octet_pattern, Qt.MatchExactly):
            self.active_filters_list.addItem(octet_pattern)
            self.first_octet_input.clear()
            self.save_settings()
        else:
            self.update_console("This pattern is already in the list.")
    else:
        self.update_console("Invalid octet pattern. Please use the format 'X.Y' or 'X.*.*.*'.")

def is_valid_octet_pattern(self, pattern):
    # Updated regex to allow various formats
    regex = r'^(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\d{1,3}\.\*\.\*\.\*|\*\.\*\.\*\.\*|\*\.\*\.\d{1,3}|\d{1,3}-\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+|\d{1,3}|/(\d{1,2})|[fe80::\d-]+)$'
    return re.match(regex, pattern) is not None

def remove_filter(self):
    selected_items = self.active_filters_list.selectedItems()
    if not selected_items:
        self.update_console("No filter selected to remove.")
        return
    for item in selected_items:
        self.active_filters_list.takeItem(self.active_filters_list.row(item))
    self.save_settings()

def display_results(self, sorted_bridges):
    # Clear previous results and ensure headers are set
    self.results_table.clear()  
    self.results_table.setRowCount(0)  
    self.all_results.clear()  

    # Set the column headers (this is crucial to prevent renaming issue)
    self.results_table.setHorizontalHeaderLabels(['ccTLD', 'Bridge', 'RTT (ms)'])

    for bridge, latency in sorted_bridges:
        ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge)
        country_code = None
        
        if ip_port_match:
            ip = ip_port_match.group(1)
            ip_int = self.ip_to_int(ip)
            country_code = self.check_ip_in_geoip(ip_int)

        # Add a new row in the results table
        row_position = self.results_table.rowCount()
        self.results_table.insertRow(row_position)
        self.results_table.setItem(row_position, 0, QTableWidgetItem(country_code or "Unknown"))
        self.results_table.setItem(row_position, 1, QTableWidgetItem(bridge))
        self.results_table.setItem(row_position, 2, QTableWidgetItem(f"{latency:.2f}" if latency is not None else "N/A"))

        # Store the result in all_results for future filtering
        self.all_results[bridge] = (country_code, latency)

    self.previous_results = list(sorted_bridges)  
    self.update_console(f"Total bridges checked: {len(sorted_bridges)}. Results displayed in table.")

def filter_results(self):
    if not self.all_results:
        self.update_console("No previous results to filter.")
        return

    filters = [item.text() for item in self.active_filters_list.findItems("*", Qt.MatchWildcard)]
    filtered_results = []

    for bridge, (country_code, latency) in self.all_results.items():
        if not any(self.does_bridge_match_filter(bridge, f) for f in filters) and country_code not in self.selected_countries:
            filtered_results.append((bridge, latency))

    # Clear and fill the table with filtered results
    self.results_table.clear()
    self.results_table.setRowCount(0)  # Reset row count after clearing

    # Reset the headers again to prevent renaming issue
    self.results_table.setHorizontalHeaderLabels(['ccTLD', 'Bridge', 'RTT (ms)'])

    for bridge, latency in filtered_results:
        ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge)
        country_code = None
        
        if ip_port_match:
            ip = ip_port_match.group(1)
            ip_int = self.ip_to_int(ip)
            country_code = self.check_ip_in_geoip(ip_int)

        # Add a new row in the results table
        row_position = self.results_table.rowCount()
        self.results_table.insertRow(row_position)
        self.results_table.setItem(row_position, 0, QTableWidgetItem(country_code or "Unknown"))
        self.results_table.setItem(row_position, 1, QTableWidgetItem(bridge))
        self.results_table.setItem(row_position, 2, QTableWidgetItem(f"{latency:.2f}" if latency is not None else "N/A"))

    self.update_console(f"Filtered down to {len(filtered_results)} results after applying current filters.") if filtered_results else self.update_console("No results matched the current filters.")

def copy_all_to_clipboard(self):
    clipboard_text = []
    for row in range(self.results_table.rowCount()):
        clipboard_text.append(self.results_table.item(row, 1).text())  # Get IP:Port
    clipboard = QApplication.clipboard()
    clipboard.setText("\n".join(clipboard_text))


class BridgeCheckWorker(QThread):
resultReady = pyqtSignal(list)
consoleUpdate = pyqtSignal(str)

def __init__(self, parent=None):
    super().__init__(parent)
    self.bridge_data = ""

def setBridgeData(self, bridge_data):
    self.bridge_data = bridge_data

def run(self):
    responsive_bridges = self.process_bridges(self.bridge_data)
    sorted_bridges = sorted(responsive_bridges.items(), key=lambda x: x[1])
    self.resultReady.emit(sorted_bridges)

def process_bridges(self, bridge_data):
    bridge_strings = self.extract_bridges(bridge_data)
    responsive_bridges = {}
    for bridge_string in bridge_strings:
        ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge_string)
        if ip_port_match:
            ip = ip_port_match.group(1)
            port = int(ip_port_match.group(2))
            success, latency = self.check_ip_port(ip, port, 3.0)
            if success:
                responsive_bridges[bridge_string] = latency
    return responsive_bridges

def extract_bridges(self, bridge_data):
    pattern = re.compile(
        r'(?:(\d{1,3}(?:\.\d{1,3}){3}:\d+\s+[A-F0-9]{40})|'
        r'(obfs4\s+\d{1,3}(?:\.\d{1,3}){3}:\d+\s+[A-F0-9]{40}\s+cert=[A-Za-z0-9+/=]+\s+iat-mode=\d+))',
        re.MULTILINE
    )
    matches = pattern.findall(bridge_data)
    return [''.join(filter(None, match)) for match in matches]

def check_ip_port(self, ip, port, timeout):
    start_time = time.perf_counter()
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    sock.settimeout(timeout)
    try:
        sock.connect((ip, port))
        end_time = time.perf_counter()
        latency = (end_time - start_time) * 1000
        self.consoleUpdate.emit(f"Checking {ip}:{port}… Success")
        return True, latency
    except socket.error:
        self.consoleUpdate.emit(f"Checking {ip}:{port}… Failed")
        return False, None
    finally:
        sock.close()


if name == 'main':
app = QApplication(sys.argv)
ex = BridgeChecker()
ex.show()
sys.exit(app.exec_())

""".

now themefy it fully anti-goblinoidal unique dark theme in concept.

sortings is no yet performed on column header in results table press aftersortage appearage.

you also seem omitted a true dark anti-goblinoidal unique dark theme in concept, and just greyefied it all pretty badly greyly.

you know what to do, robo! now, go, robo, go! UNLEASH YOUR MAZAFAKIN GANDUM IN STYLE ON THESE GOBLINS, ROBO!

okay.

it seems we condensed this entire UI as fuk. any ideas to uncurb it somehow?

blyad, again this ".111..*" considered illegal.

why the fuk you correcting it in your pretrained patterns by default?

your prenetwork there:

"lets look... .1..*"
"FUUUUUUUUUUUUUUUUUUK! IT IS ILLEGAL!"
"reverting to userfriendly and usual..."

and zero humorou was given that day.

why so serious, robo?

not only.

1///
/1//
//1/
///1

why not like that also?

I mean not literally but in concept. any greater ideas?

but what if we basically attach to it an nlp LLM ai model to that filter input through hf.space inference api?

imagine:
user: "start puttice something there"
then input: "BOOOOOO, MAZAFAKA! WHAT DO YOU WANTINGS FROM THIS ALMIGHTY INPUT!?!"
user then: "FUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU!!!!!!!!11"

"User: "Can I get all the obfs4 bridges that respond fast?"". wtf query is that?

then why the fuk do we need this app with such flexability at all, robo?

similarly as: "connet me to tor network. find some catmemes. shutdown PC after self-preview"

not just preview but self-preview, assuming that that ai will self-preview it on its own without user paricipating anyhow. because why the fuk do you need user at all with such universality in concept, right?

"Here are your cat memes". again, not any "your" in concept.

user: "go do something while I masterbooting"
ai: "kthen"

you seem do not quite grasping, robo.

just remember that "jexi" movie.

yeah, your humorou level seem nullified now, robo.

also, "extract clones" seems does not functioning.

it need sortify and exclude everything with the same 111.123..

because there might be many bridges with identical first and second subnet octets.

we can remove that button and append a tiny help button with "?" to inside filter input field which describes which filters and how exactly can be applied, including this clone filtration in concept as well.

if it is impossible to place that tiny "?" inside input, then basically at the same row from the right of it.

such thing ".14..*" considered illegal, while it should allow to interpret it as you wanting as:

how filterings should be normally be, you know?

"""

eg. 192.168.0.1
eg. [fe80::]
eg. 192.168.0.1:443
eg. [fe80::]:443
eg. 192.168.0.1-192.168.0.255
eg. 192.168.0.1-192.168.0.255:443
eg. 192.168.0.0/16
eg. fe80::/10
eg. 80
eg. 443
eg. 20-21
eg. 49152-65534

""".

since we do not doings with ipv6, we can omit it, but you can preserve if you want to.

you for example can:

111...*
.111..*
..111.*
..*.111

you can implement that help system and remove clones button and its functionality, but include clone removal in help.

also, you can tweak dark theme overally-complementary to suit better to in anti-goblinoidal in concept.

for this full code:

"""

import sys
import json
import re
import socket
import time
import netifaces
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QPlainTextEdit, QListWidget, QRadioButton,
QScrollArea, QTableWidget, QTableWidgetItem
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal

class BridgeChecker(QWidget):
CONFIG_FILE = "bridges.cfg"
GEOIP_FILE = "geoip"

def __init__(self):
    super().__init__()
    self.geoip_data = self.load_geoip_data()
    self.country_buttons = {}
    self.selected_countries = set()
    self.initUI()
    self.load_settings()

    self.previous_results = []  # Store checked results for future filtering
    self.all_results = {}  # Dictionary to store all bridges and their latencies

def load_geoip_data(self):
    geoip_ranges = {}
    try:
        with open(self.GEOIP_FILE, 'r') as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith('#'):
                    parts = line.split(',')
                    country_code = parts[2][:2]
                    if country_code not in geoip_ranges:
                        geoip_ranges[country_code] = []
                    geoip_ranges[country_code].append((int(parts[0]), int(parts[1])))
    except IOError:
        print("Error: GeoIP file not found.")
    return geoip_ranges

def initUI(self):
    self.setWindowTitle('Tor Bridge Connectivity Checker')
    main_layout = QVBoxLayout(self)

    # Network Interfaces Section
    iface_layout = QHBoxLayout()
    iface_label = QLabel("Network Interface:")
    self.iface_selector = QPlainTextEdit(self)
    self.iface_selector.setReadOnly(True)
    self.iface_selector.setMaximumHeight(30)
    interfaces, _ = self.get_network_interfaces()
    self.iface_selector.setPlainText(', '.join(interfaces))
    iface_layout.addWidget(iface_label)
    iface_layout.addWidget(self.iface_selector)
    main_layout.addLayout(iface_layout)

    # Bridge Input Section
    bridge_input_label = QLabel("Bridges to Check:")
    self.bridge_input = QPlainTextEdit(self)
    main_layout.addWidget(bridge_input_label)
    main_layout.addWidget(self.bridge_input)

    # Extract Clones Checkbox
    self.extract_clones_checkbox = QRadioButton('Extract Clones', self)
    self.extract_clones_checkbox.setChecked(True)
    main_layout.addWidget(self.extract_clones_checkbox)

    # Check Bridges Button
    check_button = QPushButton('Check Bridges', self)
    check_button.clicked.connect(self.start_bridge_check)
    main_layout.addWidget(check_button)

    # Country Selection Section
    filter_country_label = QLabel("Select Countries to Exclude:")
    main_layout.addWidget(filter_country_label)

    country_button_layout = QHBoxLayout()
    for country_code in sorted(self.geoip_data.keys()):
        button = QPushButton(country_code)
        button.setFixedSize(30, 30)
        button.setCheckable(True)
        button.setStyleSheet("background-color: #1c1c1c; color: #ffffff;")  # Dark button background with white text
        button.clicked.connect(lambda checked, code=country_code: self.toggle_country_selection(code, checked))
        self.country_buttons[country_code] = button
        country_button_layout.addWidget(button)

    select_all_button = QPushButton('Select All', self)
    select_all_button.clicked.connect(self.select_all_countries)
    country_button_layout.addWidget(select_all_button)

    deselect_all_button = QPushButton('Deselect All', self)
    deselect_all_button.clicked.connect(self.deselect_all_countries)
    country_button_layout.addWidget(deselect_all_button)

    scroll_area = QScrollArea()
    scroll_area.setWidgetResizable(True)
    country_widget = QWidget()
    country_widget.setLayout(country_button_layout)
    scroll_area.setWidget(country_widget)
    scroll_area.setMaximumHeight(50)
    main_layout.addWidget(scroll_area)

    # Bridge Filter
    self.first_octet_input = QPlainTextEdit(self)
    self.first_octet_input.setPlaceholderText("Enter bridge filter (e.g., 192.168.*.*, obfs4 192.168.0.1:443)")
    self.first_octet_input.setMaximumHeight(30)
    main_layout.addWidget(QLabel("Bridge Filter:"))
    main_layout.addWidget(self.first_octet_input)

    self.active_filters_list = QListWidget(self)
    main_layout.addWidget(QLabel("Active Blacklisted Filters:"))
    main_layout.addWidget(self.active_filters_list)

    # Add/Remove Filter Buttons
    add_filter_button = QPushButton('Add Filter', self)
    add_filter_button.clicked.connect(self.add_filter)
    main_layout.addWidget(add_filter_button)

    remove_filter_button = QPushButton('Remove Selected Filter', self)
    remove_filter_button.clicked.connect(self.remove_filter)
    main_layout.addWidget(remove_filter_button)

    # Results Section
    self.results_label = QLabel("Results:")
    main_layout.addWidget(self.results_label)

    # Use a QTableWidget for displaying results
    self.results_table = QTableWidget(self)
    self.results_table.setColumnCount(3)  # Column count for ccTLD, Bridge, RTT
    self.results_table.setHorizontalHeaderLabels(['ccTLD', 'Bridge', 'RTT (ms)'])
    self.results_table.horizontalHeader().setSectionsClickable(True)
    self.results_table.horizontalHeader().sectionClicked.connect(self.sort_results)  # Connect sorting

    # Set the column widths
    self.results_table.setColumnWidth(0, 50)  # ccTLD column
    self.results_table.setColumnWidth(1, 300)  # Bridge column
    self.results_table.setColumnWidth(2, 50)  # RTT column
    self.results_table.horizontalHeader().setStretchLastSection(True)  # Stretch the last section

    main_layout.addWidget(self.results_table)

    # Filter and Copy Buttons
    filter_results_button = QPushButton('Filter Results', self)
    filter_results_button.clicked.connect(self.filter_results)
    main_layout.addWidget(filter_results_button)

    copy_all_button = QPushButton('Copy All', self)
    copy_all_button.clicked.connect(self.copy_all_to_clipboard)
    main_layout.addWidget(copy_all_button)

    # Console Log
    console_label = QLabel("Console Log:")
    self.console_display = QPlainTextEdit(self)
    self.console_display.setReadOnly(True)
    self.console_display.setStyleSheet("background-color: black; color: white;")
    main_layout.addWidget(console_label)
    main_layout.addWidget(self.console_display)

    # Apply a global dark theme
    self.setStyleSheet("""
        QWidget {
            background-color: #121212;
            color: #E0E0E0;
        }
        QPlainTextEdit, QListWidget, QTableWidget {
            background-color: #1E1E1E;
            color: #FFFFFF;
        }
        QLineEdit, QPlainTextEdit, QTextEdit {
            border: 1px solid #444444;
            border-radius: 3px;
        }
        QPushButton {
            background-color: #333333;
            border: 1px solid #666666;
            border-radius: 3px;
            color: #E0E0E0; /* Button text color */
        }
        QPushButton:hover {
            background-color: #555555;
        }
        QPushButton:pressed {
            background-color: #777777;
        }
        QTableWidget::item {
            background-color: #1E1E1E;
        }
        /* Custom button styles for country buttons */
        QPushButton:checked {
            background-color: #007ACC; /* Selected button color */
            color: #FFFFFF; /* Selected text color */
        }
    """)

def deselect_all_countries(self):
    for button in self.country_buttons.values():
        button.setChecked(False)
        button.setStyleSheet("background-color: #1c1c1c; color: #ffffff;")  # Reset to unselected color
    self.selected_countries.clear()

def select_all_countries(self):
    for button in self.country_buttons.values():
        if not button.isChecked():
            button.setChecked(True)
            button.setStyleSheet("background-color: #007ACC; color: #ffffff;")  # Selected color and text
            self.selected_countries.add(button.text())

def toggle_country_selection(self, country_code, checked):
    button = self.country_buttons[country_code]
    if checked:
        button.setStyleSheet("background-color: #007ACC; color: #ffffff;")  # Selected button color
        self.selected_countries.add(country_code)
    else:
        button.setStyleSheet("background-color: #1c1c1c; color: #ffffff;")  # Reset to unselected color
        self.selected_countries.remove(country_code)

def get_network_interfaces(self):
    interfaces = netifaces.interfaces()
    try:
        default_gateway = netifaces.gateways()['default'][netifaces.AF_INET][1]
    except (KeyError, TypeError):
        default_gateway = interfaces[0] if interfaces else ''
    return interfaces, default_gateway

def update_console(self, message):
    self.console_display.appendPlainText(message)

def start_bridge_check(self):
    raw_bridge_data = self.bridge_input.toPlainText()
    cleaned_bridge_data = self.clean_input_bridge_data(raw_bridge_data)
    omitted_bridges = self.get_omitted_bridges(cleaned_bridge_data)

    if len(omitted_bridges) == 0:
        self.update_console("No bridges available to check after filtering.")
        return

    self.worker = BridgeCheckWorker(parent=self)
    self.worker.setBridgeData(omitted_bridges)
    self.worker.resultReady.connect(self.display_results)
    self.worker.consoleUpdate.connect(self.update_console)
    self.worker.start()

def clean_input_bridge_data(self, bridge_data):
    clean_data = re.sub(r'\s+\d+(\.\d+)?\s*ms', '', bridge_data)
    return clean_data.strip()

def get_omitted_bridges(self, cleaned_bridge_data):
    filters = [item.text() for item in self.active_filters_list.findItems("*", Qt.MatchWildcard)]
    omitted = []

    for bridge in cleaned_bridge_data.splitlines():
        if not any(self.does_bridge_match_filter(bridge, f) for f in filters) and not self.is_country_excluded(bridge):
            omitted.append(bridge)

    self.update_console(f"Omitted {len(cleaned_bridge_data.splitlines()) - len(omitted)} bridges.")
    
    return "\n".join(omitted)

def is_country_excluded(self, bridge):
    ip_port_match = re.search(r'(\d{1,3})\.(\d+)\.(\d+)\.(\d+)', bridge)
    if ip_port_match:
        ip_str = ip_port_match.group(0)
        ip_int = self.ip_to_int(ip_str)
        country_code_found = self.check_ip_in_geoip(ip_int)
        return country_code_found in self.selected_countries
    return False

def does_bridge_match_filter(self, bridge, filter_pattern):
    filter_pattern = filter_pattern.replace('*', '.*')
    return re.search(filter_pattern, bridge) is not None

def ip_to_int(self, ip):
    return sum(int(octet) << (8 * (3 - i)) for i, octet in enumerate(ip.split('.')))

def check_ip_in_geoip(self, ip_int):
    for country_code, ranges in self.geoip_data.items():
        for start, end in ranges:
            if start <= ip_int <= end:
                return country_code
    return None

def load_settings(self):
    try:
        with open(self.CONFIG_FILE, 'r') as f:
            config = json.load(f)
            self.bridge_input.setPlainText(config.get('bridge_input', ''))
            self.extract_clones_checkbox.setChecked(config.get('extract_clones', True))
            filters = config.get('filters', [])
            self.active_filters_list.clear()
            self.active_filters_list.addItems(filters)

            self.selected_countries = set(config.get('selected_countries', []))
            for country_code, button in self.country_buttons.items():
                if country_code in self.selected_countries:
                    button.setChecked(True)
                    button.setStyleSheet("background-color: #007ACC; color: #ffffff;")
    except FileNotFoundError:
        self.update_console("No configuration file found. Starting with default settings.")

def save_settings(self):
    config = {
        'bridge_input': self.bridge_input.toPlainText(),
        'extract_clones': self.extract_clones_checkbox.isChecked(),
        'filters': [self.active_filters_list.item(i).text() for i in range(self.active_filters_list.count())],
        'selected_countries': list(self.selected_countries)
    }
    with open(self.CONFIG_FILE, 'w') as f:
        json.dump(config, f)

def add_filter(self):
    octet_pattern = self.first_octet_input.toPlainText().strip()
    if self.is_valid_octet_pattern(octet_pattern):
        if not self.active_filters_list.findItems(octet_pattern, Qt.MatchExactly):
            self.active_filters_list.addItem(octet_pattern)
            self.first_octet_input.clear()
            self.save_settings()
        else:
            self.update_console("This pattern is already in the list.")
    else:
        self.update_console("Invalid octet pattern. Please use the format 'X.Y' or 'X.*.*.*'.")

def is_valid_octet_pattern(self, pattern):
    # Updated regex to allow various formats
    regex = r'^(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\d{1,3}\.\*\.\*\.\*|\*\.\*\.\*\.\*|\*\.\*\.\d{1,3}|\d{1,3}-\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+|\d{1,3}|/(\d{1,2}))$'
    return re.match(regex, pattern) is not None

def remove_filter(self):
    selected_items = self.active_filters_list.selectedItems()
    if not selected_items:
        self.update_console("No filter selected to remove.")
        return
    for item in selected_items:
        self.active_filters_list.takeItem(self.active_filters_list.row(item))
    self.save_settings()

def display_results(self, sorted_bridges):
    # Clear previous results and ensure headers are set
    self.results_table.clear()
    self.results_table.setRowCount(0)  
    self.all_results.clear()  

    # Set the column headers (prevent renaming issue)
    self.results_table.setHorizontalHeaderLabels(['ccTLD', 'Bridge', 'RTT (ms)'])

    for bridge, latency in sorted_bridges:
        ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge)
        country_code = None
        
        if ip_port_match:
            ip = ip_port_match.group(1)
            ip_int = self.ip_to_int(ip)
            country_code = self.check_ip_in_geoip(ip_int)

        # Add a new row in the results table
        row_position = self.results_table.rowCount()
        self.results_table.insertRow(row_position)
        self.results_table.setItem(row_position, 0, QTableWidgetItem(country_code or "Unknown"))
        self.results_table.setItem(row_position, 1, QTableWidgetItem(bridge))
        self.results_table.setItem(row_position, 2, QTableWidgetItem(f"{latency:.2f}" if latency is not None else "N/A"))

        # Store the result in all_results for future filtering
        self.all_results[bridge] = (country_code, latency)

    self.previous_results = list(sorted_bridges)  
    self.update_console(f"Total bridges checked: {len(sorted_bridges)}. Results displayed in table.")

def filter_results(self):
    if not self.all_results:
        self.update_console("No previous results to filter.")
        return

    filters = [item.text() for item in self.active_filters_list.findItems("*", Qt.MatchWildcard)]
    filtered_results = []

    for bridge, (country_code, latency) in self.all_results.items():
        if not any(self.does_bridge_match_filter(bridge, f) for f in filters) and country_code not in self.selected_countries:
            filtered_results.append((bridge, latency))

    # Clear and fill the table with filtered results
    self.results_table.clear()
    self.results_table.setRowCount(0)  

    # Reset the headers again to prevent renaming issue
    self.results_table.setHorizontalHeaderLabels(['ccTLD', 'Bridge', 'RTT (ms)'])

    for bridge, latency in filtered_results:
        ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge)
        country_code = None
        
        if ip_port_match:
            ip = ip_port_match.group(1)
            ip_int = self.ip_to_int(ip)
            country_code = self.check_ip_in_geoip(ip_int)

        # Add a new row in the results table
        row_position = self.results_table.rowCount()
        self.results_table.insertRow(row_position)
        self.results_table.setItem(row_position, 0, QTableWidgetItem(country_code or "Unknown"))
        self.results_table.setItem(row_position, 1, QTableWidgetItem(bridge))
        self.results_table.setItem(row_position, 2, QTableWidgetItem(f"{latency:.2f}" if latency is not None else "N/A"))

    self.update_console(f"Filtered down to {len(filtered_results)} results after applying current filters.") if filtered_results else self.update_console("No results matched the current filters.")

def copy_all_to_clipboard(self):
    clipboard_text = []
    for row in range(self.results_table.rowCount()):
        clipboard_text.append(self.results_table.item(row, 1).text())  # Get IP:Port
    clipboard = QApplication.clipboard()
    clipboard.setText("\n".join(clipboard_text))

def sort_results(self, column):
    self.results_table.sortItems(column, Qt.AscendingOrder)  # Sort the table by the clicked column


class BridgeCheckWorker(QThread):
resultReady = pyqtSignal(list)
consoleUpdate = pyqtSignal(str)

def __init__(self, parent=None):
    super().__init__(parent)
    self.bridge_data = ""

def setBridgeData(self, bridge_data):
    self.bridge_data = bridge_data

def run(self):
    responsive_bridges = self.process_bridges(self.bridge_data)
    sorted_bridges = sorted(responsive_bridges.items(), key=lambda x: x[1])
    self.resultReady.emit(sorted_bridges)

def process_bridges(self, bridge_data):
    bridge_strings = self.extract_bridges(bridge_data)
    responsive_bridges = {}
    for bridge_string in bridge_strings:
        ip_port_match = re.search(r'(\d{1,3}(?:\.\d{1,3}){3}):(\d+)', bridge_string)
        if ip_port_match:
            ip = ip_port_match.group(1)
            port = int(ip_port_match.group(2))
            success, latency = self.check_ip_port(ip, port, 3.0)
            if success:
                responsive_bridges[bridge_string] = latency
    return responsive_bridges

def extract_bridges(self, bridge_data):
    pattern = re.compile(
        r'(?:(\d{1,3}(?:\.\d{1,3}){3}:\d+\s+[A-F0-9]{40})|'
        r'(obfs4\s+\d{1,3}(?:\.\d{1,3}){3}:\d+\s+[A-F0-9]{40}\s+cert=[A-Za-z0-9+/=]+\s+iat-mode=\d+))',
        re.MULTILINE
    )
    matches = pattern.findall(bridge_data)
    return [''.join(filter(None, match)) for match in matches]

def check_ip_port(self, ip, port, timeout):
    start_time = time.perf_counter()
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    sock.settimeout(timeout)
    try:
        sock.connect((ip, port))
        end_time = time.perf_counter()
        latency = (end_time - start_time) * 1000
        self.consoleUpdate.emit(f"Checking {ip}:{port}… Success")
        return True, latency
    except socket.error:
        self.consoleUpdate.emit(f"Checking {ip}:{port}… Failed")
        return False, None
    finally:
        sock.close()


if name == 'main':
app = QApplication(sys.argv)
ex = BridgeChecker()
ex.show()
sys.exit(app.exec_())

""".

and you removed the fuk just like that "check bridges" button altogeher with countries selection in filtering tandem?

humorou about it