Remove outdated configuration files for Kazakhstan, Netherlands, Poland, and the US; add new script for AWG to Clash conversion
This commit is contained in:
176
config-sub-converter/scripts/convert-awg-to-clash.js
Normal file
176
config-sub-converter/scripts/convert-awg-to-clash.js
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/**
|
||||||
|
* Sub Store JS script: AmneziaWG/ProtonVPN (WG INI) -> Clash (wireguard proxy)
|
||||||
|
*
|
||||||
|
* Expected input: text like:
|
||||||
|
* [Interface]
|
||||||
|
* Address = ...
|
||||||
|
* DNS = ...
|
||||||
|
* PrivateKey = ...
|
||||||
|
* ...
|
||||||
|
* [Peer]
|
||||||
|
* PublicKey = ...
|
||||||
|
* PresharedKey = ...
|
||||||
|
* AllowedIPs = ...
|
||||||
|
* Endpoint = host:port
|
||||||
|
*
|
||||||
|
* Output: YAML fragment:
|
||||||
|
* proxies:
|
||||||
|
* - name: "..."
|
||||||
|
* type: wireguard
|
||||||
|
* ...
|
||||||
|
*/
|
||||||
|
function parseIniWG(text) {
|
||||||
|
const lines = text
|
||||||
|
.replace(/\r\n/g, "\n")
|
||||||
|
.split("\n")
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.filter((l) => l.length > 0 && !l.startsWith("#") && !l.startsWith(";"));
|
||||||
|
|
||||||
|
let section = null;
|
||||||
|
const data = { Interface: {}, Peer: {} };
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const mSec = line.match(/^\[(.+?)\]$/);
|
||||||
|
if (mSec) {
|
||||||
|
section = mSec[1];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const mKV = line.match(/^([^=]+?)\s*=\s*(.+)$/);
|
||||||
|
if (!mKV || !section) continue;
|
||||||
|
|
||||||
|
const key = mKV[1].trim();
|
||||||
|
const value = mKV[2].trim();
|
||||||
|
|
||||||
|
if (section.toLowerCase() === "interface") data.Interface[key] = value;
|
||||||
|
else if (section.toLowerCase() === "peer") data.Peer[key] = value;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitList(val) {
|
||||||
|
// "a, b, c" -> ["a","b","c"]
|
||||||
|
return String(val || "")
|
||||||
|
.split(",")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEndpoint(endpoint) {
|
||||||
|
// supports: host:port (IPv6 in brackets not handled here; can add if needed)
|
||||||
|
const m = String(endpoint || "").match(/^(.+?):(\d+)$/);
|
||||||
|
if (!m) return { host: "", port: 0 };
|
||||||
|
return { host: m[1], port: Number(m[2]) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNumIfPossible(v) {
|
||||||
|
const s = String(v ?? "").trim();
|
||||||
|
if (s === "") return null;
|
||||||
|
if (/^-?\d+$/.test(s)) return Number(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function yamlEscapeString(s) {
|
||||||
|
// simplest safe quoting
|
||||||
|
const str = String(s ?? "");
|
||||||
|
return `"${str.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function yamlScalar(v) {
|
||||||
|
if (v === null || v === undefined) return "null";
|
||||||
|
if (typeof v === "number" && Number.isFinite(v)) return String(v);
|
||||||
|
if (typeof v === "boolean") return v ? "true" : "false";
|
||||||
|
return yamlEscapeString(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function yamlInlineArray(arr) {
|
||||||
|
const a = Array.isArray(arr) ? arr : [];
|
||||||
|
return `[${a.map((x) => yamlScalar(x)).join(", ")}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildClashProxyFromWG(wg, proxyName) {
|
||||||
|
const i = wg.Interface || {};
|
||||||
|
const p = wg.Peer || {};
|
||||||
|
|
||||||
|
const address = i.Address || "";
|
||||||
|
const dnsList = splitList(i.DNS);
|
||||||
|
|
||||||
|
const { host, port } = parseEndpoint(p.Endpoint);
|
||||||
|
|
||||||
|
const allowed = splitList(p.AllowedIPs);
|
||||||
|
|
||||||
|
// Amnezia fields (case-insensitive keys are NOT guaranteed in input, handle both)
|
||||||
|
const pick = (k) => (i[k] !== undefined ? i[k] : i[k.toLowerCase()] !== undefined ? i[k.toLowerCase()] : undefined);
|
||||||
|
|
||||||
|
const amzKeys = [
|
||||||
|
["jc", pick("Jc")],
|
||||||
|
["jmin", pick("Jmin")],
|
||||||
|
["jmax", pick("Jmax")],
|
||||||
|
["s1", pick("S1")],
|
||||||
|
["s2", pick("S2")],
|
||||||
|
["h1", pick("H1")],
|
||||||
|
["h2", pick("H2")],
|
||||||
|
["h3", pick("H3")],
|
||||||
|
["h4", pick("H4")],
|
||||||
|
];
|
||||||
|
|
||||||
|
const amzObj = {};
|
||||||
|
for (const [k, v] of amzKeys) {
|
||||||
|
if (v !== undefined) amzObj[k] = toNumIfPossible(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compose YAML (manual, predictable formatting)
|
||||||
|
const name = proxyName && String(proxyName).trim() ? String(proxyName).trim() : "amz-wg";
|
||||||
|
|
||||||
|
let out = "";
|
||||||
|
out += "proxies:\n";
|
||||||
|
out += ` - name: ${yamlEscapeString(name)}\n`;
|
||||||
|
out += ` type: wireguard\n`;
|
||||||
|
out += ` ip: ${yamlScalar(address)}\n`;
|
||||||
|
out += ` private-key: ${yamlScalar(i.PrivateKey || "")}\n`;
|
||||||
|
out += ` peers:\n`;
|
||||||
|
out += ` - server: ${yamlScalar(host)}\n`;
|
||||||
|
out += ` port: ${yamlScalar(port)}\n`;
|
||||||
|
out += ` public-key: ${yamlScalar(p.PublicKey || "")}\n`;
|
||||||
|
if (p.PresharedKey) out += ` pre-shared-key: ${yamlScalar(p.PresharedKey)}\n`;
|
||||||
|
out += ` allowed-ips: ${yamlInlineArray(allowed)}\n`;
|
||||||
|
out += ` udp: true\n`;
|
||||||
|
out += ` remote-dns-resolve: true\n`;
|
||||||
|
if (dnsList.length) out += ` dns: ${yamlInlineArray(dnsList)}\n`;
|
||||||
|
if (Object.keys(amzObj).length) {
|
||||||
|
out += ` amnezia-wg-option:\n`;
|
||||||
|
for (const k of Object.keys(amzObj)) {
|
||||||
|
out += ` ${k}: ${yamlScalar(amzObj[k])}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub Store entrypoint.
|
||||||
|
* Most Sub Store scripts expose a function like:
|
||||||
|
* module.exports = async (ctx) => { ... }
|
||||||
|
* where ctx has raw subscription text in ctx.content / ctx.body / etc.
|
||||||
|
*
|
||||||
|
* To be robust across environments, we try common fields.
|
||||||
|
*/
|
||||||
|
module.exports = async (ctx) => {
|
||||||
|
const input =
|
||||||
|
(ctx && (ctx.content || ctx.body || ctx.data || ctx.text)) ??
|
||||||
|
"";
|
||||||
|
|
||||||
|
const wg = parseIniWG(String(input));
|
||||||
|
|
||||||
|
// Try to derive a name if Sub Store provides something (optional)
|
||||||
|
// Fallback: "amz-wg"
|
||||||
|
const derivedName =
|
||||||
|
(ctx && (ctx.name || ctx.profileName || ctx.filename)) ||
|
||||||
|
"amz-wg";
|
||||||
|
|
||||||
|
const yaml = buildClashProxyFromWG(wg, derivedName);
|
||||||
|
|
||||||
|
// Return in whatever field Sub Store expects
|
||||||
|
// Common patterns: return string, or set ctx.content
|
||||||
|
if (ctx) ctx.content = yaml;
|
||||||
|
return yaml;
|
||||||
|
};
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Generated on: 2025-12-02 09:51:50
|
|
||||||
# VPN Key: vpn://vpn://AAAA_3icdY1PC4IwGIe_iuxcHYwKu4VQIB4Uieo0xlw2dH-cm6Hid2-bRKdOL7zPw--ZAEeMgGMAToyTkaIgU4RRw8AqACXpsKJSU8H_GFjwJ61gT1S3SKF9IknhAuxjAh1RPcUE6kH6EFpm1vI381WkElpg0XjtXTlkLLNrhms12Fv6CWXA7EJGv2CJNPIdl63J4HgbX8Yy2qebq8riXCZJURcoqW67_pynbSPDcHtoHtEdgXn-AEBTVs8=
|
|
||||||
# Keenetic: interface Wireguard0 wireguard asc 4 10 50 110 20 211644422 533572853 237534087 1314455475
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
Address = 100.70.184.183/32
|
|
||||||
DNS = 8.8.8.8, 8.8.4.4
|
|
||||||
PrivateKey = ZiA7zQ9osP4nEoB5jryU99gpHFg6cTprfiaA1nV/pks=
|
|
||||||
Jc = 4
|
|
||||||
Jmin = 10
|
|
||||||
Jmax = 50
|
|
||||||
S1 = 110
|
|
||||||
S2 = 20
|
|
||||||
H1 = 211644422
|
|
||||||
H2 = 533572853
|
|
||||||
H3 = 237534087
|
|
||||||
H4 = 1314455475
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = 5uUhd8S8MjnULEEAiSQysBi+JO6zfrXC0QIIsqsjUkc=
|
|
||||||
PresharedKey = aJ4XS6+QgVasEcGP+5DInldM5i/YSR6PPmmix0wMtFA=
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
Endpoint = 5.189.202.78:46358
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
[Interface]
|
|
||||||
PrivateKey = mJd8aibZI0iuQqPxpTdbs1Enb9+Ury7ido20lu2NrWo=
|
|
||||||
Address = 10.136.82.167/32
|
|
||||||
DNS = 1.1.1.1, 8.8.4.4
|
|
||||||
MTU = 1380
|
|
||||||
Jc = 43
|
|
||||||
Jmin = 50
|
|
||||||
Jmax = 70
|
|
||||||
S1 = 110
|
|
||||||
S2 = 120
|
|
||||||
H1 = 1593635057
|
|
||||||
H2 = 430880481
|
|
||||||
H3 = 1214405368
|
|
||||||
H4 = 1739253821
|
|
||||||
[Peer]
|
|
||||||
PublicKey = gbUPMNfaxgRSGD3xcnnbAJSclxfnOyh4U1qqmYMWmCI=
|
|
||||||
PresharedKey = SCz82d6cfj9bfdlUyHwEBYC3u4T3znL6wFpJ7dMQYbM=
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
Endpoint = nl02awg.kcufwfgnkr.net:60136
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Generated on: 2025-12-02 09:35:19
|
|
||||||
# VPN Key: vpn://vpn://AAAA_3icdY1PC4IwGIe_iuxcHYwKu4VQIB4Uieo0xlw2dH-cm6Hid2-bRKdOL7zPw--ZAEeMgGMAToyTkaIgU4RRw8AqACXpsKJSU8H_GFjwJ61gT1S3SKF9IknhAuxjAh1RPcUE6kH6EFpm1vI381WkElpg0XjtXTlkLLNrhms12Fv6CWXA7EJGv2CJNPIdl63J4HgbX8Yy2qebq8riXCZJURcoqW67_pynbSPDcHtoHtEdgXn-AEBTVs8=
|
|
||||||
# Keenetic: interface Wireguard0 wireguard asc 2 10 50 18 122 1512494805 1147470590 1658720028 1826833034
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
Address = 100.71.64.86/32
|
|
||||||
DNS = 8.8.8.8, 8.8.4.4
|
|
||||||
PrivateKey = /K20yRTTw44Ged/fXjdf4hSLnr33igHMl0SAXy0aMCE=
|
|
||||||
Jc = 2
|
|
||||||
Jmin = 10
|
|
||||||
Jmax = 50
|
|
||||||
S1 = 18
|
|
||||||
S2 = 122
|
|
||||||
H1 = 1512494805
|
|
||||||
H2 = 1147470590
|
|
||||||
H3 = 1658720028
|
|
||||||
H4 = 1826833034
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = Z01PDi29KeHvdtThNoSmXdxG4zyT1yFbulcI7AA5nQg=
|
|
||||||
PresharedKey = uExSfwmdNaUTiJmyNNMuoDavJEKdrdsAqNaVcZ/U00Y=
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
Endpoint = 212.23.222.12:37089
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Generated on: 2025-12-02 09:46:50
|
|
||||||
# VPN Key: vpn://vpn://AAAA_3icdY1PC4IwGIe_iuxcHYwKu4VQIB4Uieo0xlw2dH-cm6Hid2-bRKdOL7zPw--ZAEeMgGMAToyTkaIgU4RRw8AqACXpsKJSU8H_GFjwJ61gT1S3SKF9IknhAuxjAh1RPcUE6kH6EFpm1vI381WkElpg0XjtXTlkLLNrhms12Fv6CWXA7EJGv2CJNPIdl63J4HgbX8Yy2qebq8riXCZJURcoqW67_pynbSPDcHtoHtEdgXn-AEBTVs8=
|
|
||||||
# Keenetic: interface Wireguard0 wireguard asc 3 10 50 145 34 203715079 914012290 174842657 1514769902
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
Address = 100.71.64.155/32
|
|
||||||
DNS = 8.8.8.8, 8.8.4.4
|
|
||||||
PrivateKey = Vuk0huRtjGpA7KtWuQN/kJE0CRFOFs3JysN71nDFGqg=
|
|
||||||
Jc = 3
|
|
||||||
Jmin = 10
|
|
||||||
Jmax = 50
|
|
||||||
S1 = 145
|
|
||||||
S2 = 34
|
|
||||||
H1 = 203715079
|
|
||||||
H2 = 914012290
|
|
||||||
H3 = 174842657
|
|
||||||
H4 = 1514769902
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = ZqTlR9tYsMacuawQQaU6UCoXdT1exYJD2tzFXTpN9zs=
|
|
||||||
PresharedKey = oOJNRLL+5aOgimi28Lwq18w6xCrry4AFLt68fhbUSlk=
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
Endpoint = 5.8.93.236:42854
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpER1E3THlXRHBSOVFrU2ZvZi1zcnN3@151.243.101.40:2060#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
|
|
||||||
vmess://eyJhZGQiOiAiMTUxLjI0My4xMDEuNDAiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogImEzZTc1OTM2LTBlNDQtNGJjOS1iOTdlLWExZmRiMjc2Y2NhNCIsICJuZXQiOiAidGNwIiwgInBhdGgiOiAiLyIsICJwb3J0IjogODA4MSwgInBzIjogIlx1MjZhMVx1MDQxMlx1MDQzNVx1MDQzYlx1MDQzOFx1MDQzYVx1MDQzZVx1MDQzMVx1MDQ0MFx1MDQzOFx1MDQ0Mlx1MDQzMFx1MDQzZFx1MDQzOFx1MDQ0ZiAtIEdCX1QwNzQwNTUiLCAic2N5IjogImF1dG8iLCAidGxzIjogIm5vbmUiLCAidHlwZSI6ICJodHRwIiwgInYiOiAiMiJ9
|
|
||||||
vmess://eyJhZGQiOiAiMTUxLjI0My4xMDEuNDAiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogImEzZTc1OTM2LTBlNDQtNGJjOS1iOTdlLWExZmRiMjc2Y2NhNCIsICJuZXQiOiAid3MiLCAicGF0aCI6ICIvIiwgInBvcnQiOiA4MDgwLCAicHMiOiAiXHUyNmExXHUwNDEyXHUwNDM1XHUwNDNiXHUwNDM4XHUwNDNhXHUwNDNlXHUwNDMxXHUwNDQwXHUwNDM4XHUwNDQyXHUwNDMwXHUwNDNkXHUwNDM4XHUwNDRmIC0gR0JfVDA3NDA1NSIsICJzY3kiOiAiYXV0byIsICJ0bHMiOiAibm9uZSIsICJ0eXBlIjogIiIsICJ2IjogIjIifQ==
|
|
||||||
vless://ca1a269d-6409-4bbf-a4e2-1e9cb04490e2@151.243.101.40:8443?security=reality&type=tcp&headerType=&path=&host=&sni=discordapp.com&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=6ba85179e30d4fc2#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
|
|
||||||
vless://ca1a269d-6409-4bbf-a4e2-1e9cb04490e2@151.243.101.40:2053?security=reality&type=grpc&headerType=&serviceName=xyz&authority=&mode=gun&sni=discordapp.com&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
|
|
||||||
trojan://TEx16RQixr5cJ3rCBz1Ddw@151.243.101.40:2058?security=tls&type=ws&headerType=&path=%2F&host=&sni=uk.hydranet.space&fp=#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
|
|
||||||
ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp2aVluTUlnYW83TjlIbVlCWHV5ZnRB@109.235.48.181:2060#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
|
|
||||||
vmess://eyJhZGQiOiAiMTA5LjIzNS40OC4xODEiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogIjM1Y2EyMDY5LWFmYmYtNDkwMi04ZWIwLWViYjUxZTUwYjNiMyIsICJuZXQiOiAidGNwIiwgInBhdGgiOiAiLyIsICJwb3J0IjogODA4MSwgInBzIjogIlx1MjZhMVx1MDQxZFx1MDQzOFx1MDQzNFx1MDQzNVx1MDQ0MFx1MDQzYlx1MDQzMFx1MDQzZFx1MDQzNFx1MDQ0YiAtIE5MX1QwNzg3NTYiLCAic2N5IjogImF1dG8iLCAidGxzIjogIm5vbmUiLCAidHlwZSI6ICJodHRwIiwgInYiOiAiMiJ9
|
|
||||||
vmess://eyJhZGQiOiAiMTA5LjIzNS40OC4xODEiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogIjM1Y2EyMDY5LWFmYmYtNDkwMi04ZWIwLWViYjUxZTUwYjNiMyIsICJuZXQiOiAid3MiLCAicGF0aCI6ICIvIiwgInBvcnQiOiA4MDgwLCAicHMiOiAiXHUyNmExXHUwNDFkXHUwNDM4XHUwNDM0XHUwNDM1XHUwNDQwXHUwNDNiXHUwNDMwXHUwNDNkXHUwNDM0XHUwNDRiIC0gTkxfVDA3ODc1NiIsICJzY3kiOiAiYXV0byIsICJ0bHMiOiAibm9uZSIsICJ0eXBlIjogIiIsICJ2IjogIjIifQ==
|
|
||||||
vless://b9ae9a97-18da-4c5d-9ada-3b0a92d4813e@109.235.48.181:8443?security=reality&type=tcp&headerType=&path=&host=&sni=vkvideo.ru&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=6ba85179e30d4fc2#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
|
|
||||||
vless://b9ae9a97-18da-4c5d-9ada-3b0a92d4813e@109.235.48.181:2053?security=reality&type=grpc&headerType=&serviceName=xyz&authority=&mode=gun&sni=vkvideo.ru&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
|
|
||||||
trojan://tbPsjmK8x6xSDW1vTO8V5g@109.235.48.181:2058?security=tls&type=ws&headerType=&path=%2F&host=&sni=nl2.hydranet.space&fp=#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
|
|
||||||
Reference in New Issue
Block a user