diff --git a/README.md b/README.md
index 8bc6074..36bfccc 100644
--- a/README.md
+++ b/README.md
@@ -29,11 +29,12 @@ Currently supported App sources:
- [Uptodown](https://uptodown.com/)
- [Huawei AppGallery](https://appgallery.huawei.com/)
- [Tencent App Store](https://sj.qq.com/)
+ - [RuStore](https://rustore.ru/)
- Jenkins Jobs
- [APKMirror](https://apkmirror.com/) (Track-Only)
- Other - App-Specific:
- - [Telegram App](https://telegram.org)
- - [Neutron Code](https://neutroncode.com)
+ - [Telegram App](https://telegram.org/)
+ - [Neutron Code](https://neutroncode.com/)
- Direct APK Link
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index d76120c..90025e2 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -13,7 +13,6 @@
F-Droid
Third Party F-Droid Repos
IzzyOnDroid
- SourceForge
SourceHut
@@ -22,24 +21,17 @@
- APKPure
- Aptoide
- - Uptodowng
+ - Uptodown
- APKMirror (Track-Only)
- Huawei AppGallery
+ - Tencent App Store
- Jenkins Jobs
-
-
-
- Open Source - App-Specific:
-
- - Mullvad
- - Signal
- - VLC
+ - RuStore
Other - App-Specific:
- - WhatsApp
- Telegram App
- Neutron Code
diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt
index 2fa41ba..e762156 100644
--- a/fastlane/metadata/android/ru/full_description.txt
+++ b/fastlane/metadata/android/ru/full_description.txt
@@ -13,7 +13,6 @@
F-Droid
Third Party F-Droid Repos
IzzyOnDroid
- SourceForge
SourceHut
@@ -22,24 +21,17 @@
- APKPure
- Aptoide
- - Uptodowng
+ - Uptodown
- APKMirror (Track-Only)
- Huawei AppGallery
+ - Tencent App Store
- Jenkins Jobs
-
-
-
- Свободное ПО - Для отдельных приложений:
-
- - Mullvad
- - Signal
- - VLC
+ - RuStore
Другие - Для отдельных приложений:
- - WhatsApp
- Telegram App
- Neutron Code
diff --git a/lib/app_sources/rustore.dart b/lib/app_sources/rustore.dart
new file mode 100644
index 0000000..853cfed
--- /dev/null
+++ b/lib/app_sources/rustore.dart
@@ -0,0 +1,82 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:http/http.dart';
+import 'package:obtainium/custom_errors.dart';
+import 'package:obtainium/providers/source_provider.dart';
+
+class RuStore extends AppSource {
+ RuStore() {
+ hosts = ['rustore.ru'];
+ name = 'RuStore';
+ naiveStandardVersionDetection = true;
+ showReleaseDateAsVersionToggle = true;
+ }
+
+ @override
+ String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
+ RegExp standardUrlRegEx = RegExp(
+ '^https?://(www\\.)?${getSourceRegex(hosts)}/catalog/app/+[^/]+',
+ caseSensitive: false);
+ RegExpMatch? match = standardUrlRegEx.firstMatch(url);
+ if (match == null) {
+ throw InvalidURLError(name);
+ }
+ return match.group(0)!;
+ }
+
+ @override
+ Future tryInferringAppId(String standardUrl,
+ {Map additionalSettings = const {}}) async {
+ return Uri.parse(standardUrl).pathSegments.last;
+ }
+
+ @override
+ Future getLatestAPKDetails(
+ String standardUrl,
+ Map additionalSettings,
+ ) async {
+ String? appId = await tryInferringAppId(standardUrl);
+ Response res0 = await sourceRequest(
+ 'https://backapi.rustore.ru/applicationData/overallInfo/$appId',
+ additionalSettings);
+ if (res0.statusCode != 200) {
+ throw getObtainiumHttpError(res0);
+ }
+ var appDetails = jsonDecode(res0.body)['body'];
+ if (appDetails['appId'] == null) {
+ throw NoReleasesError();
+ }
+
+ String appName = appDetails['appName'] ?? tr('app');
+ String author = appDetails['companyName'] ?? name;
+ String? dateStr = appDetails['updatedAt'];
+ String? version = appDetails['versionName'];
+ if (version == null) {
+ throw NoVersionError();
+ }
+ DateTime? relDate;
+ if (dateStr != null) {
+ relDate = DateTime.parse(dateStr);
+ }
+
+ Response res1 = await sourceRequest(
+ 'https://backapi.rustore.ru/applicationData/download-link',
+ additionalSettings,
+ followRedirects: false,
+ postBody: {"appId": appDetails['appId'], "firstInstall": true});
+ var downloadDetails = jsonDecode(res1.body)['body'];
+ if (res1.statusCode != 200 || downloadDetails['apkUrl'] == null) {
+ throw NoAPKError();
+ }
+
+ return APKDetails(
+ version,
+ getApkUrlsFromUrls([
+ (downloadDetails['apkUrl'] as String)
+ .replaceAll(RegExp('\\.zip\$'), '.apk')
+ ]),
+ AppNames(author, appName),
+ releaseDate: relDate);
+ }
+}
diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart
index b55f759..4a4cdd7 100644
--- a/lib/providers/source_provider.dart
+++ b/lib/providers/source_provider.dart
@@ -23,6 +23,7 @@ import 'package:obtainium/app_sources/izzyondroid.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/app_sources/jenkins.dart';
import 'package:obtainium/app_sources/neutroncode.dart';
+import 'package:obtainium/app_sources/rustore.dart';
import 'package:obtainium/app_sources/sourceforge.dart';
import 'package:obtainium/app_sources/sourcehut.dart';
import 'package:obtainium/app_sources/telegramapp.dart';
@@ -864,6 +865,7 @@ class SourceProvider {
Tencent(),
Jenkins(),
APKMirror(),
+ RuStore(),
TelegramApp(),
NeutronCode(),
DirectAPKLink(),