code!
This commit is contained in:
parent
27696b41a2
commit
caeea1620e
28
pom.xml
28
pom.xml
@ -55,13 +55,41 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<!-- Repos -->
|
||||
<repositories>
|
||||
<!-- Jitpack - Used for dnsjava -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<!-- Dependencies -->
|
||||
<dependencies>
|
||||
<!-- Libraries -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.32</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.lingala.zip4j</groupId>
|
||||
<artifactId>zip4j</artifactId>
|
||||
<version>2.11.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.dnsjava</groupId>
|
||||
<artifactId>dnsjava</artifactId>
|
||||
<version>v3.5.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
26
src/main/java/me/braydon/pia/PIAServer.java
Normal file
26
src/main/java/me/braydon/pia/PIAServer.java
Normal file
@ -0,0 +1,26 @@
|
||||
package me.braydon.pia;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* A representation of a PIA server.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
|
||||
public final class PIAServer {
|
||||
/**
|
||||
* The IPv4 address of this server.
|
||||
*/
|
||||
@EqualsAndHashCode.Include @NonNull private final String ip;
|
||||
|
||||
/**
|
||||
* The region of this server.
|
||||
*/
|
||||
@NonNull private final String region;
|
||||
|
||||
/**
|
||||
* The unix time of when this server was last seen.
|
||||
*/
|
||||
private final long lastSeen;
|
||||
}
|
@ -1,12 +1,138 @@
|
||||
package me.braydon.pia;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.braydon.pia.readme.ReadMeManager;
|
||||
import net.lingala.zip4j.ZipFile;
|
||||
import org.xbill.DNS.ARecord;
|
||||
import org.xbill.DNS.Lookup;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.Type;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public final class PIAServerList {
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
private static final String OPENVPN_FILES_ENDPOINT = "https://www.privateinternetaccess.com/openvpn/openvpn.zip";
|
||||
private static final File SERVERS_CONTEXT_FILE = new File("context.json");
|
||||
|
||||
@SneakyThrows
|
||||
public static void main(@NonNull String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
Set<PIAServer> servers = getNewServers(); // Get the new servers from PIA
|
||||
int before = servers.size();
|
||||
servers.addAll(loadServersFromContext()); // Load servers from context
|
||||
System.out.println("Loaded " + (servers.size() - before) + " server(s) from the context file");
|
||||
|
||||
// Delete servers that haven't been seen in more than a week
|
||||
before = servers.size();
|
||||
servers.removeIf(server -> (System.currentTimeMillis() - server.getLastSeen()) >= TimeUnit.DAYS.toMillis(7L));
|
||||
System.out.println("Removed " + (before - servers.size()) + " server(s) that haven't been seen in more than a week");
|
||||
|
||||
// Write the servers to the context file
|
||||
System.out.println("Writing context file...");
|
||||
try (FileWriter fileWriter = new FileWriter(SERVERS_CONTEXT_FILE)) {
|
||||
GSON.toJson(servers, fileWriter);
|
||||
}
|
||||
System.out.println("Done, wrote " + servers.size() + " servers to the file");
|
||||
|
||||
// Update the README.md file
|
||||
ReadMeManager.update(servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new servers from the
|
||||
* OpenVPN files provided by PIA.
|
||||
*
|
||||
* @return the new servers
|
||||
*/
|
||||
@SneakyThrows
|
||||
private static Set<PIAServer> getNewServers() {
|
||||
Set<PIAServer> servers = new HashSet<>(); // The new servers to return
|
||||
File serversZip = new File("servers.zip"); // The zip file containing the servers
|
||||
|
||||
// Download the OpenVPN servers zip from PIA
|
||||
long before = System.currentTimeMillis();
|
||||
System.out.println("Downloading OpenVPN files from PIA...");
|
||||
try (InputStream inputStream = new URL(OPENVPN_FILES_ENDPOINT).openStream();
|
||||
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(serversZip)
|
||||
) {
|
||||
byte[] dataBuffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = bufferedInputStream.read(dataBuffer, 0, 1024)) != -1) {
|
||||
fileOutputStream.write(dataBuffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
assert serversZip.exists(); // Confirm the zip exists
|
||||
System.out.println("Downloaded in " + (System.currentTimeMillis() - before) + "ms, extracting...");
|
||||
|
||||
// Extract the servers zip files
|
||||
before = System.currentTimeMillis();
|
||||
try (ZipFile zip = new ZipFile(serversZip)) {
|
||||
zip.extractAll("servers");
|
||||
}
|
||||
serversZip.delete(); // Delete the zip file after extraction
|
||||
System.out.println("Extracted in " + (System.currentTimeMillis() - before) + "ms");
|
||||
|
||||
// Iterate over the OpenVPN files downloaded from PIA
|
||||
File serversDir = new File("servers"); // The dir where the downloaded OpenVPN files are stored
|
||||
File[] openVpnFiles = serversDir.listFiles();
|
||||
assert openVpnFiles != null;
|
||||
System.out.println("Found " + openVpnFiles.length + " OpenVPN files, reading them...");
|
||||
|
||||
for (File file : openVpnFiles) {
|
||||
String region = file.getName().split("\\.")[0]; // The server region
|
||||
try {
|
||||
for (String line : Files.readAllLines(file.toPath())) {
|
||||
// Line doesn't contain the remote server, ignore it
|
||||
if (!line.startsWith("remote ")) {
|
||||
continue;
|
||||
}
|
||||
Record[] records = new Lookup(line.split(" ")[1], Type.A).run(); // Resolve A records
|
||||
if (records == null) { // No A records resolved
|
||||
continue;
|
||||
}
|
||||
System.out.println("Resolved " + records.length + " A Records for region " + region);
|
||||
for (Record record : records) {
|
||||
servers.add(new PIAServer(((ARecord) record).getAddress().getHostAddress(), region, System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
file.delete(); // Delete the OpenVPN file after reading it
|
||||
}
|
||||
}
|
||||
serversDir.delete(); // Delete the servers dir after reading the OpenVPN files
|
||||
|
||||
return servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the servers from the context file.
|
||||
*
|
||||
* @return the loaded servers
|
||||
*/
|
||||
@SneakyThrows
|
||||
private static List<PIAServer> loadServersFromContext() {
|
||||
if (!SERVERS_CONTEXT_FILE.exists()) { // No context file to load
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try (FileReader fileReader = new FileReader(SERVERS_CONTEXT_FILE);
|
||||
JsonReader jsonReader = new JsonReader(fileReader)
|
||||
) {
|
||||
return GSON.fromJson(jsonReader, new TypeToken<List<PIAServer>>() {}.getType());
|
||||
}
|
||||
}
|
||||
}
|
37
src/main/java/me/braydon/pia/common/StringUtils.java
Normal file
37
src/main/java/me/braydon/pia/common/StringUtils.java
Normal file
@ -0,0 +1,37 @@
|
||||
package me.braydon.pia.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class StringUtils {
|
||||
/**
|
||||
* Capitalize the first character
|
||||
* in each word in the given string.
|
||||
*
|
||||
* @param input the input to capitalize
|
||||
* @return the capitalized string
|
||||
*/
|
||||
@NonNull
|
||||
public static String capitalizeFully(@NonNull String input, char delimiter) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String part : input.split(String.valueOf(delimiter))) {
|
||||
builder.append(part.length() <= 3 ? part.toUpperCase() : capitalize(part)).append(delimiter);
|
||||
}
|
||||
return builder.substring(0, builder.length() - 1).replace(delimiter, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalize the first character in the given string.
|
||||
*
|
||||
* @param input the input to capitalize
|
||||
* @return the capitalized string
|
||||
*/
|
||||
@NonNull
|
||||
public static String capitalize(@NonNull String input) {
|
||||
return Character.toUpperCase(input.charAt(0)) + input.substring(1).toLowerCase();
|
||||
}
|
||||
}
|
57
src/main/java/me/braydon/pia/readme/ReadMeManager.java
Normal file
57
src/main/java/me/braydon/pia/readme/ReadMeManager.java
Normal file
@ -0,0 +1,57 @@
|
||||
package me.braydon.pia.readme;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.pia.PIAServer;
|
||||
import me.braydon.pia.common.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public final class ReadMeManager {
|
||||
/**
|
||||
* Copy the template README.md file from the jar
|
||||
* and update it with data from the given servers.
|
||||
*
|
||||
* @param servers the server data
|
||||
*/
|
||||
public static void update(@NonNull Set<PIAServer> servers) {
|
||||
System.out.println("Updating README.md...");
|
||||
try (InputStream templateResource = ReadMeManager.class.getClassLoader().getResourceAsStream("README_TEMPLATE.md")) {
|
||||
assert templateResource != null; // Ensure the template is present
|
||||
|
||||
// Copy the template README.md to the root directory
|
||||
Path localReadMe = new File("README.md").toPath(); // The local README.md file
|
||||
Files.copy(templateResource, localReadMe, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// Replace variables in the README.md file
|
||||
String contents = new String(Files.readAllBytes(localReadMe));
|
||||
contents = contents.replace("<total-servers>", String.valueOf(servers.size())); // Total servers variable
|
||||
|
||||
// Write the total servers per-region table
|
||||
Map<String, Integer> regionCounts = new HashMap<>();
|
||||
for (PIAServer server : servers) {
|
||||
regionCounts.put(server.getRegion(), regionCounts.getOrDefault(server.getRegion(), 0) + 1);
|
||||
}
|
||||
contents = contents.replace("<region-table-entry>", regionCounts.entrySet().stream()
|
||||
.map(entry -> "| " + StringUtils.capitalizeFully(entry.getKey(), '_') + " | " + entry.getValue() + " |") // Map the region to the count
|
||||
.reduce((a, b) -> a + "\n" + b).orElse("")); // Reduce the entries to a single string
|
||||
|
||||
// Write the contents to the file
|
||||
Files.write(localReadMe, contents.getBytes());
|
||||
System.out.println("Done!");
|
||||
} catch (IOException ex) {
|
||||
System.err.println("Failed to update the README.md file");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
9
src/main/resources/README_TEMPLATE.md
Normal file
9
src/main/resources/README_TEMPLATE.md
Normal file
@ -0,0 +1,9 @@
|
||||
![Servers](https://img.shields.io/badge/servers-<total-servers>-darkgreen)
|
||||
|
||||
# PIA-ServerList
|
||||
An automatically updated list of IPs for PIA servers, this list is updated every hour, and servers in this list will be removed in they have not been seen in the last week.
|
||||
|
||||
## Servers
|
||||
| Region | Servers |
|
||||
|----------------------|---------|
|
||||
<region-table-entry>
|
Loading…
x
Reference in New Issue
Block a user