2020-09-11 14:10:27 +00:00
|
|
|
package awais.instagrabber.webservices;
|
2020-08-29 08:01:42 +00:00
|
|
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
|
|
|
import org.json.JSONArray;
|
|
|
|
import org.json.JSONException;
|
|
|
|
import org.json.JSONObject;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2020-12-17 20:13:41 +00:00
|
|
|
import java.util.UUID;
|
2020-08-29 08:01:42 +00:00
|
|
|
|
|
|
|
import awais.instagrabber.models.FeedStoryModel;
|
2020-12-20 18:35:16 +00:00
|
|
|
import awais.instagrabber.models.HighlightModel;
|
2020-08-29 08:01:42 +00:00
|
|
|
import awais.instagrabber.models.ProfileModel;
|
|
|
|
import awais.instagrabber.models.StoryModel;
|
|
|
|
import awais.instagrabber.models.enums.MediaItemType;
|
|
|
|
import awais.instagrabber.models.stickers.PollModel;
|
|
|
|
import awais.instagrabber.models.stickers.QuestionModel;
|
|
|
|
import awais.instagrabber.models.stickers.QuizModel;
|
2020-12-17 20:13:41 +00:00
|
|
|
import awais.instagrabber.models.stickers.SliderModel;
|
|
|
|
import awais.instagrabber.models.stickers.SwipeUpModel;
|
2020-08-29 08:01:42 +00:00
|
|
|
import awais.instagrabber.repositories.StoriesRepository;
|
2020-12-17 20:13:41 +00:00
|
|
|
import awais.instagrabber.repositories.responses.StoryStickerResponse;
|
2020-08-29 08:01:42 +00:00
|
|
|
import awais.instagrabber.utils.Constants;
|
2020-09-10 11:44:25 +00:00
|
|
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
2020-12-20 18:35:16 +00:00
|
|
|
import awais.instagrabber.utils.TextUtils;
|
2020-12-17 20:13:41 +00:00
|
|
|
import awais.instagrabber.utils.Utils;
|
2020-08-29 08:01:42 +00:00
|
|
|
import retrofit2.Call;
|
|
|
|
import retrofit2.Callback;
|
|
|
|
import retrofit2.Response;
|
|
|
|
import retrofit2.Retrofit;
|
|
|
|
|
|
|
|
public class StoriesService extends BaseService {
|
|
|
|
private static final String TAG = "StoriesService";
|
2020-10-17 10:07:03 +00:00
|
|
|
private static final boolean loadFromMock = false;
|
2020-08-29 08:01:42 +00:00
|
|
|
|
|
|
|
private final StoriesRepository repository;
|
|
|
|
|
|
|
|
private static StoriesService instance;
|
|
|
|
|
|
|
|
private StoriesService() {
|
|
|
|
final Retrofit retrofit = getRetrofitBuilder()
|
2020-12-17 20:13:41 +00:00
|
|
|
.baseUrl("https://i.instagram.com")
|
2020-08-29 08:01:42 +00:00
|
|
|
.build();
|
|
|
|
repository = retrofit.create(StoriesRepository.class);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static StoriesService getInstance() {
|
|
|
|
if (instance == null) {
|
|
|
|
instance = new StoriesService();
|
|
|
|
}
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-12-25 21:40:17 +00:00
|
|
|
public void fetch(final String mediaId,
|
|
|
|
final ServiceCallback<StoryModel> callback) {
|
|
|
|
final Call<String> request = repository.fetch(mediaId);
|
|
|
|
request.enqueue(new Callback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onResponse(@NonNull final Call<String> call,
|
|
|
|
@NonNull final Response<String> response) {
|
|
|
|
if (callback == null) return;
|
|
|
|
final String body = response.body();
|
|
|
|
if (body == null) {
|
|
|
|
callback.onSuccess(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
final JSONObject itemJson = new JSONObject(body).getJSONArray("items").getJSONObject(0);
|
|
|
|
callback.onSuccess(ResponseBodyUtils.parseStoryItem(itemJson, false, false, null));
|
|
|
|
} catch (JSONException e) {
|
|
|
|
callback.onFailure(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(@NonNull final Call<String> call,
|
|
|
|
@NonNull final Throwable t) {
|
|
|
|
if (callback != null) {
|
|
|
|
callback.onFailure(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-17 20:13:41 +00:00
|
|
|
public void getFeedStories(final String csrfToken, final ServiceCallback<List<FeedStoryModel>> callback) {
|
|
|
|
final Map<String, Object> form = new HashMap<>(4);
|
|
|
|
form.put("reason", "cold_start");
|
|
|
|
form.put("_csrftoken", csrfToken);
|
|
|
|
form.put("_uuid", UUID.randomUUID().toString());
|
|
|
|
form.put("supported_capabilities_new", Constants.SUPPORTED_CAPABILITIES);
|
|
|
|
final Map<String, String> signedForm = Utils.sign(form);
|
2020-12-20 18:35:16 +00:00
|
|
|
final Call<String> response = repository.getFeedStories(Constants.I_USER_AGENT, signedForm);
|
2020-08-29 08:01:42 +00:00
|
|
|
response.enqueue(new Callback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
|
|
final String body = response.body();
|
|
|
|
if (body == null) {
|
|
|
|
Log.e(TAG, "getFeedStories: body is empty");
|
|
|
|
return;
|
|
|
|
}
|
2020-10-17 10:07:03 +00:00
|
|
|
parseStoriesBody(body, callback);
|
2020-08-29 08:01:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
|
|
callback.onFailure(t);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-17 10:07:03 +00:00
|
|
|
private void parseStoriesBody(final String body, final ServiceCallback<List<FeedStoryModel>> callback) {
|
|
|
|
try {
|
|
|
|
final List<FeedStoryModel> feedStoryModels = new ArrayList<>();
|
2020-12-17 20:13:41 +00:00
|
|
|
final JSONArray feedStoriesReel = new JSONObject(body).getJSONArray("tray");
|
2020-10-17 10:07:03 +00:00
|
|
|
for (int i = 0; i < feedStoriesReel.length(); ++i) {
|
2020-12-17 20:13:41 +00:00
|
|
|
final JSONObject node = feedStoriesReel.getJSONObject(i);
|
2020-10-17 10:07:03 +00:00
|
|
|
final JSONObject user = node.getJSONObject(node.has("user") ? "user" : "owner");
|
|
|
|
final ProfileModel profileModel = new ProfileModel(false, false, false,
|
2020-12-17 20:13:41 +00:00
|
|
|
user.getString("pk"),
|
2020-10-17 10:07:03 +00:00
|
|
|
user.getString("username"),
|
|
|
|
null, null, null,
|
|
|
|
user.getString("profile_pic_url"),
|
2020-12-20 02:38:21 +00:00
|
|
|
null, 0, 0, 0, false, false, false, false, false);
|
2020-10-17 10:07:03 +00:00
|
|
|
final String id = node.getString("id");
|
|
|
|
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
|
|
|
|
feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead));
|
|
|
|
}
|
|
|
|
callback.onSuccess(feedStoryModels);
|
|
|
|
} catch (JSONException e) {
|
|
|
|
Log.e(TAG, "Error parsing json", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-20 18:35:16 +00:00
|
|
|
public void fetchHighlights(final String profileId,
|
|
|
|
final ServiceCallback<List<HighlightModel>> callback) {
|
|
|
|
final Call<String> request = repository.fetchHighlights(profileId);
|
|
|
|
request.enqueue(new Callback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
|
|
try {
|
|
|
|
if (callback == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final String body = response.body();
|
|
|
|
if (TextUtils.isEmpty(body)) {
|
|
|
|
callback.onSuccess(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final JSONArray highlightsReel = new JSONObject(body).getJSONArray("tray");
|
|
|
|
|
|
|
|
final int length = highlightsReel.length();
|
|
|
|
final List<HighlightModel> highlightModels = new ArrayList<>();
|
|
|
|
// final String[] highlightIds = new String[length];
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
final JSONObject highlightNode = highlightsReel.getJSONObject(i);
|
|
|
|
highlightModels.add(new HighlightModel(
|
|
|
|
highlightNode.getString("title"),
|
|
|
|
highlightNode.getString(Constants.EXTRAS_ID),
|
|
|
|
highlightNode.getJSONObject("cover_media")
|
|
|
|
.getJSONObject("cropped_image_version")
|
|
|
|
.getString("url")
|
|
|
|
));
|
|
|
|
}
|
|
|
|
callback.onSuccess(highlightModels);
|
|
|
|
} catch (JSONException e) {
|
|
|
|
Log.e(TAG, "onResponse", e);
|
|
|
|
callback.onFailure(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
|
|
if (callback != null) {
|
|
|
|
callback.onFailure(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-08-29 08:01:42 +00:00
|
|
|
public void getUserStory(final String id,
|
|
|
|
final String username,
|
|
|
|
final boolean isLoc,
|
|
|
|
final boolean isHashtag,
|
|
|
|
final boolean highlight,
|
|
|
|
final ServiceCallback<List<StoryModel>> callback) {
|
2020-09-21 02:36:23 +00:00
|
|
|
final String url = buildUrl(id, isLoc, isHashtag, highlight);
|
|
|
|
final Call<String> userStoryCall = repository.getUserStory(Constants.I_USER_AGENT, url);
|
2020-08-29 08:01:42 +00:00
|
|
|
userStoryCall.enqueue(new Callback<String>() {
|
|
|
|
@Override
|
|
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
|
|
JSONObject data;
|
|
|
|
String localUsername = username;
|
|
|
|
try {
|
|
|
|
final String body = response.body();
|
|
|
|
if (body == null) {
|
|
|
|
Log.e(TAG, "body is null");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
data = new JSONObject(body);
|
|
|
|
|
2020-09-21 02:36:23 +00:00
|
|
|
if (!highlight)
|
2020-08-29 08:01:42 +00:00
|
|
|
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
|
|
|
|
else if (highlight) data = data.getJSONObject("reels").optJSONObject(id);
|
|
|
|
|
|
|
|
if (data != null
|
|
|
|
&& localUsername == null
|
|
|
|
&& !isLoc
|
|
|
|
&& !isHashtag)
|
|
|
|
localUsername = data.getJSONObject("user").getString("username");
|
|
|
|
|
|
|
|
JSONArray media;
|
|
|
|
if (data != null
|
|
|
|
&& (media = data.optJSONArray("items")) != null
|
|
|
|
&& media.length() > 0 && media.optJSONObject(0) != null) {
|
|
|
|
|
|
|
|
final int mediaLen = media.length();
|
|
|
|
final List<StoryModel> models = new ArrayList<>();
|
|
|
|
for (int i = 0; i < mediaLen; ++i) {
|
|
|
|
data = media.getJSONObject(i);
|
2020-12-25 21:40:17 +00:00
|
|
|
models.add(ResponseBodyUtils.parseStoryItem(data, isLoc, isHashtag, localUsername));
|
2020-08-29 08:01:42 +00:00
|
|
|
}
|
|
|
|
callback.onSuccess(models);
|
2020-10-17 10:07:03 +00:00
|
|
|
} else {
|
2020-09-26 00:15:46 +00:00
|
|
|
callback.onSuccess(null);
|
|
|
|
}
|
2020-08-29 08:01:42 +00:00
|
|
|
} catch (JSONException e) {
|
|
|
|
Log.e(TAG, "Error parsing string");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
|
|
callback.onFailure(t);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-17 20:13:41 +00:00
|
|
|
private void respondToSticker(final String storyId,
|
|
|
|
final String stickerId,
|
|
|
|
final String action,
|
|
|
|
final String arg1,
|
|
|
|
final String arg2,
|
|
|
|
final String userId,
|
|
|
|
final String csrfToken,
|
|
|
|
final ServiceCallback<StoryStickerResponse> callback) {
|
|
|
|
final Map<String, Object> form = new HashMap<>();
|
|
|
|
form.put("_csrftoken", csrfToken);
|
|
|
|
form.put("_uid", userId);
|
|
|
|
form.put("_uuid", UUID.randomUUID().toString());
|
|
|
|
form.put("mutation_token", UUID.randomUUID().toString());
|
|
|
|
form.put("client_context", UUID.randomUUID().toString());
|
|
|
|
form.put("radio_type", "wifi-none");
|
|
|
|
form.put(arg1, arg2);
|
|
|
|
final Map<String, String> signedForm = Utils.sign(form);
|
|
|
|
final Call<StoryStickerResponse> request =
|
|
|
|
repository.respondToSticker(Constants.I_USER_AGENT, storyId, stickerId, action, signedForm);
|
|
|
|
request.enqueue(new Callback<StoryStickerResponse>() {
|
|
|
|
@Override
|
|
|
|
public void onResponse(@NonNull final Call<StoryStickerResponse> call,
|
|
|
|
@NonNull final Response<StoryStickerResponse> response) {
|
|
|
|
if (callback != null) {
|
|
|
|
callback.onSuccess(response.body());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(@NonNull final Call<StoryStickerResponse> call,
|
|
|
|
@NonNull final Throwable t) {
|
|
|
|
if (callback != null) {
|
|
|
|
callback.onFailure(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// RespondAction.java
|
|
|
|
public void respondToQuestion(final String storyId,
|
|
|
|
final String stickerId,
|
|
|
|
final String answer,
|
|
|
|
final String userId,
|
|
|
|
final String csrfToken,
|
|
|
|
final ServiceCallback<StoryStickerResponse> callback) {
|
|
|
|
respondToSticker(storyId, stickerId, "story_question_response", "response", answer, userId, csrfToken, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
// QuizAction.java
|
|
|
|
public void respondToQuiz(final String storyId,
|
|
|
|
final String stickerId,
|
|
|
|
final int answer,
|
|
|
|
final String userId,
|
|
|
|
final String csrfToken,
|
|
|
|
final ServiceCallback<StoryStickerResponse> callback) {
|
|
|
|
respondToSticker(storyId, stickerId, "story_quiz_answer", "answer", String.valueOf(answer), userId, csrfToken, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
// VoteAction.java
|
|
|
|
public void respondToPoll(final String storyId,
|
|
|
|
final String stickerId,
|
|
|
|
final int answer,
|
|
|
|
final String userId,
|
|
|
|
final String csrfToken,
|
|
|
|
final ServiceCallback<StoryStickerResponse> callback) {
|
|
|
|
respondToSticker(storyId, stickerId, "story_poll_vote", "vote", String.valueOf(answer), userId, csrfToken, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void respondToSlider(final String storyId,
|
|
|
|
final String stickerId,
|
|
|
|
final double answer,
|
|
|
|
final String userId,
|
|
|
|
final String csrfToken,
|
|
|
|
final ServiceCallback<StoryStickerResponse> callback) {
|
|
|
|
respondToSticker(storyId, stickerId, "story_slider_vote", "vote", String.valueOf(answer), userId, csrfToken, callback);
|
|
|
|
}
|
|
|
|
|
2020-09-21 02:36:23 +00:00
|
|
|
private String buildUrl(final String id, final boolean isLoc, final boolean isHashtag, final boolean highlight) {
|
2020-08-29 08:01:42 +00:00
|
|
|
final String userId = id.replace(":", "%3A");
|
|
|
|
final StringBuilder builder = new StringBuilder();
|
2020-09-21 02:36:23 +00:00
|
|
|
builder.append("https://i.instagram.com/api/v1/");
|
2020-08-29 08:01:42 +00:00
|
|
|
if (isLoc) {
|
|
|
|
builder.append("locations/");
|
2020-10-17 10:07:03 +00:00
|
|
|
} else if (isHashtag) {
|
2020-08-29 08:01:42 +00:00
|
|
|
builder.append("tags/");
|
2020-10-17 10:07:03 +00:00
|
|
|
} else if (highlight) {
|
2020-09-27 01:33:41 +00:00
|
|
|
builder.append("feed/reels_media/?user_ids=");
|
2020-10-17 10:07:03 +00:00
|
|
|
} else {
|
2020-08-29 08:01:42 +00:00
|
|
|
builder.append("feed/user/");
|
|
|
|
}
|
|
|
|
builder.append(userId);
|
|
|
|
if (!highlight) {
|
2020-09-21 02:36:23 +00:00
|
|
|
builder.append("/story/");
|
2020-08-29 08:01:42 +00:00
|
|
|
}
|
|
|
|
return builder.toString();
|
|
|
|
}
|
|
|
|
}
|