mirror of
				https://codeberg.org/Bazsalanszky/Infinity-For-Lemmy.git
				synced 2025-10-31 17:08:11 +01:00 
			
		
		
		
	Add Reveddit fallback for retrieving deleted comments (#591)
This commit is contained in:
		| @@ -146,6 +146,16 @@ class AppModule { | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|     @Provides | ||||
|     @Named("reveddit") | ||||
|     @Singleton | ||||
|     Retrofit provideRevedditRetrofit() { | ||||
|         return new Retrofit.Builder() | ||||
|                 .baseUrl(APIUtils.REVEDDIT_API_BASE_URI) | ||||
|                 .addConverterFactory(ScalarsConverterFactory.create()) | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|     @Provides | ||||
|     @Named("vReddIt") | ||||
|     @Singleton | ||||
|   | ||||
| @@ -10,4 +10,13 @@ public interface PushshiftAPI { | ||||
|  | ||||
|     @GET("reddit/submission/search/") | ||||
|     Call<String> getRemovedPost(@Query("ids") String postId); | ||||
|  | ||||
|     @GET("reddit/comment/search/") | ||||
|     Call<String> searchComments(@Query("link_id") String linkId, | ||||
|                                 @Query("limit") int limit, | ||||
|                                 @Query("sort") String sort, | ||||
|                                 @Query(value = "fields", encoded = true) String fields, | ||||
|                                 @Query("after") long after, | ||||
|                                 @Query("before") long before, | ||||
|                                 @Query("q") String query); | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,20 @@ | ||||
| package ml.docilealligator.infinityforreddit.apis; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| import retrofit2.Call; | ||||
| import retrofit2.http.GET; | ||||
| import retrofit2.http.HeaderMap; | ||||
| import retrofit2.http.Query; | ||||
|  | ||||
| public interface RevedditAPI { | ||||
|     @GET("/short/thread-comments/") | ||||
|     Call<String> getRemovedComments(@HeaderMap Map<String, String> headers, | ||||
|                                     @Query("link_id") String threadId, | ||||
|                                     @Query("after") long after, | ||||
|                                     @Query("root_comment_id") String rootCommentId, | ||||
|                                     @Query("comment_id") String commentId, | ||||
|                                     @Query("num_comments") int numComments, | ||||
|                                     @Query("post_created_utc") long postCreatedUtc, | ||||
|                                     @Query("focus_comment_removed") boolean focusCommentRemoved); | ||||
| } | ||||
| @@ -4,6 +4,7 @@ import android.os.AsyncTask; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
|  | ||||
| import org.json.JSONArray; | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| @@ -37,12 +38,43 @@ public class FetchRemovedComment { | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
|     private static Comment parseRemovedComment(JSONObject result, Comment comment) throws JSONException { | ||||
|     // At the moment of writing this code, directly fetching a removed comment from | ||||
|     // Pushift.io API returns an Internal Server Error, so we temporarily do it this way instead. | ||||
|     // If this fails to return the removed comment, we try our luck with Reveddit API | ||||
|     public static void searchRemovedComment(Retrofit retrofit, Comment comment, FetchRemovedCommentListener listener) { | ||||
|         long after = (comment.getCommentTimeMillis() / 1000) - 1; // 1 second before comment creation epoch | ||||
|         retrofit.create(PushshiftAPI.class).searchComments( | ||||
|                 comment.getLinkId(), | ||||
|                 3000, | ||||
|                 "asc", | ||||
|                 "id,author,body", | ||||
|                 after, | ||||
|                 after + 43200, // 12 Hours later | ||||
|                 "*") | ||||
|                 .enqueue(new Callback<String>() { | ||||
|                     @Override | ||||
|                     public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { | ||||
|                         if (response.isSuccessful()) { | ||||
|                             new ParseCommentAsyncTask(response.body(), comment, listener).execute(); | ||||
|                         } else { | ||||
|                             listener.fetchFailed(); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { | ||||
|                         t.printStackTrace(); | ||||
|                         listener.fetchFailed(); | ||||
|                     } | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
|     private static Comment parseRemovedComment(@NonNull JSONObject result, Comment comment) throws JSONException { | ||||
|         String id = result.getString(JSONUtils.ID_KEY); | ||||
|         String author = result.getString(JSONUtils.AUTHOR_KEY); | ||||
|         String body = Utils.modifyMarkdown(result.optString(JSONUtils.BODY_KEY).trim()); | ||||
|  | ||||
|         if ( id.equals(comment.getId()) && | ||||
|         if (id.equals(comment.getId()) && | ||||
|                 (!author.equals(comment.getAuthor()) || | ||||
|                         !body.equals(comment.getCommentRawText())) | ||||
|         ) { | ||||
| @@ -63,8 +95,8 @@ public class FetchRemovedComment { | ||||
|  | ||||
|     private static class ParseCommentAsyncTask extends AsyncTask<Void, Void, Void> { | ||||
|  | ||||
|         private String responseBody; | ||||
|         private FetchRemovedCommentListener listener; | ||||
|         private final String responseBody; | ||||
|         private final FetchRemovedCommentListener listener; | ||||
|         Comment comment; | ||||
|  | ||||
|         public ParseCommentAsyncTask(String responseBody, Comment comment, FetchRemovedCommentListener listener) { | ||||
| @@ -76,8 +108,17 @@ public class FetchRemovedComment { | ||||
|         @Override | ||||
|         protected Void doInBackground(Void... voids) { | ||||
|             try { | ||||
|                 JSONObject commentJSON = new JSONObject(responseBody).getJSONArray(JSONUtils.DATA_KEY).getJSONObject(0); | ||||
|                 comment = parseRemovedComment(commentJSON, comment); | ||||
|                 JSONArray commentJSONArray = new JSONObject(responseBody).getJSONArray(JSONUtils.DATA_KEY); | ||||
|                 JSONObject commentFound = null; | ||||
|                 for (int i = 0; i < commentJSONArray.length(); i++) { | ||||
|                     JSONObject commentJSON = commentJSONArray.getJSONObject(i); | ||||
|                     if (!commentJSON.isNull("id") && commentJSON.getString("id").equals(comment.getId())) { | ||||
|                         commentFound = commentJSON; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 assert commentFound != null; | ||||
|                 comment = parseRemovedComment(commentFound, comment); | ||||
|             } catch (JSONException e) { | ||||
|                 e.printStackTrace(); | ||||
|                 comment = null; | ||||
|   | ||||
| @@ -0,0 +1,108 @@ | ||||
| package ml.docilealligator.infinityforreddit.comment; | ||||
|  | ||||
| import android.os.AsyncTask; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
|  | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| import ml.docilealligator.infinityforreddit.apis.RevedditAPI; | ||||
| import ml.docilealligator.infinityforreddit.utils.APIUtils; | ||||
| import ml.docilealligator.infinityforreddit.utils.JSONUtils; | ||||
| import ml.docilealligator.infinityforreddit.utils.Utils; | ||||
| import retrofit2.Call; | ||||
| import retrofit2.Callback; | ||||
| import retrofit2.Response; | ||||
| import retrofit2.Retrofit; | ||||
|  | ||||
| public class FetchRemovedCommentReveddit { | ||||
|  | ||||
|     public static void fetchRemovedComment(Retrofit retrofit, Comment comment, long postCreatedUtc, int nComments, FetchRemovedCommentListener listener) { | ||||
|         String parentIdWithoutPrefix = comment.getParentId().substring(3); | ||||
|         String rootCommentId = parentIdWithoutPrefix.equals(comment.getLinkId()) ? comment.getId() : parentIdWithoutPrefix; | ||||
|         retrofit.create(RevedditAPI.class).getRemovedComments( | ||||
|                 APIUtils.getRevedditHeader(), | ||||
|                 comment.getLinkId(), | ||||
|                 (comment.getCommentTimeMillis() / 1000) - 1, | ||||
|                 rootCommentId, | ||||
|                 comment.getId(), | ||||
|                 nComments, | ||||
|                 postCreatedUtc / 1000, | ||||
|                 true) | ||||
|                 .enqueue(new Callback<String>() { | ||||
|                     @Override | ||||
|                     public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { | ||||
|                         if (response.isSuccessful()) { | ||||
|                             new ParseCommentAsyncTask(response.body(), comment, listener).execute(); | ||||
|                         } else { | ||||
|                             listener.fetchFailed(); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { | ||||
|                         t.printStackTrace(); | ||||
|                         listener.fetchFailed(); | ||||
|                     } | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
|     private static Comment parseRemovedComment(JSONObject result, Comment comment) throws JSONException { | ||||
|         String id = result.getString(JSONUtils.ID_KEY); | ||||
|         String author = result.getString(JSONUtils.AUTHOR_KEY); | ||||
|         String body = Utils.modifyMarkdown(result.optString(JSONUtils.BODY_KEY).trim()); | ||||
|  | ||||
|         if (id.equals(comment.getId()) && | ||||
|                 (!author.equals(comment.getAuthor()) || | ||||
|                         !body.equals(comment.getCommentRawText())) | ||||
|         ) { | ||||
|             comment.setAuthor(author); | ||||
|             comment.setCommentMarkdown(body); | ||||
|             comment.setCommentRawText(body); | ||||
|             return comment; | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public interface FetchRemovedCommentListener { | ||||
|         void fetchSuccess(Comment comment); | ||||
|  | ||||
|         void fetchFailed(); | ||||
|     } | ||||
|  | ||||
|     private static class ParseCommentAsyncTask extends AsyncTask<Void, Void, Void> { | ||||
|  | ||||
|         private final String responseBody; | ||||
|         private final FetchRemovedCommentListener listener; | ||||
|         Comment comment; | ||||
|  | ||||
|         public ParseCommentAsyncTask(String responseBody, Comment comment, FetchRemovedCommentListener listener) { | ||||
|             this.responseBody = responseBody; | ||||
|             this.comment = comment; | ||||
|             this.listener = listener; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         protected Void doInBackground(Void... voids) { | ||||
|             try { | ||||
|                 JSONObject commentJSON = new JSONObject(responseBody).getJSONObject(comment.getId()); | ||||
|                 comment = parseRemovedComment(commentJSON, comment); | ||||
|             } catch (JSONException e) { | ||||
|                 e.printStackTrace(); | ||||
|                 comment = null; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         protected void onPostExecute(Void aVoid) { | ||||
|             super.onPostExecute(aVoid); | ||||
|             if (comment != null) | ||||
|                 listener.fetchSuccess(comment); | ||||
|             else | ||||
|                 listener.fetchFailed(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -89,6 +89,7 @@ import ml.docilealligator.infinityforreddit.bottomsheetfragments.PostCommentSort | ||||
| import ml.docilealligator.infinityforreddit.comment.Comment; | ||||
| import ml.docilealligator.infinityforreddit.comment.FetchComment; | ||||
| import ml.docilealligator.infinityforreddit.comment.FetchRemovedComment; | ||||
| import ml.docilealligator.infinityforreddit.comment.FetchRemovedCommentReveddit; | ||||
| import ml.docilealligator.infinityforreddit.comment.ParseComment; | ||||
| import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; | ||||
| import ml.docilealligator.infinityforreddit.customviews.CustomToroContainer; | ||||
| @@ -144,6 +145,9 @@ public class ViewPostDetailFragment extends Fragment implements FragmentCommunic | ||||
|     @Named("pushshift") | ||||
|     Retrofit pushshiftRetrofit; | ||||
|     @Inject | ||||
|     @Named("reveddit") | ||||
|     Retrofit revedditRetrofit; | ||||
|     @Inject | ||||
|     @Named("oauth") | ||||
|     Retrofit mOauthRetrofit; | ||||
|     @Inject | ||||
| @@ -1717,7 +1721,7 @@ public class ViewPostDetailFragment extends Fragment implements FragmentCommunic | ||||
|  | ||||
|     public void showRemovedComment(Comment comment, int position) { | ||||
|         Toast.makeText(activity, R.string.fetching_removed_comment, Toast.LENGTH_SHORT).show(); | ||||
|         FetchRemovedComment.fetchRemovedComment( | ||||
|         FetchRemovedComment.searchRemovedComment( | ||||
|                 pushshiftRetrofit, | ||||
|                 comment, | ||||
|                 new FetchRemovedComment.FetchRemovedCommentListener() { | ||||
| @@ -1726,12 +1730,23 @@ public class ViewPostDetailFragment extends Fragment implements FragmentCommunic | ||||
|                         mCommentsAdapter.editComment(comment.getAuthor(), comment.getCommentMarkdown(), position); | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void fetchFailed() { | ||||
|                         // Reveddit fallback | ||||
|                         FetchRemovedCommentReveddit.fetchRemovedComment(revedditRetrofit, comment, mPost.getPostTimeMillis(), mPost.getNComments(), new FetchRemovedCommentReveddit.FetchRemovedCommentListener() { | ||||
|                             @Override | ||||
|                             public void fetchSuccess(Comment comment) { | ||||
|                                 mCommentsAdapter.editComment(comment.getAuthor(), comment.getCommentMarkdown(), position); | ||||
|                             } | ||||
|  | ||||
|                             @Override | ||||
|                             public void fetchFailed() { | ||||
|                                 Toast.makeText(activity, R.string.show_removed_comment_failed, Toast.LENGTH_SHORT).show(); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
|     public void changeToNomalThreadMode() { | ||||
|         isSingleCommentThreadMode = false; | ||||
|   | ||||
| @@ -156,8 +156,10 @@ public class ParsePost { | ||||
|         boolean nsfw = data.getBoolean(JSONUtils.NSFW_KEY); | ||||
|         boolean stickied = data.getBoolean(JSONUtils.STICKIED_KEY); | ||||
|         boolean archived = data.getBoolean(JSONUtils.ARCHIVED_KEY); | ||||
|         boolean locked = data.getBoolean(JSONUtils.LOCKEC_KEY); | ||||
|         boolean locked = data.getBoolean(JSONUtils.LOCKED_KEY); | ||||
|         boolean saved = data.getBoolean(JSONUtils.SAVED_KEY); | ||||
|         boolean deleted = !data.isNull(JSONUtils.REMOVED_BY_CATEGORY_KEY) && data.getString(JSONUtils.REMOVED_BY_CATEGORY_KEY).equals("deleted"); | ||||
|         boolean removed = !data.isNull(JSONUtils.REMOVED_BY_CATEGORY_KEY) && data.getString(JSONUtils.REMOVED_BY_CATEGORY_KEY).equals("moderator"); | ||||
|         StringBuilder postFlairHTMLBuilder = new StringBuilder(); | ||||
|         String flair = ""; | ||||
|         if (data.has(JSONUtils.LINK_FLAIR_RICHTEXT_KEY)) { | ||||
| @@ -231,7 +233,7 @@ public class ParsePost { | ||||
|                     author, authorFlair, authorFlairHTMLBuilder.toString(), | ||||
|                     postTime, title, previews, | ||||
|                     score, voteType, nComments, upvoteRatio, flair, awardingsBuilder.toString(), nAwards, hidden, | ||||
|                     spoiler, nsfw, stickied, archived, locked, saved, true); | ||||
|                     spoiler, nsfw, stickied, archived, locked, saved, deleted, removed, true); | ||||
|             post.setCrosspostParentId(crosspostParent.getId()); | ||||
|             return post; | ||||
|         } else { | ||||
| @@ -239,7 +241,7 @@ public class ParsePost { | ||||
|                     author, authorFlair, authorFlairHTMLBuilder.toString(), | ||||
|                     postTime, title, previews, | ||||
|                     score, voteType, nComments, upvoteRatio, flair, awardingsBuilder.toString(), nAwards, hidden, | ||||
|                     spoiler, nsfw, stickied, archived, locked, saved, false); | ||||
|                     spoiler, nsfw, stickied, archived, locked, saved, deleted, removed, false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -250,7 +252,7 @@ public class ParsePost { | ||||
|                                   int score, int voteType, int nComments, int upvoteRatio, String flair, | ||||
|                                   String awards, int nAwards, boolean hidden, boolean spoiler, | ||||
|                                   boolean nsfw, boolean stickied, boolean archived, boolean locked, | ||||
|                                   boolean saved, boolean isCrosspost) throws JSONException { | ||||
|                                   boolean saved, boolean deleted, boolean removed, boolean isCrosspost) throws JSONException { | ||||
|         Post post; | ||||
|  | ||||
|         boolean isVideo = data.getBoolean(JSONUtils.IS_VIDEO_KEY); | ||||
|   | ||||
| @@ -22,6 +22,7 @@ public class APIUtils { | ||||
|     public static final String REDGIFS_API_BASE_URI = "https://api.redgifs.com/v1/gfycats/"; | ||||
|     public static final String IMGUR_API_BASE_URI = "https://api.imgur.com/3/"; | ||||
|     public static final String PUSHSHIFT_API_BASE_URI = "https://api.pushshift.io/"; | ||||
|     public static final String REVEDDIT_API_BASE_URI = "https://api.reveddit.com/"; | ||||
|     public static final String STRAPI_BASE_URI = "https://strapi.reddit.com"; | ||||
|     public static final String STREAMABLE_API_BASE_URI = "https://api.streamable.com"; | ||||
|  | ||||
| @@ -105,6 +106,11 @@ public class APIUtils { | ||||
|     public static final String GILD_TYPE = "gild_type"; | ||||
|     public static final String IS_ANONYMOUS = "is_anonymous"; | ||||
|  | ||||
|     public static final String ORIGIN_KEY = "Origin"; | ||||
|     public static final String REVEDDIT_ORIGIN = "https://www.reveddit.com"; | ||||
|     public static final String REFERER_KEY = "Referer"; | ||||
|     public static final String REVEDDIT_REFERER = "https://www.reveddit.com/"; | ||||
|  | ||||
|     public static Map<String, String> getHttpBasicAuthHeader() { | ||||
|         Map<String, String> params = new HashMap<>(); | ||||
|         String credentials = String.format("%s:%s", APIUtils.CLIENT_ID, ""); | ||||
| @@ -123,4 +129,12 @@ public class APIUtils { | ||||
|     public static RequestBody getRequestBody(String s) { | ||||
|         return RequestBody.create(s, MediaType.parse("text/plain")); | ||||
|     } | ||||
|  | ||||
|     public static Map<String, String> getRevedditHeader() { | ||||
|         Map<String, String> params = new HashMap<>(); | ||||
|         params.put(APIUtils.ORIGIN_KEY, APIUtils.REVEDDIT_ORIGIN); | ||||
|         params.put(APIUtils.REFERER_KEY, APIUtils.REVEDDIT_REFERER); | ||||
|         params.put(APIUtils.USER_AGENT_KEY, APIUtils.USER_AGENT); | ||||
|         return params; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -85,8 +85,10 @@ public class JSONUtils { | ||||
|     public static final String DESCRIPTION_HTML_KEY = "description_html"; | ||||
|     public static final String DESCRIPTION_MD_KEY = "description_md"; | ||||
|     public static final String ARCHIVED_KEY = "archived"; | ||||
|     public static final String LOCKEC_KEY = "locked"; | ||||
|     public static final String LOCKED_KEY = "locked"; | ||||
|     public static final String SAVED_KEY = "saved"; | ||||
|     public static final String REMOVED_KEY = "removed"; | ||||
|     public static final String REMOVED_BY_CATEGORY_KEY = "removed_by_category"; | ||||
|     public static final String TEXT_EDITABLE_KEY = "text_editable"; | ||||
|     public static final String SUBJECT_KEY = "subject"; | ||||
|     public static final String CONTEXT_KEY = "context"; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user