the great user agent change

This commit is contained in:
Austin Huang 2021-01-11 16:37:34 -05:00
parent c9f891e4e1
commit e8711379d0
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
21 changed files with 163 additions and 65 deletions

View File

@ -10,8 +10,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 57 versionCode 58
versionName '19.0.5' versionName '19.1.0'
multiDexEnabled true multiDexEnabled true

View File

@ -11,12 +11,14 @@ import com.facebook.imagepipeline.core.ImagePipelineConfig;
import java.net.CookieHandler; import java.net.CookieHandler;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.concurrent.ThreadLocalRandom;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.SettingsHelper;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.UserAgentUtils;
import awaisomereport.CrashReporter; import awaisomereport.CrashReporter;
import awaisomereport.LogCollector; import awaisomereport.LogCollector;
@ -85,5 +87,15 @@ public final class InstaGrabberApplication extends Application {
if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) { if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) {
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString()); settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString());
} }
if (settingsHelper.getInteger(Constants.BROWSER_UA_CODE) == -1) {
int randomNum = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.browsers.length);
settingsHelper.putInteger(Constants.BROWSER_UA_CODE, randomNum);
}
if (settingsHelper.getInteger(Constants.APP_UA_CODE) == -1) {
int randomNum = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.devices.length);
settingsHelper.putInteger(Constants.APP_UA_CODE, randomNum);
}
} }
} }

View File

@ -35,7 +35,7 @@ public class CreateThreadAction extends AsyncTask<Void, Void, String> {
try { try {
urlConnection = (HttpURLConnection) new URL(url).openConnection(); urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST"); urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); urlConnection.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.APP_UA));
urlConnection.setUseCaches(false); urlConnection.setUseCaches(false);
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie) + "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)

View File

@ -14,6 +14,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsyncTask.NotificationCounts> { public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsyncTask.NotificationCounts> {
private static final String TAG = "GetActivityAsyncTask"; private static final String TAG = "GetActivityAsyncTask";
@ -44,7 +45,7 @@ public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsy
try { try {
urlConnection = (HttpURLConnection) new URL(url).openConnection(); urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setUseCaches(false); urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT); urlConnection.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.BROWSER_UA));
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
urlConnection.connect(); urlConnection.connect();
if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) { if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {

View File

@ -14,6 +14,7 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.Utils;
public final class UsernameFetcher extends AsyncTask<Void, Void, String> { public final class UsernameFetcher extends AsyncTask<Void, Void, String> {
private final FetchListener<String> fetchListener; private final FetchListener<String> fetchListener;
@ -31,7 +32,7 @@ public final class UsernameFetcher extends AsyncTask<Void, Void, String> {
try { try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://i.instagram.com/api/v1/users/" + uid + "/info/").openConnection(); final HttpURLConnection conn = (HttpURLConnection) new URL("https://i.instagram.com/api/v1/users/" + uid + "/info/").openConnection();
conn.setRequestProperty("User-Agent", Constants.USER_AGENT); conn.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.BROWSER_UA));
conn.setUseCaches(true); conn.setUseCaches(true);
final JSONObject user; final JSONObject user;

View File

@ -414,10 +414,11 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtagDetailsBinding.btnFollowTag.setOnClickListener(v -> { hashtagDetailsBinding.btnFollowTag.setOnClickListener(v -> {
final String cookie = settingsHelper.getString(Constants.COOKIE); final String cookie = settingsHelper.getString(Constants.COOKIE);
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
final String ua = settingsHelper.getString(Constants.BROWSER_UA);
if (csrfToken != null) { if (csrfToken != null) {
hashtagDetailsBinding.btnFollowTag.setClickable(false); hashtagDetailsBinding.btnFollowTag.setClickable(false);
if (!hashtagModel.getFollowing()) { if (!hashtagModel.getFollowing()) {
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() { tagsService.follow(ua, hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
@Override @Override
public void onSuccess(final Boolean result) { public void onSuccess(final Boolean result) {
hashtagDetailsBinding.btnFollowTag.setClickable(true); hashtagDetailsBinding.btnFollowTag.setClickable(true);
@ -445,7 +446,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
}); });
return; return;
} }
tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() { tagsService.unfollow(ua, hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
@Override @Override
public void onSuccess(final Boolean result) { public void onSuccess(final Boolean result) {
hashtagDetailsBinding.btnFollowTag.setClickable(true); hashtagDetailsBinding.btnFollowTag.setClickable(true);

View File

@ -8,7 +8,6 @@ import retrofit2.Call;
import retrofit2.http.FieldMap; import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.QueryMap; import retrofit2.http.QueryMap;
@ -17,20 +16,17 @@ public interface FriendshipRepository {
@FormUrlEncoded @FormUrlEncoded
@POST("/api/v1/friendships/{action}/{id}/") @POST("/api/v1/friendships/{action}/{id}/")
Call<FriendshipChangeResponse> change(@Header("User-Agent") String userAgent, Call<FriendshipChangeResponse> change(@Path("action") String action,
@Path("action") String action,
@Path("id") long id, @Path("id") long id,
@FieldMap Map<String, String> form); @FieldMap Map<String, String> form);
@FormUrlEncoded @FormUrlEncoded
@POST("/api/v1/restrict_action/{action}/") @POST("/api/v1/restrict_action/{action}/")
Call<FriendshipRestrictResponse> toggleRestrict(@Header("User-Agent") String userAgent, Call<FriendshipRestrictResponse> toggleRestrict(@Path("action") String action,
@Path("action") String action,
@FieldMap Map<String, String> form); @FieldMap Map<String, String> form);
@GET("/api/v1/friendships/{userId}/{type}/") @GET("/api/v1/friendships/{userId}/{type}/")
Call<String> getList(@Header("User-Agent") String userAgent, Call<String> getList(@Path("userId") long userId,
@Path("userId") long userId,
@Path("type") String type, // following or followers @Path("type") String type, // following or followers
@QueryMap(encoded = true) Map<String, String> queryParams); @QueryMap(encoded = true) Map<String, String> queryParams);
} }

View File

@ -3,6 +3,7 @@ package awais.instagrabber.repositories;
import java.util.Map; import java.util.Map;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.FieldMap; import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;
@ -14,16 +15,13 @@ import retrofit2.http.Query;
public interface NewsRepository { public interface NewsRepository {
@Headers("User-Agent: " + Constants.USER_AGENT)
@GET("https://www.instagram.com/accounts/activity/?__a=1") @GET("https://www.instagram.com/accounts/activity/?__a=1")
Call<String> webInbox(); Call<String> webInbox(@Header("User-Agent") String userAgent);
@Headers("User-Agent: " + Constants.I_USER_AGENT)
@GET("/api/v1/news/inbox/") @GET("/api/v1/news/inbox/")
Call<String> appInbox(@Query(value = "mark_as_seen", encoded = true) boolean markAsSeen); Call<String> appInbox(@Header("User-Agent") String userAgent, @Query(value = "mark_as_seen", encoded = true) boolean markAsSeen);
@FormUrlEncoded @FormUrlEncoded
@Headers("User-Agent: " + Constants.I_USER_AGENT)
@POST("/api/v1/discover/ayml/") @POST("/api/v1/discover/ayml/")
Call<String> getAyml(@FieldMap final Map<String, String> form); Call<String> getAyml(@Header("User-Agent") String userAgent, @FieldMap final Map<String, String> form);
} }

View File

@ -28,12 +28,11 @@ public interface StoriesRepository {
Call<String> fetchArchive(@QueryMap Map<String, String> queryParams); Call<String> fetchArchive(@QueryMap Map<String, String> queryParams);
@GET @GET
Call<String> getUserStory(@Header("User-Agent") String userAgent, @Url String url); Call<String> getUserStory(@Url String url);
@FormUrlEncoded @FormUrlEncoded
@POST("/api/v1/media/{storyId}/{stickerId}/{action}/") @POST("/api/v1/media/{storyId}/{stickerId}/{action}/")
Call<StoryStickerResponse> respondToSticker(@Header("User-Agent") String userAgent, Call<StoryStickerResponse> respondToSticker(@Path("storyId") String storyId,
@Path("storyId") String storyId,
@Path("stickerId") String stickerId, @Path("stickerId") String stickerId,
@Path("action") String action, @Path("action") String action,
// story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer

View File

@ -9,8 +9,10 @@ public final class Constants {
public static final String APP_THEME = "app_theme_v19"; public static final String APP_THEME = "app_theme_v19";
public static final String APP_LANGUAGE = "app_language_v19"; public static final String APP_LANGUAGE = "app_language_v19";
public static final String STORY_SORT = "story_sort"; public static final String STORY_SORT = "story_sort";
// int prefs // int prefs, do not export
public static final String PREV_INSTALL_VERSION = "prevVersion"; public static final String PREV_INSTALL_VERSION = "prevVersion";
public static final String BROWSER_UA_CODE = "browser_ua_code";
public static final String APP_UA_CODE = "app_ua_code";
// boolean prefs // boolean prefs
public static final String DOWNLOAD_USER_FOLDER = "download_user_folder"; public static final String DOWNLOAD_USER_FOLDER = "download_user_folder";
// deprecated: public static final String BOTTOM_TOOLBAR = "bottom_toolbar"; // deprecated: public static final String BOTTOM_TOOLBAR = "bottom_toolbar";
@ -31,6 +33,8 @@ public final class Constants {
public static final String COOKIE = "cookie"; public static final String COOKIE = "cookie";
public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg"; public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg";
public static final String DEVICE_UUID = "device_uuid"; public static final String DEVICE_UUID = "device_uuid";
public static final String BROWSER_UA = "browser_ua";
public static final String APP_UA = "app_ua";
//////////////////////// EXTRAS //////////////////////// //////////////////////// EXTRAS ////////////////////////
public static final String EXTRAS_USER = "user"; public static final String EXTRAS_USER = "user";
public static final String EXTRAS_HASHTAG = "hashtag"; public static final String EXTRAS_HASHTAG = "hashtag";
@ -54,12 +58,6 @@ public final class Constants {
// Notification ids // Notification ids
public static final int ACTIVITY_NOTIFICATION_ID = 10; public static final int ACTIVITY_NOTIFICATION_ID = 10;
// spoof
public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 8.1.0; motorola one Build/OPKS28.63-18-3; wv) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36";
public static final String I_USER_AGENT =
"Instagram 169.3.0.30.135 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 264009054)";
public static final String A_USER_AGENT = "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me";
// see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts // see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts
public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" + public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" +
" \"13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0," + " \"13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0," +

View File

@ -343,6 +343,10 @@ public final class ExportImportUtils {
jsonObject.remove(Constants.COOKIE); jsonObject.remove(Constants.COOKIE);
jsonObject.remove(Constants.DEVICE_UUID); jsonObject.remove(Constants.DEVICE_UUID);
jsonObject.remove(Constants.PREV_INSTALL_VERSION); jsonObject.remove(Constants.PREV_INSTALL_VERSION);
jsonObject.remove(Constants.BROWSER_UA_CODE);
jsonObject.remove(Constants.BROWSER_UA);
jsonObject.remove(Constants.APP_UA_CODE);
jsonObject.remove(Constants.APP_UA);
return jsonObject; return jsonObject;
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Error exporting settings", e); Log.e(TAG, "Error exporting settings", e);

View File

@ -102,6 +102,14 @@ public final class FlavorTown {
public static void changelogCheck(@NonNull final Context context) { public static void changelogCheck(@NonNull final Context context) {
if (settingsHelper.getInteger(Constants.PREV_INSTALL_VERSION) < BuildConfig.VERSION_CODE) { if (settingsHelper.getInteger(Constants.PREV_INSTALL_VERSION) < BuildConfig.VERSION_CODE) {
final String langCode = settingsHelper.getString(Constants.APP_LANGUAGE);
final String lang = LocaleUtils.getCorrespondingLanguageCode(langCode);
final int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);
final String appUa = UserAgentUtils.generateAppUA(appUaCode, lang);
settingsHelper.putString(Constants.APP_UA, appUa);
final int browserUaCode = settingsHelper.getInteger(Constants.BROWSER_UA_CODE);
final String browserUa = UserAgentUtils.generateBrowserUA(browserUaCode);
settingsHelper.putString(Constants.BROWSER_UA, browserUa);
Toast.makeText(context, R.string.updated, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.updated, Toast.LENGTH_SHORT).show();
settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE); settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE);
} }

View File

@ -19,7 +19,11 @@ public final class LocaleUtils {
if (baseContext instanceof ContextThemeWrapper) if (baseContext instanceof ContextThemeWrapper)
baseContext = ((ContextThemeWrapper) baseContext).getBaseContext(); baseContext = ((ContextThemeWrapper) baseContext).getBaseContext();
final String lang = LocaleUtils.getCorrespondingLanguageCode(baseContext); if (Utils.settingsHelper == null)
Utils.settingsHelper = new SettingsHelper(baseContext);
final String appLanguageSettings = Utils.settingsHelper.getString(Constants.APP_LANGUAGE);
final String lang = TextUtils.isEmpty(appLanguageSettings) ? null : LocaleUtils.getCorrespondingLanguageCode(appLanguageSettings);
currentLocale = TextUtils.isEmpty(lang) ? defaultLocale : currentLocale = TextUtils.isEmpty(lang) ? defaultLocale :
(lang.contains("_") ? new Locale(lang.split("_")[0], lang.split("_")[1]) : new Locale(lang)); (lang.contains("_") ? new Locale(lang.split("_")[0], lang.split("_")[1]) : new Locale(lang));
@ -49,13 +53,7 @@ public final class LocaleUtils {
} }
@Nullable @Nullable
private static String getCorrespondingLanguageCode(final Context baseContext) { public static String getCorrespondingLanguageCode(final String appLanguageSettings) {
if (Utils.settingsHelper == null)
Utils.settingsHelper = new SettingsHelper(baseContext);
final String appLanguageSettings = Utils.settingsHelper.getString(Constants.APP_LANGUAGE);
if (TextUtils.isEmpty(appLanguageSettings)) return null;
final int appLanguageIndex = Integer.parseInt(appLanguageSettings); final int appLanguageIndex = Integer.parseInt(appLanguageSettings);
if (appLanguageIndex == 1) return "en"; if (appLanguageIndex == 1) return "en";
if (appLanguageIndex == 2) return "fr"; if (appLanguageIndex == 2) return "fr";

View File

@ -9,8 +9,12 @@ import androidx.annotation.StringDef;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_LANGUAGE;
import static awais.instagrabber.utils.Constants.APP_UA;
import static awais.instagrabber.utils.Constants.APP_UA_CODE;
import static awais.instagrabber.utils.Constants.APP_THEME; import static awais.instagrabber.utils.Constants.APP_THEME;
import static awais.instagrabber.utils.Constants.AUTOPLAY_VIDEOS; import static awais.instagrabber.utils.Constants.AUTOPLAY_VIDEOS;
import static awais.instagrabber.utils.Constants.BROWSER_UA;
import static awais.instagrabber.utils.Constants.BROWSER_UA_CODE;
import static awais.instagrabber.utils.Constants.CHECK_ACTIVITY; import static awais.instagrabber.utils.Constants.CHECK_ACTIVITY;
import static awais.instagrabber.utils.Constants.CHECK_UPDATES; import static awais.instagrabber.utils.Constants.CHECK_UPDATES;
import static awais.instagrabber.utils.Constants.COOKIE; import static awais.instagrabber.utils.Constants.COOKIE;
@ -79,7 +83,7 @@ public final class SettingsHelper {
private int getIntegerDefault(@IntegerSettings final String key) { private int getIntegerDefault(@IntegerSettings final String key) {
if (APP_THEME.equals(key)) return getThemeCode(true); if (APP_THEME.equals(key)) return getThemeCode(true);
if (PREV_INSTALL_VERSION.equals(key)) return -1; if (PREV_INSTALL_VERSION.equals(key) || APP_UA_CODE.equals(key) || BROWSER_UA_CODE.equals(key)) return -1;
return 0; return 0;
} }
@ -120,10 +124,11 @@ public final class SettingsHelper {
} }
@StringDef( @StringDef(
{APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, {APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION,
DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, PREF_POSTS_LAYOUT, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME,
PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, PREF_LOCATION_POSTS_LAYOUT, PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT,
PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, STORY_SORT, PREF_EMOJI_VARIANTS}) PREF_LOCATION_POSTS_LAYOUT, PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT,
STORY_SORT, PREF_EMOJI_VARIANTS})
public @interface StringSettings {} public @interface StringSettings {}
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
@ -131,6 +136,6 @@ public final class SettingsHelper {
CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED}) CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED})
public @interface BooleanSettings {} public @interface BooleanSettings {}
@StringDef({PREV_INSTALL_VERSION}) @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE})
public @interface IntegerSettings {} public @interface IntegerSettings {}
} }

View File

@ -30,7 +30,7 @@ public final class UpdateChecker extends AsyncTask<Void, Void, Boolean> {
HttpURLConnection conn = HttpURLConnection conn =
(HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection(); (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection();
conn.setUseCaches(false); conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", Constants.A_USER_AGENT); conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me");
conn.connect(); conn.connect();
final int responseCode = conn.getResponseCode(); final int responseCode = conn.getResponseCode();

View File

@ -0,0 +1,77 @@
package awais.instagrabber.utils;
import androidx.annotation.NonNull;
public class UserAgentUtils {
/* GraphQL user agents (which are just standard browser UA's).
* Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it
* Windows first (Assume win64 not wow64): Chrome, Firefox, Edge
* Then macOS: Chrome, Firefox, Safari
*/
public static final String[] browsers = {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11.1; rv:84.0) Gecko/20100101 Firefox/84.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15"
};
// use APKpure, assume x86
private static final String igVersion = "169.3.0.30.135";
private static final String igVersionCode = "264009054";
// https://github.com/dilame/instagram-private-api/blob/master/src/samples/devices.json
// presumed constant, so no need to update
public static final String[] devices = {
"25/7.1.1; 440dpi; 1080x1920; Xiaomi; Mi Note 3; jason; qcom",
"23/6.0.1; 480dpi; 1080x1920; Xiaomi; Redmi Note 3; kenzo; qcom",
"23/6.0; 480dpi; 1080x1920; Xiaomi; Redmi Note 4; nikel; mt6797",
"24/7.0; 480dpi; 1080x1920; Xiaomi/xiaomi; Redmi Note 4; mido; qcom",
"23/6.0; 480dpi; 1080x1920; Xiaomi; Redmi Note 4X; nikel; mt6797",
"27/8.1.0; 440dpi; 1080x2030; Xiaomi/xiaomi; Redmi Note 5; whyred; qcom",
"23/6.0.1; 480dpi; 1080x1920; Xiaomi; Redmi 4; markw; qcom",
"27/8.1.0; 440dpi; 1080x2030; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom",
"25/7.1.2; 440dpi; 1080x2030; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom",
"26/8.0.0; 480dpi; 1080x1920; Xiaomi; MI 5; gemini; qcom",
"27/8.1.0; 480dpi; 1080x1920; Xiaomi/xiaomi; Mi A1; tissot_sprout; qcom",
"26/8.0.0; 480dpi; 1080x1920; Xiaomi; MI 6; sagit; qcom",
"25/7.1.1; 440dpi; 1080x1920; Xiaomi; MI MAX 2; oxygen; qcom",
"24/7.0; 480dpi; 1080x1920; Xiaomi; MI 5s; capricorn; qcom",
"26/8.0.0; 480dpi; 1080x1920; samsung; SM-A520F; a5y17lte; samsungexynos7880",
"26/8.0.0; 480dpi; 1080x2076; samsung; SM-G950F; dreamlte; samsungexynos8895",
"26/8.0.0; 640dpi; 1440x2768; samsung; SM-G950F; dreamlte; samsungexynos8895",
"26/8.0.0; 420dpi; 1080x2094; samsung; SM-G955F; dream2lte; samsungexynos8895",
"26/8.0.0; 560dpi; 1440x2792; samsung; SM-G955F; dream2lte; samsungexynos8895",
"24/7.0; 480dpi; 1080x1920; samsung; SM-A510F; a5xelte; samsungexynos7580",
"26/8.0.0; 480dpi; 1080x1920; samsung; SM-G930F; herolte; samsungexynos8890",
"26/8.0.0; 480dpi; 1080x1920; samsung; SM-G935F; hero2lte; samsungexynos8890",
"26/8.0.0; 420dpi; 1080x2094; samsung; SM-G965F; star2lte; samsungexynos9810",
"26/8.0.0; 480dpi; 1080x2076; samsung; SM-A530F; jackpotlte; samsungexynos7885",
"24/7.0; 640dpi; 1440x2560; samsung; SM-G925F; zerolte; samsungexynos7420",
"26/8.0.0; 420dpi; 1080x1920; samsung; SM-A720F; a7y17lte; samsungexynos7880",
"24/7.0; 640dpi; 1440x2560; samsung; SM-G920F; zeroflte; samsungexynos7420",
"24/7.0; 420dpi; 1080x1920; samsung; SM-J730FM; j7y17lte; samsungexynos7870",
"26/8.0.0; 480dpi; 1080x2076; samsung; SM-G960F; starlte; samsungexynos9810",
"26/8.0.0; 420dpi; 1080x2094; samsung; SM-N950F; greatlte; samsungexynos8895",
"26/8.0.0; 420dpi; 1080x2094; samsung; SM-A730F; jackpot2lte; samsungexynos7885",
"26/8.0.0; 420dpi; 1080x2094; samsung; SM-A605FN; a6plte; qcom",
"26/8.0.0; 480dpi; 1080x1920; HUAWEI/HONOR; STF-L09; HWSTF; hi3660",
"27/8.1.0; 480dpi; 1080x2280; HUAWEI/HONOR; COL-L29; HWCOL; kirin970",
"26/8.0.0; 480dpi; 1080x2032; HUAWEI/HONOR; LLD-L31; HWLLD-H; hi6250",
"26/8.0.0; 480dpi; 1080x2150; HUAWEI; ANE-LX1; HWANE; hi6250",
"26/8.0.0; 480dpi; 1080x2032; HUAWEI; FIG-LX1; HWFIG-H; hi6250",
"27/8.1.0; 480dpi; 1080x2150; HUAWEI/HONOR; COL-L29; HWCOL; kirin970",
"26/8.0.0; 480dpi; 1080x2038; HUAWEI/HONOR; BND-L21; HWBND-H; hi6250",
"23/6.0.1; 420dpi; 1080x1920; LeMobile/LeEco; Le X527; le_s2_ww; qcom"
};
@NonNull
public static String generateBrowserUA(final int code) {
return browsers[code - 1];
}
@NonNull
public static String generateAppUA(final int code, final String lang) {
return "Instagram " + igVersion + " Android (" + devices[code] + "; " + lang + "; " + igVersionCode + ")";
}
}

View File

@ -25,7 +25,7 @@ public class AddCookiesInterceptor implements Interceptor {
} }
final String userAgentHeader = "User-Agent"; final String userAgentHeader = "User-Agent";
if (request.header(userAgentHeader) == null) { if (request.header(userAgentHeader) == null) {
builder.addHeader(userAgentHeader, hasCookie ? Constants.I_USER_AGENT : Constants.USER_AGENT); builder.addHeader(userAgentHeader, Utils.settingsHelper.getString(hasCookie ? Constants.APP_UA : Constants.BROWSER_UA));
} }
final String languageHeader = "Accept-Language"; final String languageHeader = "Accept-Language";
if (request.header(languageHeader) == null) { if (request.header(languageHeader) == null) {

View File

@ -14,14 +14,12 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID;
import awais.instagrabber.models.FollowModel; import awais.instagrabber.models.FollowModel;
import awais.instagrabber.repositories.FriendshipRepository; import awais.instagrabber.repositories.FriendshipRepository;
import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.FriendshipChangeResponse;
import awais.instagrabber.repositories.responses.FriendshipListFetchResponse; import awais.instagrabber.repositories.responses.FriendshipListFetchResponse;
import awais.instagrabber.repositories.responses.FriendshipRestrictResponse; import awais.instagrabber.repositories.responses.FriendshipRestrictResponse;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import retrofit2.Call; import retrofit2.Call;
@ -100,7 +98,7 @@ public class FriendshipService extends BaseService {
form.put("_uuid", deviceUuid); form.put("_uuid", deviceUuid);
form.put("target_user_id", String.valueOf(targetUserId)); form.put("target_user_id", String.valueOf(targetUserId));
final String action = restrict ? "restrict" : "unrestrict"; final String action = restrict ? "restrict" : "unrestrict";
final Call<FriendshipRestrictResponse> request = repository.toggleRestrict(Constants.I_USER_AGENT, action, form); final Call<FriendshipRestrictResponse> request = repository.toggleRestrict(action, form);
request.enqueue(new Callback<FriendshipRestrictResponse>() { request.enqueue(new Callback<FriendshipRestrictResponse>() {
@Override @Override
public void onResponse(@NonNull final Call<FriendshipRestrictResponse> call, public void onResponse(@NonNull final Call<FriendshipRestrictResponse> call,
@ -140,7 +138,7 @@ public class FriendshipService extends BaseService {
form.put("radio_type", "wifi-none"); form.put("radio_type", "wifi-none");
form.put("user_id", targetUserId); form.put("user_id", targetUserId);
final Map<String, String> signedForm = Utils.sign(form); final Map<String, String> signedForm = Utils.sign(form);
final Call<FriendshipChangeResponse> request = repository.change(Constants.I_USER_AGENT, action, targetUserId, signedForm); final Call<FriendshipChangeResponse> request = repository.change(action, targetUserId, signedForm);
request.enqueue(new Callback<FriendshipChangeResponse>() { request.enqueue(new Callback<FriendshipChangeResponse>() {
@Override @Override
public void onResponse(@NonNull final Call<FriendshipChangeResponse> call, public void onResponse(@NonNull final Call<FriendshipChangeResponse> call,
@ -166,8 +164,8 @@ public class FriendshipService extends BaseService {
final ServiceCallback<FriendshipListFetchResponse> callback) { final ServiceCallback<FriendshipListFetchResponse> callback) {
final Map<String, String> queryMap = new HashMap<>(); final Map<String, String> queryMap = new HashMap<>();
if (maxId != null) queryMap.put("max_id", maxId); if (maxId != null) queryMap.put("max_id", maxId);
final Call<String> request = repository.getList(Constants.I_USER_AGENT, final Call<String> request = repository.getList(
targetUserId, targetUserId,
follower ? "followers" : "following", follower ? "followers" : "following",
queryMap); queryMap);
request.enqueue(new Callback<String>() { request.enqueue(new Callback<String>() {

View File

@ -21,6 +21,7 @@ import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.NewsRepository; import awais.instagrabber.repositories.NewsRepository;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
@ -32,6 +33,7 @@ public class NewsService extends BaseService {
private final NewsRepository repository; private final NewsRepository repository;
private static NewsService instance; private static NewsService instance;
private static String browserUa, appUa;
private NewsService() { private NewsService() {
final Retrofit retrofit = getRetrofitBuilder() final Retrofit retrofit = getRetrofitBuilder()
@ -44,13 +46,15 @@ public class NewsService extends BaseService {
if (instance == null) { if (instance == null) {
instance = new NewsService(); instance = new NewsService();
} }
appUa = Utils.settingsHelper.getString(Constants.APP_UA);
browserUa = Utils.settingsHelper.getString(Constants.BROWSER_UA);
return instance; return instance;
} }
public void fetchAppInbox(final boolean markAsSeen, public void fetchAppInbox(final boolean markAsSeen,
final ServiceCallback<List<NotificationModel>> callback) { final ServiceCallback<List<NotificationModel>> callback) {
final List<NotificationModel> result = new ArrayList<>(); final List<NotificationModel> result = new ArrayList<>();
final Call<String> request = repository.appInbox(markAsSeen); final Call<String> request = repository.appInbox(appUa, markAsSeen);
request.enqueue(new Callback<String>() { request.enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -90,7 +94,7 @@ public class NewsService extends BaseService {
public void fetchWebInbox(final boolean markAsSeen, public void fetchWebInbox(final boolean markAsSeen,
final ServiceCallback<List<NotificationModel>> callback) { final ServiceCallback<List<NotificationModel>> callback) {
final Call<String> request = repository.webInbox(); final Call<String> request = repository.webInbox(browserUa);
request.enqueue(new Callback<String>() { request.enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -206,7 +210,7 @@ public class NewsService extends BaseService {
form.put("device_id", UUID.randomUUID().toString()); form.put("device_id", UUID.randomUUID().toString());
form.put("module", "discover_people"); form.put("module", "discover_people");
form.put("paginate", "false"); form.put("paginate", "false");
final Call<String> request = repository.getAyml(form); final Call<String> request = repository.getAyml(appUa, form);
request.enqueue(new Callback<String>() { request.enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {

View File

@ -326,7 +326,7 @@ public class StoriesService extends BaseService {
public void getUserStory(final StoryViewerOptions options, public void getUserStory(final StoryViewerOptions options,
final ServiceCallback<List<StoryModel>> callback) { final ServiceCallback<List<StoryModel>> callback) {
final String url = buildUrl(options); final String url = buildUrl(options);
final Call<String> userStoryCall = repository.getUserStory(Constants.I_USER_AGENT, url); final Call<String> userStoryCall = repository.getUserStory(url);
final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION; final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION;
final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG; final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG;
final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT; final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT;
@ -401,7 +401,7 @@ public class StoriesService extends BaseService {
form.put(arg1, arg2); form.put(arg1, arg2);
final Map<String, String> signedForm = Utils.sign(form); final Map<String, String> signedForm = Utils.sign(form);
final Call<StoryStickerResponse> request = final Call<StoryStickerResponse> request =
repository.respondToSticker(Constants.I_USER_AGENT, storyId, stickerId, action, signedForm); repository.respondToSticker(storyId, stickerId, action, signedForm);
request.enqueue(new Callback<StoryStickerResponse>() { request.enqueue(new Callback<StoryStickerResponse>() {
@Override @Override
public void onResponse(@NonNull final Call<StoryStickerResponse> call, public void onResponse(@NonNull final Call<StoryStickerResponse> call,

View File

@ -46,12 +46,11 @@ public class TagsService extends BaseService {
return instance; return instance;
} }
public void follow(@NonNull final String tag, public void follow(@NonNull final String ua,
@NonNull final String tag,
@NonNull final String csrfToken, @NonNull final String csrfToken,
final ServiceCallback<Boolean> callback) { final ServiceCallback<Boolean> callback) {
final Call<String> request = webRepository.follow(Constants.USER_AGENT, final Call<String> request = webRepository.follow(ua, csrfToken, tag);
csrfToken,
tag);
request.enqueue(new Callback<String>() { request.enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -77,12 +76,11 @@ public class TagsService extends BaseService {
}); });
} }
public void unfollow(@NonNull final String tag, public void unfollow(@NonNull final String ua,
@NonNull final String tag,
@NonNull final String csrfToken, @NonNull final String csrfToken,
final ServiceCallback<Boolean> callback) { final ServiceCallback<Boolean> callback) {
final Call<String> request = webRepository.unfollow(Constants.USER_AGENT, final Call<String> request = webRepository.unfollow(ua, csrfToken, tag);
csrfToken,
tag);
request.enqueue(new Callback<String>() { request.enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {