Skip to content

Commit

Permalink
Add --list flag, add spinners, update dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
grant0417 committed Apr 7, 2024
1 parent e6debc3 commit c2e466e
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 109 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Compile Linux
run: deno compile --allow-net --allow-run --target x86_64-unknown-linux-gnu -o mullvad-ping-linux script.ts
run: deno compile --allow-net=api.mullvad.net,deno.land --allow-run=ping --target x86_64-unknown-linux-gnu -o mullvad-ping-linux script.ts
- name: Compile Windows
run: deno compile --allow-net --allow-run --target x86_64-pc-windows-msvc -o mullvad-ping-windows.exe script.ts
run: deno compile --allow-net=api.mullvad.net,deno.land --allow-run=ping --target x86_64-pc-windows-msvc -o mullvad-ping-windows.exe script.ts
- name: Compile Apple Intel
run: deno compile --allow-net --allow-run --target x86_64-apple-darwin -o mullvad-ping-macos-intel script.ts
run: deno compile --allow-net=api.mullvad.net,deno.land --allow-run=ping --target x86_64-apple-darwin -o mullvad-ping-macos-intel script.ts
- name: Compile Apple Arm
run: deno compile --allow-net --allow-run --target aarch64-apple-darwin -o mullvad-ping-macos-arm script.ts
run: deno compile --allow-net=api.mullvad.net,deno.land --allow-run=ping --target aarch64-apple-darwin -o mullvad-ping-macos-arm script.ts
- name: Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
files: |
mullvad-ping-linux
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"deno.enable": true,
"editor.defaultFormatter": "denoland.vscode-deno"
"editor.defaultFormatter": "denoland.vscode-deno",
"editor.formatOnSave": true
}
33 changes: 8 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

Gets the list of Mullvad servers with the best latency according to `ping`.

Run
## Install

```bash
deno run --allow-net --allow-run https://raw.githubusercontent.com/grant0417/mullvad-ping/main/script.ts
```
1. First
[install Deno](https://docs.deno.com/runtime/manual/getting_started/installation),
an open-source runtime for TypeScript and JavaScript.

Build Exe
2. Run the following command

```
deno compile --allow-net --allow-run -o mullvad-ping https://raw.githubusercontent.com/grant0417/mullvad-ping/main/script.ts
```shell
deno run --allow-net=api.mullvad.net,deno.land --allow-run=ping https://raw.githubusercontent.com/grant0417/mullvad-ping/v0.6.0/script.ts
```

Note: The Windows version of `ping` is somewhat more limited than that of Linux
Expand All @@ -22,6 +22,7 @@ or Mac so the times are less precice and the script will take ~5x longer.
```
Usage: script [OPTION]
--country <code> the country you want to query (eg. us, gb, de)
--list <plain|json> lists the available servers
--list-countries lists the available countries
--type <type> the type of server to query (openvpn, bridge, wireguard, all)
--count <n> the number of pings to the server (default 3)
Expand All @@ -33,21 +34,3 @@ Usage: script [OPTION]
--run-mode <type> only show servers running from (all, ram, disk)
--help usage information
```

## Installation Guides

### Windows Subsystem for Linux (WSL) [w/ default distro Ubuntu 20.04 LTS]

#### Pre-requisites:

Install Windows Subsystem for Linux (WSL):
[Guide](https://learn.microsoft.com/en-us/windows/wsl/install)

Install Deno:
[Guide](https://www.digitalocean.com/community/tutorials/how-to-install-the-deno-javascript-runtime-on-ubuntu-20-04#step-1-downloading-deno)

Now you can run the script:

```
deno run --allow-net --allow-run https://raw.githubusercontent.com/grant0417/mullvad-ping/main/script.ts
```
189 changes: 113 additions & 76 deletions script.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { parse } from "https://deno.land/std@0.201.0/flags/mod.ts";
import { parseArgs, Spinner } from "https://deno.land/std@0.221.0/cli/mod.ts";

type ServerDataJSON = {
hostname: string;
Expand Down Expand Up @@ -34,10 +34,14 @@ function checkRunMode(stboot: boolean, runMode: string) {
const serverTypes = ["openvpn", "bridge", "wireguard", "all"];
const runTypes = ["all", "ram", "disk"];

const args = parse(Deno.args);
const args = parseArgs(Deno.args, {
"--": false,
});

if (args.help == true) {
console.log(`Usage: script [OPTION]
--country <code> the country you want to query (eg. us, gb, de)
--list <plain|json> lists the available servers
--list-countries lists the available countries
--type <type> the type of server to query (${
serverTypes.join(", ")
Expand Down Expand Up @@ -95,12 +99,36 @@ if (args.owned != null) {

const provider = args.provider;

console.log("Fetching currently available relays...");
let fetchingSpinner: Spinner | undefined;
if (Deno.stdout.isTerminal()) {
fetchingSpinner = new Spinner({
message: "Fetching currently available relays...",
color: "cyan",
});
fetchingSpinner.start();
}

const response = await fetch(
`https://api.mullvad.net/www/relays/${serverType}/`,
);

if (fetchingSpinner) {
fetchingSpinner.stop();

console.log("Fetched available relays");
console.log();
}

const json: Array<ServerDataJSON> = await response.json();

const servers = json.filter((server) =>
(country == null || country == server.country_code) &&
(server.network_port_speed >= portSpeed) &&
checkRunMode(server.stboot, runMode) &&
(provider == null || provider == server.provider) &&
(owned == null || owned == server.owned)
);

if (args["list-countries"]) {
const countries = new Set();
json.forEach((e) => {
Expand All @@ -109,84 +137,93 @@ if (args["list-countries"]) {
countries.forEach((e) => {
console.log(e);
});
} else if (args.list) {
if (args.list == "json") {
console.log(JSON.stringify(servers, null, 2));
} else if (args.list == "plain" || args.list == true) {
for (const server of servers) {
console.log(
`${server.hostname}.mullvad.net, ${server.city_name}, ${server.country_name} (${server.network_port_speed} Gigabit ${server.type})`,
);
}
} else {
throw new Error("Invalid list type, must be json or plain");
}
} else {
const results = [];

for (const server of json) {
if (
(country == null || country == server.country_code) &&
(server.network_port_speed >= portSpeed) &&
checkRunMode(server.stboot, runMode) &&
(provider == null || provider == server.provider) &&
(owned == null || owned == server.owned)
) {
let args = [];
if (Deno.build.os == "windows") {
args = ["-n", count.toString(), server.ipv4_addr_in];
} else {
args = [
"-c",
count.toString(),
"-i",
interval.toString(),
server.ipv4_addr_in,
];
}
for (const server of servers) {
let args = [];
if (Deno.build.os == "windows") {
args = ["-n", count.toString(), server.ipv4_addr_in];
} else {
args = [
"-c",
count.toString(),
"-i",
interval.toString(),
server.ipv4_addr_in,
];
}

const p = new Deno.Command(
"ping",
{
args,
stdout: "piped",
},
);
const p = new Deno.Command(
"ping",
{
args,
stdout: "piped",
},
);

const output = new TextDecoder().decode((await p.output()).stdout);

if (Deno.build.os == "windows") {
// [all, min, avg, max, mdev]
const regex = /Average = (\d*)ms/;
const avg = output.match(regex);
if (avg) {
console.log(`Pinged ${server.hostname}.mullvad.net, avg ${avg[1]}ms`);

results.push({
hostname: server.hostname,
city: server.city_name,
country: server.country_name,
type: server.type,
ip: server.ipv4_addr_in,
avg: parseFloat(avg[1]) || 0,
network_port_speed: server.network_port_speed,
});
}

await sleep(200);
} else {
// [all, min, avg, max, mdev]
const regex =
/(?<min>\d+(?:.\d+)?)\/(?<avg>\d+(?:.\d+)?)\/(?<max>\d+(?:.\d+)?)\/(?<mdev>\d+(?:.\d+)?)/;

const values = output.match(regex);
if (values) {
console.log(
`Pinged ${server.hostname}.mullvad.net, min/avg/max/mdev ${
values[0]
}`,
);

results.push({
hostname: server.hostname,
city: server.city_name,
country: server.country_name,
type: server.type,
ip: server.ipv4_addr_in,
avg: parseFloat(values[2]) || 0,
network_port_speed: server.network_port_speed,
});
}

await sleep(interval * 1000);
let pingSpinner: Spinner | undefined;
if (Deno.stdout.isTerminal()) {
pingSpinner = new Spinner({
message: `${server.hostname}.mullvad.net`,
color: "cyan",
});
pingSpinner.start();
}
const output = new TextDecoder().decode((await p.output()).stdout);
if (pingSpinner) {
pingSpinner.stop();
}

if (Deno.build.os == "windows") {
// [all, min, avg, max, mdev]
const regex = /Average = (\d*)ms/;
const avg = output.match(regex);
if (avg) {
console.log(` ${server.hostname}.mullvad.net, avg ${avg[1]}ms`);

results.push({
hostname: server.hostname,
city: server.city_name,
country: server.country_name,
type: server.type,
ip: server.ipv4_addr_in,
avg: parseFloat(avg[1]) || 0,
network_port_speed: server.network_port_speed,
});
}
} else {
// [all, min, avg, max, mdev]
const regex =
/(?<min>\d+(?:.\d+)?)\/(?<avg>\d+(?:.\d+)?)\/(?<max>\d+(?:.\d+)?)\/(?<mdev>\d+(?:.\d+)?)/;

const values = output.match(regex);
if (values) {
console.log(
` ${server.hostname}.mullvad.net, min/avg/max/mdev ${values[0]}`,
);

results.push({
hostname: server.hostname,
city: server.city_name,
country: server.country_name,
type: server.type,
ip: server.ipv4_addr_in,
avg: parseFloat(values[2]) || 0,
network_port_speed: server.network_port_speed,
});
}
}
}
Expand Down

0 comments on commit c2e466e

Please sign in to comment.