MagicEyeRepostCheck — image repost detection as a Devvit library
A headless port of /u/MAGIC_EYE_BOT (downfromthetrees/the_magic_eye, MIT). No menus. No forms. No panels. Three lines of code inside your existing Devvit app's onPostSubmit trigger.
npm i @magic-eye/devvit
Three-line integration
Drop into any existing Devvit app. The SDK never registers a menu item, never renders a block, never opens a form. It returns a verdict; your app decides.
main.tsTypeScript
12 lines
import { Devvit } from '@devvit/public-api';
import { MagicEyeRepostCheck } from '@magic-eye/devvit';
Devvit.addTrigger({
event: 'PostSubmit',
async onEvent(event, ctx) {
const verdict = await MagicEyeRepostCheck.run(event.post, ctx);
if (verdict.isRepost && verdict.confidence > 0.92) {
await ctx.reddit.remove(event.post.id, false);
}
}
});
Devvit.addMenuItem, no Devvit.addCustomPostType, no settings UI we render for you. Your app keeps its identity; we ship a function.Reference
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
post | Post | required | Devvit Post object from PostSubmit trigger. |
ctx | TriggerContext | required | Provides redis, media, reddit. Used internally; not mutated. |
options.threshold | number | optional | Hamming distance ceiling. Default 5 (matches legacy bot). |
options.window | Duration | optional | Look-back window. Default '90d'. |
options.algo | 'dhash'|'phash'|'ahash' | optional | Hashing strategy. Default 'dhash'. |
Returns Promise<RepostVerdict>
RepostVerdicttype
type RepostVerdict = {
isRepost: boolean;
confidence: number; // 0..1
hammingDistance: number | null;
originalPostId: string | null;
originalAuthor: string | null;
algo: 'dhash' | 'phash' | 'ahash';
suggestedAction: 'remove' | 'report' | 'flair' | 'noop';
latencyMs: number;
};
Example response
200 · repost
{
"isRepost": true,
"confidence": 0.97,
"hammingDistance": 2,
"originalPostId": "t3_1abx9k2",
"originalAuthor": "u/og_poster",
"algo": "dhash",
"suggestedAction": "remove",
"latencyMs": 312
}
200 · clean
{
"isRepost": false,
"confidence": 0.04,
"hammingDistance": null,
"originalPostId": null,
"originalAuthor": null,
"algo": "dhash",
"suggestedAction": "noop",
"latencyMs": 198
}
settings.json — config without UI
The SDK reads its config from devvit.yaml → settings. No Devvit.addSettings form is rendered; mods edit the YAML once at install.
devvit.yaml
name: my-mod-app
version: 0.7.2
settings:
magicEye:
enabled: true
threshold: 5
window: "90d"
algo: dhash
onMatch: remove # or: report | flair | noop
telemetry: true # emit time-saved counter to Redis
CLI — hash-DB import / export
The legacy snoowrap bot stored hashes in MongoDB. magic-eye-cli migrates them to Devvit Redis in one command.
terminal
$ magic-eye import legacy_hashes.jsonl --sub r/aww --algo dhash
reading 412,883 records ............................. ok
hashing skipped (pre-computed dhash detected)
writing redis://devvit/r_aww/hashes ................. 412,883 / 412,883
verify sample=200, parity=99.5%, drift=0.5% ........ PASS
done 18.4s · 6.1MB redis · 0 errors
Telemetry — time-saved as data, not UI
When telemetry: true, the SDK increments two Redis counters per match: magic_eye:queue_skipped and magic_eye:seconds_saved (28s per manual repost review, configurable). The host app reads them however it wants.
read counters from anywhere in your app
const saved = await ctx.redis.get('magic_eye:seconds_saved');
// => "11538340" (~3,205 mod-hours saved across 90 days)