feat(api): init

This commit is contained in:
2025-10-26 09:19:36 +07:00
parent 4add2ee5a2
commit 35e88de99a
14 changed files with 345 additions and 3 deletions
+1
View File
@@ -1,2 +1,3 @@
# store-scraper-bot
Telegram bot that support scrape infos of an app on stores
+19 -2
View File
@@ -9,12 +9,29 @@ repositories {
mavenCentral()
}
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
testCompileOnly {
extendsFrom(configurations.testAnnotationProcessor.get())
}
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.0"))
annotationProcessor("org.projectlombok:lombok:1.18.36")
implementation("com.couchbase.client:java-client:3.4.11")
implementation("org.apache.logging.log4j:log4j-1.2-api:2.24.3")
implementation("org.apache.logging.log4j:log4j-core:2.24.3")
implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3")
implementation("org.telegram:telegrambots-abilities:8.0.0")
testAnnotationProcessor("org.projectlombok:lombok:1.18.36")
testImplementation(platform("org.junit:junit-bom:5.11.4"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.test {
useJUnitPlatform()
}
}
+16
View File
@@ -0,0 +1,16 @@
services:
couchbase:
image: couchbase:community-7.6.2
ports:
- "8091-8097:8091-8097"
- "9123:9123"
- "11207:11207"
- "11210:11210"
- "11280:11280"
- "18091-18097:18091-18097"
volumes:
- couchbase_data:/opt/couchbase/var
restart: unless-stopped
volumes:
couchbase_data:
@@ -1,4 +1,4 @@
package com.miti99;
package com.miti99.storescraperbot;
// TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
@@ -0,0 +1,40 @@
package com.miti99.storescraperbot.api.apple;
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpHeaderNames;
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpHeaderValues;
import com.miti99.storescraperbot.api.apple.request.AppleAppRequest;
import com.miti99.storescraperbot.api.apple.response.AppleAppResponse;
import com.miti99.storescraperbot.util.JacksonUtil;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import lombok.SneakyThrows;
public class AppStoreScraper {
public static final String BASE_URL = "https://miti-app-store-scraper.vercel.app/";
@SneakyThrows
public static AppleAppResponse app(AppleAppRequest request) {
var httpRequest =
HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/app"))
// .timeout(Duration.ofMillis(TIMEOUT))
.header(
HttpHeaderNames.CONTENT_TYPE.toString(),
HttpHeaderValues.APPLICATION_JSON.toString())
.POST(BodyPublishers.ofString(JacksonUtil.writeValueAsString(request)))
.build();
var body =
HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
// .connectTimeout(Duration.ofMillis(TIMEOUT))
.build()
.send(httpRequest, BodyHandlers.ofString())
.body();
return JacksonUtil.readValue(body, AppleAppResponse.class);
}
}
@@ -0,0 +1,5 @@
package com.miti99.storescraperbot.api.apple.request;
public record AppleAppRequest(String appId) {
}
@@ -0,0 +1,67 @@
package com.miti99.storescraperbot.api.apple.response;
import java.util.List;
import java.util.Map;
public record AppleAppResponse(
String title,
String description,
String descriptionHTML,
String summary,
String installs,
long minInstalls,
long maxInstalls,
double score,
String scoreText,
long ratings,
long reviews,
Map<String, Long> histogram,
double price,
boolean free,
String currency,
String priceText,
boolean offersIAP,
String IAPRange,
String androidVersion,
String androidVersionText,
String androidMaxVersion,
String developer,
String developerId,
String developerEmail,
String developerWebsite,
String developerAddress,
String developerLegalName,
String developerLegalEmail,
String developerLegalAddress,
String developerLegalPhoneNumber,
String privacyPolicy,
String developerInternalID,
String genre,
String genreId,
List<Category> categories,
String icon,
String headerImage,
List<String> screenshots,
String video,
String videoImage,
String previewVideo,
String contentRating,
String contentRatingDescription,
boolean adSupported,
String released,
String updated,
String version,
String recentChanges,
List<String> comments,
boolean preregister,
boolean earlyAccessEnabled,
boolean isAvailableInPlayPass,
boolean editorsChoice,
List<Feature> features,
String appId,
String url) {
public record Category(String name, String id) {}
public record Feature(String title, String description) {}
}
@@ -0,0 +1,40 @@
package com.miti99.storescraperbot.api.google;
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpHeaderNames;
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpHeaderValues;
import com.miti99.storescraperbot.api.google.request.GoogleAppRequest;
import com.miti99.storescraperbot.api.google.response.GoogleAppResponse;
import com.miti99.storescraperbot.util.JacksonUtil;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import lombok.SneakyThrows;
public class GooglePlayScraper {
public static final String BASE_URL = "https://miti-google-play-scraper.vercel.app/";
@SneakyThrows
public static GoogleAppResponse app(GoogleAppRequest request) {
var httpRequest =
HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/app"))
// .timeout(Duration.ofMillis(TIMEOUT))
.header(
HttpHeaderNames.CONTENT_TYPE.toString(),
HttpHeaderValues.APPLICATION_JSON.toString())
.POST(BodyPublishers.ofString(JacksonUtil.writeValueAsString(request)))
.build();
var body =
HttpClient.newBuilder()
// .connectTimeout(Duration.ofMillis(TIMEOUT))
.followRedirects(Redirect.NORMAL)
.build()
.send(httpRequest, BodyHandlers.ofString())
.body();
return JacksonUtil.readValue(body, GoogleAppResponse.class);
}
}
@@ -0,0 +1,5 @@
package com.miti99.storescraperbot.api.google.request;
public record GoogleAppRequest(String appId) {
}
@@ -0,0 +1,67 @@
package com.miti99.storescraperbot.api.google.response;
import java.util.List;
import java.util.Map;
public record GoogleAppResponse(
String title,
String description,
String descriptionHTML,
String summary,
String installs,
long minInstalls,
long maxInstalls,
double score,
String scoreText,
long ratings,
long reviews,
Map<String, Long> histogram,
double price,
boolean free,
String currency,
String priceText,
boolean offersIAP,
String IAPRange,
String androidVersion,
String androidVersionText,
String androidMaxVersion,
String developer,
String developerId,
String developerEmail,
String developerWebsite,
String developerAddress,
String developerLegalName,
String developerLegalEmail,
String developerLegalAddress,
String developerLegalPhoneNumber,
String privacyPolicy,
String developerInternalID,
String genre,
String genreId,
List<Category> categories,
String icon,
String headerImage,
List<String> screenshots,
String video,
String videoImage,
String previewVideo,
String contentRating,
String contentRatingDescription,
boolean adSupported,
Long released,
long updated,
String version,
String recentChanges,
List<String> comments,
boolean preregister,
boolean earlyAccessEnabled,
boolean isAvailableInPlayPass,
boolean editorsChoice,
List<Feature> features,
String appId,
String url) {
public record Category(String name, String id) {}
public record Feature(String title, String description) {}
}
@@ -0,0 +1,5 @@
package com.miti99.storescraperbot.config;
public class Config {
}
@@ -0,0 +1,47 @@
package com.miti99.storescraperbot.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.SneakyThrows;
public class JacksonUtil {
public static ObjectMapper MAPPER = objectMapper();
private static ObjectMapper objectMapper() {
var objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.disable(
SerializationFeature.FAIL_ON_EMPTY_BEANS,
SerializationFeature.INDENT_OUTPUT,
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.enable(
DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,
DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
objectMapper.enable(
Feature.ALLOW_SINGLE_QUOTES, Feature.ALLOW_UNQUOTED_FIELD_NAMES, Feature.IGNORE_UNDEFINED);
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
return objectMapper;
}
@SneakyThrows
public static <T> T readValue(String input, Class<T> valueType) {
return MAPPER.readValue(input, valueType);
}
@SneakyThrows
public static String writeValueAsString(Object input) {
return MAPPER.writeValueAsString(input);
}
}
@@ -0,0 +1,16 @@
package com.miti99.storescraperbot.api.apple;
import static org.junit.jupiter.api.Assertions.*;
import com.miti99.storescraperbot.api.apple.request.AppleAppRequest;
import com.miti99.storescraperbot.util.JacksonUtil;
import org.junit.jupiter.api.Test;
class AppStoreScraperTest {
@Test
void testApp() {
var request = new AppleAppRequest("com.mpt.kvtm");
var response = AppStoreScraper.app(request);
System.out.println(JacksonUtil.writeValueAsString(response));
}
}
@@ -0,0 +1,16 @@
package com.miti99.storescraperbot.api.google;
import static org.junit.jupiter.api.Assertions.*;
import com.miti99.storescraperbot.api.google.request.GoogleAppRequest;
import com.miti99.storescraperbot.util.JacksonUtil;
import org.junit.jupiter.api.Test;
class GooglePlayScraperTest {
@Test
void testApp() {
var request = new GoogleAppRequest("vn.kvtm.js");
var response = GooglePlayScraper.app(request);
System.out.println(JacksonUtil.writeValueAsString(response));
}
}