package awais.instagrabber.webservices; import androidx.annotation.NonNull; import com.google.common.collect.ImmutableMap; import org.json.JSONArray; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import awais.instagrabber.repositories.DirectMessagesRepository; import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions.ThreadIdOrUserIds; import awais.instagrabber.repositories.requests.directmessages.LinkBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.PhotoBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.ReactionBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.StoryReplyBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.TextBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.VideoBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.VoiceBroadcastOptions; import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount; import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import retrofit2.Call; import retrofit2.Retrofit; public class DirectMessagesService extends BaseService { private static final String TAG = "DiscoverService"; private static DirectMessagesService instance; private final DirectMessagesRepository repository; private final String csrfToken; private final long userId; private final String deviceUuid; private DirectMessagesService(@NonNull final String csrfToken, final long userId, @NonNull final String deviceUuid) { this.csrfToken = csrfToken; this.userId = userId; this.deviceUuid = deviceUuid; final Retrofit retrofit = getRetrofitBuilder() .baseUrl("https://i.instagram.com") .build(); repository = retrofit.create(DirectMessagesRepository.class); } public String getCsrfToken() { return csrfToken; } public long getUserId() { return userId; } public String getDeviceUuid() { return deviceUuid; } public static DirectMessagesService getInstance(@NonNull final String csrfToken, final long userId, @NonNull final String deviceUuid) { if (instance == null || !Objects.equals(instance.getCsrfToken(), csrfToken) || !Objects.equals(instance.getUserId(), userId) || !Objects.equals(instance.getDeviceUuid(), deviceUuid)) { instance = new DirectMessagesService(csrfToken, userId, deviceUuid); } return instance; } public Call fetchInbox(final String cursor, final long seqId) { final ImmutableMap.Builder queryMapBuilder = ImmutableMap.builder() .put("visual_message_return_type", "unseen") .put("thread_message_limit", 10) .put("persistentBadging", true) .put("limit", 10); if (!TextUtils.isEmpty(cursor)) { queryMapBuilder.put("cursor", cursor); queryMapBuilder.put("direction", "older"); } if (seqId != 0) { queryMapBuilder.put("seq_id", seqId); } return repository.fetchInbox(queryMapBuilder.build()); } public Call fetchThread(final String threadId, final String cursor) { final ImmutableMap.Builder queryMapBuilder = ImmutableMap.builder() .put("visual_message_return_type", "unseen") .put("limit", 10) .put("direction", "older"); if (!TextUtils.isEmpty(cursor)) { queryMapBuilder.put("cursor", cursor); } return repository.fetchThread(threadId, queryMapBuilder.build()); } public Call fetchUnseenCount() { return repository.fetchUnseenCount(); } public Call broadcastText(final String clientContext, final ThreadIdOrUserIds threadIdOrUserIds, final String text) { final List urls = TextUtils.extractUrls(text); if (!urls.isEmpty()) { return broadcastLink(clientContext, threadIdOrUserIds, text, urls); } return broadcast(new TextBroadcastOptions(clientContext, threadIdOrUserIds, text)); } public Call broadcastLink(final String clientContext, final ThreadIdOrUserIds threadIdOrUserIds, final String linkText, final List urls) { return broadcast(new LinkBroadcastOptions(clientContext, threadIdOrUserIds, linkText, urls)); } public Call broadcastPhoto(final String clientContext, final ThreadIdOrUserIds threadIdOrUserIds, final String uploadId) { return broadcast(new PhotoBroadcastOptions(clientContext, threadIdOrUserIds, true, uploadId)); } public Call broadcastVideo(final String clientContext, final ThreadIdOrUserIds threadIdOrUserIds, final String uploadId, final String videoResult, final boolean sampled) { return broadcast(new VideoBroadcastOptions(clientContext, threadIdOrUserIds, videoResult, uploadId, sampled)); } public Call broadcastVoice(final String clientContext, final ThreadIdOrUserIds threadIdOrUserIds, final String uploadId, final List waveform, final int samplingFreq) { return broadcast(new VoiceBroadcastOptions(clientContext, threadIdOrUserIds, uploadId, waveform, samplingFreq)); } public Call broadcastStoryReply(final ThreadIdOrUserIds threadIdOrUserIds, final String text, final String mediaId, final String reelId) throws UnsupportedEncodingException { return broadcast(new StoryReplyBroadcastOptions(UUID.randomUUID().toString(), threadIdOrUserIds, text, mediaId, reelId)); } public Call broadcastReaction(final String clientContext, final ThreadIdOrUserIds threadIdOrUserIds, final String itemId, final String emoji, final boolean delete) { return broadcast(new ReactionBroadcastOptions(clientContext, threadIdOrUserIds, itemId, emoji, delete)); } private Call broadcast(@NonNull final BroadcastOptions broadcastOptions) { if (TextUtils.isEmpty(broadcastOptions.getClientContext())) { throw new IllegalArgumentException("Broadcast requires a valid client context value"); } final Map form = new HashMap<>(); if (!TextUtils.isEmpty(broadcastOptions.getThreadId())) { form.put("thread_id", broadcastOptions.getThreadId()); } else { form.put("recipient_users", new JSONArray(broadcastOptions.getUserIds()).toString()); } form.put("_csrftoken", csrfToken); form.put("_uid", userId); form.put("__uuid", deviceUuid); form.put("client_context", broadcastOptions.getClientContext()); form.put("mutation_token", broadcastOptions.getClientContext()); form.putAll(broadcastOptions.getFormMap()); form.put("action", "send_item"); final Map signedForm = Utils.sign(form); return repository.broadcast(broadcastOptions.getItemType().getValue(), signedForm); } public Call addUsers(final String threadId, final Collection userIds) { final ImmutableMap form = ImmutableMap.of( "_csrftoken", csrfToken, "_uuid", deviceUuid, "user_ids", new JSONArray(userIds).toString() ); return repository.addUsers(threadId, form); } public Call removeUsers(final String threadId, final Collection userIds) { final ImmutableMap form = ImmutableMap.of( "_csrftoken", csrfToken, "_uuid", deviceUuid, "user_ids", new JSONArray(userIds).toString() ); return repository.removeUsers(threadId, form); } public Call updateTitle(final String threadId, final String title) { final ImmutableMap form = ImmutableMap.of( "_csrftoken", csrfToken, "_uuid", deviceUuid, "title", title ); return repository.updateTitle(threadId, form); } public Call addAdmins(final String threadId, final Collection userIds) { final ImmutableMap form = ImmutableMap.of( "_csrftoken", csrfToken, "_uuid", deviceUuid, "user_ids", new JSONArray(userIds).toString() ); return repository.addAdmins(threadId, form); } public Call removeAdmins(final String threadId, final Collection userIds) { final ImmutableMap form = ImmutableMap.of( "_csrftoken", csrfToken, "_uuid", deviceUuid, "user_ids", new JSONArray(userIds).toString() ); return repository.removeAdmins(threadId, form); } public Call deleteItem(final String threadId, final String itemId) { final ImmutableMap form = ImmutableMap.of( "_csrftoken", csrfToken, "_uuid", deviceUuid ); return repository.deleteItem(threadId, itemId, form); } }