mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-13 15:09:12 +00:00
update(ToBypassTrackers): 异步查询DNS
This commit is contained in:
@@ -404,7 +404,7 @@
|
||||
"name": "绕过Trackers",
|
||||
"description": "提供tracker服务器IP地址列表,帮助IPv6连接绕过OpenClash",
|
||||
"labels": "工具",
|
||||
"version": "1.3",
|
||||
"version": "1.4",
|
||||
"icon": "Clash_A.png",
|
||||
"author": "wumode",
|
||||
"level": 2,
|
||||
@@ -412,7 +412,8 @@
|
||||
"v1.0": "支持自定义Trackers",
|
||||
"v1.1": "更新列表后发送通知",
|
||||
"v1.2": "修复Trackers加载错误",
|
||||
"v1.3": "新增一些Trackers"
|
||||
"v1.3": "新增一些Trackers",
|
||||
"v1.4": "异步查询DNS"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import ipaddress
|
||||
import socket
|
||||
import base64
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from fastapi import Response
|
||||
@@ -30,7 +31,7 @@ class ToBypassTrackers(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "Clash_A.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.3"
|
||||
plugin_version = "1.4"
|
||||
# 插件作者
|
||||
plugin_author = "wumode"
|
||||
# 作者主页
|
||||
@@ -47,7 +48,6 @@ class ToBypassTrackers(_PluginBase):
|
||||
site_chain: SiteChain = None
|
||||
siteoper: SiteOper = None
|
||||
|
||||
|
||||
# 事件管理器
|
||||
event: EventManager = None
|
||||
# 定时器
|
||||
@@ -161,7 +161,7 @@ class ToBypassTrackers(_PluginBase):
|
||||
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
|
||||
site_options = ([{"title": site.name, "value": site.id}
|
||||
for site in self.siteoper.list_order_by_pri()]
|
||||
)
|
||||
)
|
||||
return [
|
||||
{
|
||||
'component': 'VForm',
|
||||
@@ -429,9 +429,8 @@ class ToBypassTrackers(_PluginBase):
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '【订阅URL】'
|
||||
'「IPv4 Api」: /api/v1/plugin/ToBypassTrackers/bypassed_ips?apikey=moviepilot&protocol=4; '
|
||||
'「IPv6 Api」: /api/v1/plugin/ToBypassTrackers/bypassed_ips?apikey=moviepilot&protocol=6; '
|
||||
'其中moviepilot修改为实际配置中的API_TOKEN的值。'
|
||||
f'「IPv4 API」: /api/v1/plugin/ToBypassTrackers/bypassed_ips?apikey={settings.API_TOKEN}&protocol=4; '
|
||||
f'「IPv6 API」: /api/v1/plugin/ToBypassTrackers/bypassed_ips?apikey={settings.API_TOKEN}&protocol=6'
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -574,7 +573,38 @@ class ToBypassTrackers(_PluginBase):
|
||||
remaining_ranges = list(net_b.address_exclude(net_a))
|
||||
|
||||
return [str(sub_net) for sub_net in remaining_ranges]
|
||||
# replacing = data.get('replace')
|
||||
|
||||
async def resolve_and_check(domain_, results_, failed_msg_, dns_type_, ip_list_):
|
||||
try:
|
||||
addresses = await query_helper.query_dns(domain_, dns_type_)
|
||||
if addresses is None:
|
||||
failed_msg_.append(f"【{domain_name_map.get(domain_, domain_)}】 {domain_}: {dns_type_} 记录查询失败")
|
||||
results_[domain_name_map.get(domain_, domain_)] = False
|
||||
return
|
||||
|
||||
for address in addresses:
|
||||
has_flag = any(__is_ip_in_subnet(address, subnet) for subnet in ip_list_)
|
||||
if not has_flag:
|
||||
if dns_type_ == "AAAA":
|
||||
ip_list_.append(address)
|
||||
else:
|
||||
ip_list_.append(address)
|
||||
logger.info(f"Resolving【{domain_name_map.get(domain_, domain_)}】{address} ({domain_})")
|
||||
except Exception as e:
|
||||
logger.exception(f"处理 {domain_} 出错: {e}")
|
||||
results_[domain_name_map.get(domain_, domain_)] = False
|
||||
|
||||
async def resolve_all(domains_, ipv6_list_, ip_list_):
|
||||
tasks = [
|
||||
resolve_and_check(domain_, results_v6, failed_msg, "AAAA", ipv6_list_)
|
||||
for domain_ in domains_
|
||||
]
|
||||
tasks.extend([resolve_and_check(domain_, results, failed_msg, "A", ip_list_)
|
||||
for domain_ in domains_])
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
query_helper = DnsHelper(self._dns_input)
|
||||
logger.info(f"开始通过 {query_helper.method_name} 解析DNS")
|
||||
chnroute6_lists_url = "https://ispip.clang.cn/all_cn_ipv6.txt"
|
||||
chnroute_lists_url = "https://ispip.clang.cn/all_cn.txt"
|
||||
ipv6_list = []
|
||||
@@ -584,6 +614,7 @@ class ToBypassTrackers(_PluginBase):
|
||||
failed_msg = []
|
||||
results = {}
|
||||
unsupported_msg = []
|
||||
results_v6 = {}
|
||||
if self._china_ipv6_route:
|
||||
# Load Chnroute6 Lists
|
||||
res = RequestUtils().get_res(url=chnroute6_lists_url)
|
||||
@@ -598,7 +629,8 @@ class ToBypassTrackers(_PluginBase):
|
||||
chnroute_lists = res.text[:-1].split('\n')
|
||||
for ipr in chnroute_lists:
|
||||
ip_list.append(ipr)
|
||||
do_sites = {site.domain: site.name for site in self.siteoper.list_order_by_pri() if site.id in self._bypassed_sites}
|
||||
do_sites = {site.domain: site.name for site in self.siteoper.list_order_by_pri() if
|
||||
site.id in self._bypassed_sites}
|
||||
domain_name_map = {}
|
||||
for site in do_sites:
|
||||
site_domains = self.trackers.get(site)
|
||||
@@ -623,44 +655,17 @@ class ToBypassTrackers(_PluginBase):
|
||||
ipv6_list.append(ipaddress.ip_network(f"{custom_tracker}/128", strict=False).compressed)
|
||||
except socket.error:
|
||||
domains.append(custom_tracker)
|
||||
for domain in domains:
|
||||
if self._bypass_ipv6:
|
||||
ipv6_addresses = DnsHelper.query_domain(domain, self._dns_input, "AAAA")
|
||||
if ipv6_addresses is None:
|
||||
logger.warn(f"{domain} AAAA 记录查询失败")
|
||||
failed_msg.append(f"【{domain_name_map.get(domain, domain)}】 {domain}: AAAA记录查询失败")
|
||||
results[{domain_name_map.get(domain, domain)}] = False
|
||||
continue
|
||||
for address in ipv6_addresses:
|
||||
has_flag = False
|
||||
for subnet in ipv6_list:
|
||||
if __is_ip_in_subnet(address, subnet):
|
||||
has_flag = True
|
||||
break
|
||||
if not has_flag:
|
||||
ipv6_list.append(ipaddress.ip_network(f"{address}/128", strict=False).compressed)
|
||||
logger.info(f"【{domain_name_map.get(domain, domain)}】{address} ({domain}) 已被添加")
|
||||
if self._bypass_ipv4:
|
||||
ip_addresses = DnsHelper.query_domain(domain, self._dns_input, 'A')
|
||||
if ip_addresses is None:
|
||||
logger.warn(f"{domain} A 记录查询失败")
|
||||
failed_msg.append(f"【{domain_name_map.get(domain, '')}】 {domain}: A记录查询失败")
|
||||
results[{domain_name_map.get(domain, domain)}] = False
|
||||
continue
|
||||
for address in ip_addresses:
|
||||
has_flag = False
|
||||
for subnet in ip_list:
|
||||
if __is_ip_in_subnet(address, subnet):
|
||||
has_flag = True
|
||||
break
|
||||
if not has_flag:
|
||||
ip_list.append(f"{address}/32")
|
||||
logger.info(f"【{domain_name_map.get(domain, domain)}】{address} ({domain}) 已被添加")
|
||||
v6_ips = []
|
||||
v4_ips = []
|
||||
asyncio.run(resolve_all(domains, v6_ips, v4_ips))
|
||||
ipv6_list.extend([ipaddress.ip_network(f"{ad}/128", strict=False).compressed for ad in v6_ips])
|
||||
ip_list.extend([f"{ad}/32" for ad in v4_ips])
|
||||
for result in results:
|
||||
if results[result]:
|
||||
success_msg.append(f"【{result}】 Trackers已被添加")
|
||||
exempted_ip = []
|
||||
exempted_ipv6 = []
|
||||
exempted_domains = []
|
||||
for exempted_domain in self._exempted_domains.split('\n'):
|
||||
if exempted_domain:
|
||||
try:
|
||||
@@ -673,12 +678,9 @@ class ToBypassTrackers(_PluginBase):
|
||||
if self._bypass_ipv6:
|
||||
exempted_ipv6.append(f"{exempted_domain}")
|
||||
except socket.error:
|
||||
ipv6_addresses = DnsHelper.query_domain(exempted_domain, self._dns_input, 'AAAA')
|
||||
if ipv6_addresses:
|
||||
exempted_ipv6.extend(ipv6_addresses)
|
||||
ipv4_addresses = DnsHelper.query_domain(exempted_domain, self._dns_input, 'A')
|
||||
if ipv4_addresses:
|
||||
exempted_ip.extend(ipv4_addresses)
|
||||
exempted_domains.append(exempted_domain)
|
||||
|
||||
asyncio.run(resolve_all(exempted_domains, exempted_ip, exempted_ipv6))
|
||||
for ip in exempted_ip:
|
||||
index = __search_ip(ip, ip_list)
|
||||
if index == -1:
|
||||
|
||||
@@ -1,105 +1,88 @@
|
||||
import socket
|
||||
import re
|
||||
from typing import Optional
|
||||
from typing import Optional, List, Callable
|
||||
|
||||
import aioquic
|
||||
import dns.asyncresolver
|
||||
import dns.resolver
|
||||
|
||||
from app.log import logger
|
||||
from app.utils.http import RequestUtils
|
||||
|
||||
|
||||
class DnsHelper:
|
||||
def __init__(self, dns_server: str):
|
||||
self.method_name = "Local"
|
||||
self.doh_url = "https://dns.alidns.com/dns-query"
|
||||
self.__resolver = dns.asyncresolver.Resolver()
|
||||
self.__dns_query_method = self.__query_method(dns_server)
|
||||
|
||||
@staticmethod
|
||||
def query_dns_udp(domain: str, dns_server: str, port: int =53, dns_type: str ='A') -> list[str]:
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [dns_server]
|
||||
resolver.port = port
|
||||
|
||||
try:
|
||||
ip_answer = resolver.resolve(domain, dns_type)
|
||||
ip_addresses = [record.address for record in ip_answer]
|
||||
except dns.resolver.NoAnswer:
|
||||
ip_addresses = []
|
||||
except:
|
||||
return None
|
||||
return ip_addresses
|
||||
|
||||
@staticmethod
|
||||
def query_doh(domain: str, doh_url: str, dns_type: str = 'A') -> Optional[list]:
|
||||
params = {
|
||||
'name': domain,
|
||||
'type': dns_type,
|
||||
}
|
||||
headers = {
|
||||
'Accept': 'application/dns-json',
|
||||
}
|
||||
response = RequestUtils().get_res(url=doh_url, headers=headers, params=params)
|
||||
if not response.status_code == 200:
|
||||
return None
|
||||
data = response.json()
|
||||
return [answer['data'] for answer in data.get('Answer', []) if
|
||||
answer.get('type') == 28 or answer.get('type') == 1]
|
||||
|
||||
@staticmethod
|
||||
def parse_dns_input(dns_input: str):
|
||||
def __query_method(self, dns_input: str) -> Callable:
|
||||
if not dns_input:
|
||||
return 'local', dns_input
|
||||
# Check if it's a DoH URL (starts with https://)
|
||||
return self.query_dns_local
|
||||
if dns_input.startswith('https://'):
|
||||
return 'doh', dns_input
|
||||
self.doh_url = dns_input
|
||||
self.method_name = dns_input
|
||||
return self.query_dns_doh
|
||||
udp_match = re.match(r"^(?:udp://)?(\[?.+?]?)(?::(\d+))?$", dns_input)
|
||||
if udp_match:
|
||||
try:
|
||||
self.__resolver.nameservers = [udp_match.group(1).strip('[]')]
|
||||
if udp_match.group(2):
|
||||
self.__resolver.port = int(udp_match.group(2))
|
||||
self.method_name = f"udp://{self.__resolver.nameservers[0]}:{self.__resolver.port}"
|
||||
except Exception as e:
|
||||
logger.warn(f'{e}, using default resolver')
|
||||
return self.query_dns_local
|
||||
return self.query_dns_udp
|
||||
logger.warn(f'Unknown method {dns_input}, using default resolver')
|
||||
return self.query_dns_local
|
||||
|
||||
# Check if it's a UDP DNS with hostname (e.g., udp://unfiltered.adguard-dns.com)
|
||||
if dns_input.startswith('udp://'):
|
||||
hostname = dns_input[len('udp://'):]
|
||||
return 'udp', hostname, 53
|
||||
async def query_dns(self, domain: str, dns_type: str = "A") -> Optional[List[str]]:
|
||||
answers = await self.__dns_query_method(domain, dns_type)
|
||||
return answers
|
||||
|
||||
# Check if it's an IP address with port (e.g., 94.140.14.140:53 or [2a10:50c0::1:ff]:53)
|
||||
port_match = re.match(r'^(\[?.+?\]?):(\d+)$', dns_input)
|
||||
if port_match:
|
||||
dns_server = port_match.group(1).strip('[]')
|
||||
port = int(port_match.group(2))
|
||||
return 'udp', dns_server, port
|
||||
|
||||
# Default to regular DNS over UDP with default port 53
|
||||
return 'udp', dns_input, 53
|
||||
|
||||
@staticmethod
|
||||
def query_dns_local(domain_name: str, dns_type: str = 'A') -> Optional[list]:
|
||||
async def query_dns_local(self, domain: str, dns_type: str = "A") -> Optional[List[str]]:
|
||||
try:
|
||||
# Get address info for both IPv4 and IPv6
|
||||
addr_info = socket.getaddrinfo(domain_name, None, socket.AF_UNSPEC, socket.SOCK_STREAM)
|
||||
answer = await self.__resolver.resolve(domain, dns_type)
|
||||
return [record.address for record in answer if hasattr(record, "address")]
|
||||
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
|
||||
return []
|
||||
except Exception as e:
|
||||
# logger.error(f"本地DNS查询错误: {e} {domain}")
|
||||
return None
|
||||
|
||||
ipv4_addresses = []
|
||||
ipv6_addresses = []
|
||||
async def query_dns_doh(self, domain: str, dns_type: str = 'A') -> Optional[List[str]]:
|
||||
"""
|
||||
使用 DNS-over-HTTPS (DoH) 异步解析域名。
|
||||
|
||||
# Iterate over the address info
|
||||
for info in addr_info:
|
||||
ip_address = info[4][0]
|
||||
:param domain: 要解析的域名
|
||||
:param dns_type: DNS 记录类型,例如 'A', 'AAAA'
|
||||
:return: IP 地址列表,或 None
|
||||
"""
|
||||
|
||||
# Check if the IP address is IPv4 or IPv6
|
||||
if '.' in ip_address:
|
||||
ipv4_addresses.append(ip_address)
|
||||
elif ':' in ip_address:
|
||||
ipv6_addresses.append(ip_address)
|
||||
if dns_type == 'A':
|
||||
return ipv4_addresses
|
||||
elif dns_type == 'AAAA':
|
||||
return ipv6_addresses
|
||||
try:
|
||||
query = dns.message.make_query(domain, dns_type)
|
||||
response = await dns.asyncquery.https(query, self.doh_url)
|
||||
return [
|
||||
item.address for rrset in response.answer for item in rrset.items
|
||||
if hasattr(item, "address")
|
||||
]
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
except socket.gaierror as e:
|
||||
logger.error(f"本地DNS查询错误: {e} {domain_name}")
|
||||
async def query_dns_udp(self, domain: str, dns_type: str = 'A') -> Optional[List[str]]:
|
||||
"""
|
||||
使用 UDP 异步方式解析域名
|
||||
|
||||
@staticmethod
|
||||
def query_domain(domain: str, dns_input: str, dns_type='A') -> Optional[list]:
|
||||
method, *args = DnsHelper.parse_dns_input(dns_input)
|
||||
if method == 'local':
|
||||
return DnsHelper.query_dns_local(domain, dns_type)
|
||||
elif method == 'udp':
|
||||
dns_server, port = args
|
||||
return DnsHelper.query_dns_udp(domain, dns_server, port, dns_type)
|
||||
elif method == 'doh':
|
||||
doh_url = args[0]
|
||||
return DnsHelper.query_doh(domain, doh_url, dns_type)
|
||||
else:
|
||||
logger.error(f'Unknown method {method}')
|
||||
:param domain: 域名
|
||||
:param port: DNS服务器端口(默认53)
|
||||
:param dns_type: 记录类型,如 A、AAAA
|
||||
:return: IP地址列表 或 None
|
||||
"""
|
||||
|
||||
try:
|
||||
answer = await self.__resolver.resolve(domain, dns_type)
|
||||
return [record.address for record in answer]
|
||||
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
|
||||
return []
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
dnspython~=2.7.0
|
||||
dnspython~=2.7.0
|
||||
aioquic~=1.2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
eyJoZGRvbGJ5LmNvbSI6IFsidC5oZGRvbGJ5LmNvbSJdLCAidGp1cHQub3JnIjogWyJ0cmFja2VyLXB1YmxpYy50anVwdC5vcmciXSwgIm5pY2VwdC5uZXQiOiBbInd3dy5uaWNlcHQubmV0Il0sICJyb3VzaS56aXAiOiBbImhpdHB0LmNvbSJdLCAicHRob21lLm5ldCI6IFsicHRob21lLm5ldCJdLCAiaGR0aW1lLm9yZyI6IFsiaGR0aW1lLm9yZyJdLCAiZWFzdGdhbWUub3JnIjogWyJwdC5lYXN0Z2FtZS5vcmciXSwgInB0dGltZS5vcmciOiBbInd3dy5wdHRpbWUub3JnIl0sICJtLXRlYW0uY2MiOiBbInRyYWNrZXIubS10ZWFtLmNjIiwgInRyYWNrZXIubS10ZWFtLmlvIl0sICI1MnB0LnNpdGUiOiBbIjUycHQuc2l0ZSJdLCAicWluZ3dhcHQuY29tIjogWyJ0cmFja2VyLnFpbmd3YS5wcm8iLCAidHJhY2tlci5xaW5nd2FwdC5jb20iXSwgImhka3lsLmluIjogWyJ0cmFja2VyLmhka3lsLmluIl0sICJyYWluZ2ZoLnRvcCI6IFsicmFpbmdmaC50b3AiXSwgImhkZmFucy5vcmciOiBbImhkZmFucy5vcmciXSwgInB0bGdzLm9yZyI6IFsicHRsLmdzIiwgInJlbGF5MDEucHRsLmdzIl0sICJtb25pa2FkZXNpZ24udWsiOiBbInRyYWNrZXIubW9uaWthZGVzaWduLnVrIiwgImRhaWtpcmFpLm1vbmlrYWRlc2lnbi51ayIsICJhbmltZS1uby1pbmRleC5jb20iXSwgInB0c2Jhby5jbHViIjogWyJwdHNiYW8uY2x1YiJdLCAidG90aGVnbG9yeS5pbSI6IFsidHJhY2tlci50b3RoZWdsb3J5LmltIl0sICJ1Mi5kbWh5Lm9yZyI6IFsiZGF5ZHJlYW0uZG1oeS5iZXN0Il0sICJieXIucHQiOiBbInRyYWNrZXIuYnlyLnB0Il0sICJodWRidC5odXN0LmVkdS5jbiI6IFsiaHVkYnQuaHVzdC5lZHUuY24iXSwgImlsb2xpY29uLmNvbSI6IFsidHJhY2tlci5pbG9saWNvbi5jYyJdLCAiaGl0cHQuY29tIjogWyJoaXRwdC5jb20iXSwgImJ0c2Nob29sLmNsdWIiOiBbInB0LmJ0c2Nob29sLmNsdWIiXSwgImhkYXJlYS5jbHViIjogWyJ0cmFja2VyLmhkYXJlYS5jbHViIl0sICJzcHJpbmdzdW5kYXkubmV0IjogWyJvbjYuc3ByaW5nc3VuZGF5Lm5ldCJdLCAiem1wdC5jYyI6IFsiem1wdC5jYyJdLCAiY2FycHQubmV0IjogWyJ0cmFja2VyLmNhcnB0Lm5ldCJdLCAiaWNjMjAyMi5jb20iOiBbInRyYWNrZXIuaWNjMjAyMi54eXoiXSwgImtlZXBmcmRzLmNvbSI6IFsidHJhY2tlci5rZWVwZnJkcy5jb20iXSwgInB0em9uZS54eXoiOiBbInB0em9uZS54eXoiXSwgImNzcHQudG9wIjogWyJjc3B0LnRvcCJdLCAiY3JhYnB0LnZpcCI6IFsiY3JhYnB0LnZpcCJdLCAib2twdC5uZXQiOiBbInd3dy5va3B0Lm5ldCJdLCAiZ2FtZWdhbWVwdC5jb20iOiBbInd3dy5nYW1lZ2FtZXB0LmNvbSJdfQ==
|
||||
eyJoZGRvbGJ5LmNvbSI6IFsidC5oZGRvbGJ5LmNvbSJdLCAidGp1cHQub3JnIjogWyJ0cmFja2VyLXB1YmxpYy50anVwdC5vcmciXSwgIm5pY2VwdC5uZXQiOiBbInd3dy5uaWNlcHQubmV0Il0sICJyb3VzaS56aXAiOiBbImhpdHB0LmNvbSJdLCAicHRob21lLm5ldCI6IFsicHRob21lLm5ldCJdLCAiaGR0aW1lLm9yZyI6IFsiaGR0aW1lLm9yZyJdLCAiZWFzdGdhbWUub3JnIjogWyJwdC5lYXN0Z2FtZS5vcmciXSwgInB0dGltZS5vcmciOiBbInd3dy5wdHRpbWUub3JnIl0sICJtLXRlYW0uY2MiOiBbInRyYWNrZXIubS10ZWFtLmNjIiwgInRyYWNrZXIubS10ZWFtLmlvIl0sICI1MnB0LnNpdGUiOiBbIjUycHQuc2l0ZSJdLCAicWluZ3dhcHQuY29tIjogWyJ0cmFja2VyLnFpbmd3YS5wcm8iLCAidHJhY2tlci5xaW5nd2FwdC5jb20iXSwgImhka3lsLmluIjogWyJ0cmFja2VyLmhka3lsLmluIl0sICJyYWluZ2ZoLnRvcCI6IFsicmFpbmdmaC50b3AiXSwgImhkZmFucy5vcmciOiBbImhkZmFucy5vcmciXSwgInB0bGdzLm9yZyI6IFsicHRsLmdzIiwgInJlbGF5MDEucHRsLmdzIl0sICJtb25pa2FkZXNpZ24udWsiOiBbInRyYWNrZXIubW9uaWthZGVzaWduLnVrIiwgImRhaWtpcmFpLm1vbmlrYWRlc2lnbi51ayIsICJhbmltZS1uby1pbmRleC5jb20iXSwgInB0c2Jhby5jbHViIjogWyJwdHNiYW8uY2x1YiJdLCAidG90aGVnbG9yeS5pbSI6IFsidHJhY2tlci50b3RoZWdsb3J5LmltIl0sICJ1Mi5kbWh5Lm9yZyI6IFsiZGF5ZHJlYW0uZG1oeS5iZXN0Il0sICJieXIucHQiOiBbInRyYWNrZXIuYnlyLnB0Il0sICJodWRidC5odXN0LmVkdS5jbiI6IFsiaHVkYnQuaHVzdC5lZHUuY24iXSwgImlsb2xpY29uLmNvbSI6IFsidHJhY2tlci5pbG9saWNvbi5jYyJdLCAiaGl0cHQuY29tIjogWyJoaXRwdC5jb20iXSwgImJ0c2Nob29sLmNsdWIiOiBbInB0LmJ0c2Nob29sLmNsdWIiXSwgImhkYXJlYS5jbHViIjogWyJ0cmFja2VyLmhkYXJlYS5jbHViIl0sICJzcHJpbmdzdW5kYXkubmV0IjogWyJvbjYuc3ByaW5nc3VuZGF5Lm5ldCIsICJvbi5zcHJpbmdzdW5kYXkubmV0Il0sICJ6bXB0LmNjIjogWyJ6bXB0LmNjIl0sICJjYXJwdC5uZXQiOiBbInRyYWNrZXIuY2FycHQubmV0Il0sICJpY2MyMDIyLmNvbSI6IFsidHJhY2tlci5pY2MyMDIyLnh5eiJdLCAia2VlcGZyZHMuY29tIjogWyJ0cmFja2VyLmtlZXBmcmRzLmNvbSJdLCAicHR6b25lLnh5eiI6IFsicHR6b25lLnh5eiJdLCAiY3NwdC50b3AiOiBbInRyYWNrZXIuY3NwdC50b3AiLCAidHJhY2tlci5jc3B0LmNjIiwgInRyYWNrZXIuY3NwdC5kYXRlIl0sICJjcmFicHQudmlwIjogWyJjcmFicHQudmlwIl0sICJva3B0Lm5ldCI6IFsid3d3Lm9rcHQubmV0Il0sICJnYW1lZ2FtZXB0LmNvbSI6IFsid3d3LmdhbWVnYW1lcHQuY29tIl0sICJhdWRpZW5jZXMubWUiOiBbInQuYXVkaWVuY2VzLm1lIiwgInRyYWNrZXIuY2luZWZpbGVzLmluZm8iXSwgInhpbmd5dW5nZS50b3AiOiBbInRyYWNrZXIueGluZ3l1bmdlLnRvcCIsICJ0cmFja2VyLnhpbmd5dW5nZS5zYnMiXSwgImV0OC5vcmciOiBbImV0OC5vcmciLCAidC5ldDgub3JnIl0sICJkaXNjZmFuLm5ldCI6IFsiZGlzY2Zhbi54eXoiXX0=
|
||||
Reference in New Issue
Block a user