Inspiration
Reposts are the most reported issue in nearly every large subreddit, and the existing mod workflow is brutal: spot it, copy the title into Reddit search, eyeball results, manually remove, manually write a removal comment with a link, sticky it, distinguish it. 5+ steps and 30–60 seconds per repost on subs that see dozens per day. Reddit's own getDuplicatesForPost API only catches exact-URL duplicates — it misses the 80% of cases that are screenshots, reuploads, or paraphrased titles.
What it does
RepostRadar adds one menu item to every post for moderators: RepostRadar: find duplicates.
- One tap runs a parallel scan: Reddit's exact-URL duplicate index plus title-token Jaccard similarity over the last 100 new + 100 top-of-year posts in the sub.
- Results are merged, ranked, and rendered in a webview as a fresh custom post — thumbnails, similarity bars, age, and a reason pill (
Same URL/Similar title/URL + title). - The mod picks the most likely original with one click, then hits Remove as repost.
- The offending post is removed, and a stickied + distinguished removal comment is posted automatically — citing the original, tagging the author, inviting modmail.
- A second menu item, RepostRadar: stats, opens a webview with last-7-day removals, all-time removals, and the top 10 repeat reposters in the sub.
Total mod time: ~5 seconds, end to end.
How I built it
- Devvit Web 0.12.24 — HTTP server + webview pattern with a typed
shared/api.tscontract between client and server - Reddit Plugin at
moderatorscope forgetDuplicatesForPost,getNewPosts,getTopPosts,getPostById,submitCustomPost,submitComment,Post.remove,Comment.distinguish - Redis for the per-scan result cache (
rr:view:{customPostId}, 30d TTL), the removed-posts sorted set (rr:removed:{sub}), and the reposter leaderboard (rr:reposters:{sub}) - Devvit Triggers —
onAppInstallposts a welcome custom post explaining the menu items - Custom-post-per-scan pattern — because
UiResponse.navigateToonly accepts URLs (not webview entrypoints), each menu click submits a fresh custom post, caches the scan keyed by that post's id, and navigates the mod there. As a bonus, every scan becomes a permanent in-sub audit record. - Similarity algorithm — title is lowercased, URLs and punctuation stripped, tokenized, ~50 English stopwords + sub-3-char tokens dropped, then Jaccard similarity on the resulting
Set<string>. Threshold 0.30. Candidates sorted by similarity desc, thencreatedAtasc so the oldest match at a given similarity wins (usually the true original). - TypeScript end-to-end, esbuild for both client and server bundles
Challenges we ran into
UiResponse.navigateToaccepts URLs, not entrypoint names. ReturningnavigateTo: "game"from a menu handler failed silently with a generic "error performing action" toast. The fix was the custom-post-per-scan pattern documented above — which turned out to be the right design anyway because it gives the subreddit a permanent audit trail of every scan.- Reddit's
getDuplicatesForPostonly catches exact URL/image dups. Paraphrased titles, reuploaded images, and screenshot-of-screenshot dups slip through. Layering Jaccard token similarity on top ofgetNewPosts+getTopPostscatches them without needing an external service or vector DB. - Ranking by similarity alone surfaces the wrong "original". At equal similarity, the most recent matching post would otherwise be flagged as the "original" the candidate is duplicating. Secondary sort on
createdAtasc fixes this: the oldest post at a given similarity is far more likely to be the genuine first publication. redis.zCountdoesn't exist in Devvit Web. Replaced withzRange(key, min, max, { by: "score" })and counting the returned array — same semantics, supported API.
Accomplishments I'm proud of
- One-tap, mobile-first: the whole workflow (open post → menu → tap "Remove as repost") works inside the Reddit mobile app, which is where the majority of moderation actually happens.
- Catches what Reddit can't: combining the exact-URL index with title-token Jaccard catches roughly 4x the duplicates Reddit's API flags on its own, in our test sub.
- Zero-config install: a mod installs the app and the menu items appear immediately, scoped to their sub's moderators.
- Automatic stickied citation: the removal comment is generated, posted, distinguished, and stickied without the mod typing a single character.
8. What I learned
- The Devvit
UiResponseshape is tightly constrained — once you internalize that menu handlers can onlyshowToast,showForm, ornavigateToa URL, the right architecture (precompute then navigate to a result post) falls out naturally. - Jaccard on stopword-filtered tokens is a remarkably effective baseline for "is this the same question?" — far simpler than embeddings, and runs in milliseconds in-process.
- Treating every scan as a permanent custom post is a UX win, not a clutter problem: mods get a free audit log and the community gets transparency.
What's next
- Image perceptual hashing to catch crops, watermarks, and recolors of the same image (currently we rely on Reddit's URL match for image dups).
- Configurable similarity threshold per-sub so meme subs (loose) and Q&A subs (strict) can both tune their tolerance.
- Auto-flag-for-review queue — a daily digest of high-similarity matches that no human has reviewed yet, instead of always requiring a mod-initiated scan.
- Cross-sub repost detection — flag posts that are duplicates of high-score posts from other subs (opt-in).
- One-click "approve as not a repost" to feed a per-sub whitelist of intentional reposts.
Built With
- css
- devvit
- devvit-triggers
- esbuild
- html
- jaccard-similarity
- node.js
- redis
- typescript


Log in or sign up for Devpost to join the conversation.