Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions devvit/DEVELOPING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# autowikimod (RedditModLog on Devvit Web)

User-facing overview: [README.md](README.md). This file is for developers.

Reddit Developer Platform port of [`../modlog_wiki_publisher.py`](../modlog_wiki_publisher.py).
Each install reads its subreddit's moderation log every 20 minutes and writes it
to a wiki page as daily markdown tables with a prefilled modmail inquiry link per
row. Reddit hosts and authenticates it: no bot password, no daemon, no SQLite.

## Layout

| Path | Purpose |
| --- | --- |
| `devvit.json` | App manifest: server bundle, Redis + Reddit permissions, cron task, menu items, settings |
| `src/server/index.ts` | Hono server: `/internal/cron/*`, `/internal/menu/*`, `/internal/settings/*` |
| `src/server/reddit.ts` | Mod log and wiki calls through `reddit` from `@devvit/web/server` |
| `src/core/record.ts` | Mod-log entry to stored row (Python `store_processed_action`) |
| `src/core/render.ts` | Page renderer (Python `build_wiki_content`), FR-7 merging, FR-8 approval gating |
| `src/core/store.ts` | Redis layout (hashes and sorted sets only) |
| `src/core/publish.ts` | One cycle: ingest, render, hash-skip, size guard, write, prune |
| `src/core/settings.ts` | Setting validators and config loader |
| `test/` | Vitest + `@devvit/test`; `test/parity/` holds the Python golden comparison |

## Behaviour

- Cron `*/20 * * * *` fetches up to 500 mod-log entries, stores new ones, rebuilds the page from storage and writes it only when the content hash (minus the timestamp line) changed.
- Rows match the Python output: human moderators show as `HumanModerator`, AutoModerator removals show as `filter-<action>`, links point only at posts and comments, emails in reasons become `[EMAIL]`, pipes become spaces.
- FR-7: a removal and its `addremovalreason` on the same target render as one row carrying the reason.
- FR-8: an approval is shown only when it reverses a Reddit or AutoModerator removal of the same post or comment. The latest such removal per target is kept in the `prior` Redis hash.
- Pages are trimmed oldest day first at 90% of Reddit's 512 KiB limit.

## Settings (per subreddit)

| Setting | Default | Notes |
| --- | --- | --- |
| Wiki page name | `modlog-devvit` | `config/*`, `automoderator`, `usernotes`, `toolbox` are refused |
| Days shown on the wiki page | 90 | 1-365, capped at days kept |
| Days of history kept | 90 | 1-365 |
| Actions shown on the wiki page | removals, spam, `addremovalreason`, approvals | Python `DEFAULT_WIKI_ACTIONS` |
| Anonymize moderators | on | Cannot be turned off, as in the Python app |
| Ignored moderators | empty | Comma-separated; their actions are never stored |

## Menu (subreddit menu, moderators only)

- **Modlog: Publish now**: same as one cron run.
- **Modlog: Force rebuild**: rewrite the page from storage without fetching (Python `--force-wiki`).
- **Modlog: Test connection**: checks settings, mod-log read, wiki page and storage, shown in a toast.

## Develop

```bash
npm ci
npm run type-check
npm test
npm run build
```

Regenerate the Python golden after changing the fixture or the Python renderer:

```bash
uv run --no-project --python 3.13 --with praw==8.0.3 \
python -I test/parity/python_render.py test/parity/fixture.json .. > test/parity/golden.md
```
73 changes: 73 additions & 0 deletions devvit/PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Privacy Policy: autowikimod

Effective 2026-11-03. This is a project policy, not legal advice.

autowikimod is not an official Reddit product. It runs on Reddit's Developer Platform (Devvit). This policy covers only what the app itself collects and stores. Reddit's own [Privacy Policy](https://www.reddit.com/policies/privacy-policy) and Developer Terms govern the platform. If this policy conflicts with Reddit's terms, Reddit's terms control.

## Summary

- The app copies mod-log entries to a wiki page in the subreddit that installed it.
- It keeps those entries in Reddit-hosted storage for that subreddit only, for 90 days by default.
- It sends nothing to any outside server. It has no analytics, ads, payments, or AI use.
- Known gap: it does not yet remove an entry when the post, comment, or account is deleted. See "Deleted content".

## What the app handles

For each mod-log entry it reads, the app stores:

| Data | Source | Notes |
| --- | --- | --- |
| Username of the content author | Mod log target | Shown on the wiki page as "Post by u/name" or "Comment by u/name". Stored as `null` when the log has none. |
| Link to the post or comment | Mod log target | Posts and comments only. Profile links are never stored. |
| Action type | Mod log | For example `removecomment`, `banuser`. |
| Moderator label | Mod log | Human moderators are stored as `HumanModerator`. The real name is not stored. `AutoModerator` and `Reddit` are stored by name. |
| Reason text | Mod log | Written by moderators. Email addresses become `[EMAIL]`. May contain whatever a moderator typed. |
| Time of the action | Mod log | UTC. |
| Entry ID | Mod log | Used to avoid storing an entry twice. |
| Run status | App | Time and result of the last run, last error text, a hash of the last page written, and a short run lock. |

Entries by moderators on your "Ignored moderators" list are not stored.

The app does not read post or comment bodies, titles, private messages, IP addresses, or email addresses of users. It does not profile, track, or sell data about anyone.

## Where it goes

1. App storage. Reddit's Devvit Redis, separate for each installation. Only the app for that subreddit can read it. The developers have no dashboard or export of it.
2. The wiki page. The app writes the log to the wiki page named in settings. Visibility of that page is set by the subreddit's moderators in the wiki settings. Anyone allowed to read the page can see the usernames and reasons on it. Each modmail link on the page contains the author's username, the action type, a short content ID, and the content link.
3. Wiki revision history. Reddit keeps earlier versions of a wiki page. Older rows that are no longer on the current page can remain in the revision history. The app cannot edit or delete revisions. Moderators can manage them in Reddit's wiki tools, and Reddit's own policies apply to them.

The app calls no service other than Reddit's.

## Retention

- Stored entries are deleted when they are older than "Days of history kept". Default 90 days, allowed 1 to 365. The check runs after every publish cycle, which is every 20 minutes.
- Rows leave the wiki page when they pass "Days shown" (default 90, never more than the retention days) or when the page is trimmed for size.
- Run status values are overwritten on each run and are not deleted on a schedule.
- The wiki page itself is not deleted by the app.

## Deleted content

The app does not yet delete a stored entry when its post, comment, or author account is deleted on Reddit. The author's username, the content link, and the reason text stay in app storage. They also stay on the wiki page. This lasts until the retention period ends or the page is rewritten without them. Reddit's Devvit Rules require removal on deletion. This is a known gap. Use a short retention period until it is fixed.

## Uninstall and removal

- Uninstalling the app stops all collection.
- Whether Reddit deletes the stored entries at uninstall is set by Reddit's platform, not by this app. [unverified: not confirmed in Reddit's documentation as of 2026-11-03.] To remove stored entries before or after uninstalling, lower "Days of history kept" to 1 and run **Modlog: Publish now** so storage is pruned, or ask us to help.
- The wiki page stays after uninstall. Moderators must delete or blank it, and may need to review its revision history.
- To ask for removal of a specific user's data, open an issue at <https://github.com/baker-scripts/RedditModLog/issues> (do not include private details) or ask the subreddit's moderators, who control the install.

## Who is responsible

The moderators who install the app choose its settings and what the wiki page shows. They decide who can read it. This project provides the software only.

## Children

Reddit requires users to be at least 13. The app handles only data Reddit already holds about users of the installing subreddit and does not target anyone.

## Changes

Changes are made in this file and recorded in the repository history at <https://github.com/baker-scripts/RedditModLog>.

## Contact

<https://github.com/baker-scripts/RedditModLog/issues>
133 changes: 76 additions & 57 deletions devvit/README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,80 @@
# autowikimod (RedditModLog on Devvit Web)
# autowikimod

Reddit Developer Platform port of [`../modlog_wiki_publisher.py`](../modlog_wiki_publisher.py).
Each install reads its subreddit's moderation log every 20 minutes and writes it
to a wiki page as daily markdown tables with a prefilled modmail inquiry link per
row. Reddit hosts and authenticates it: no bot password, no daemon, no SQLite.
Not an official Reddit product. Not affiliated with or endorsed by Reddit, Inc.

## Layout
autowikimod copies your subreddit's moderation log to a wiki page, one table per day. Each row has a "Contact Mods" link that opens a prefilled modmail, so a user can ask about a removal without retyping the details. It is for moderators who want a public or mod-only audit trail that Reddit's own mod log page does not give them. It is free and open source and runs on Reddit's servers (Devvit). It uses no outside server, account, or payment.

| Path | Purpose |
| --- | --- |
| `devvit.json` | App manifest: server bundle, Redis + Reddit permissions, cron task, menu items, settings |
| `src/server/index.ts` | Hono server: `/internal/cron/*`, `/internal/menu/*`, `/internal/settings/*` |
| `src/server/reddit.ts` | Mod log and wiki calls through `reddit` from `@devvit/web/server` |
| `src/core/record.ts` | Mod-log entry to stored row (Python `store_processed_action`) |
| `src/core/render.ts` | Page renderer (Python `build_wiki_content`), FR-7 merging, FR-8 approval gating |
| `src/core/store.ts` | Redis layout (hashes and sorted sets only) |
| `src/core/publish.ts` | One cycle: ingest, render, hash-skip, size guard, write, prune |
| `src/core/settings.ts` | Setting validators and config loader |
| `test/` | Vitest + `@devvit/test`; `test/parity/` holds the Python golden comparison |

## Behaviour

- Cron `*/20 * * * *` fetches up to 500 mod-log entries, stores new ones, rebuilds the page from storage and writes it only when the content hash (minus the timestamp line) changed.
- Rows match the Python output: human moderators show as `HumanModerator`, AutoModerator removals show as `filter-<action>`, links point only at posts and comments, emails in reasons become `[EMAIL]`, pipes become spaces.
- FR-7: a removal and its `addremovalreason` on the same target render as one row carrying the reason.
- FR-8: an approval is shown only when it reverses a Reddit or AutoModerator removal of the same post or comment. The latest such removal per target is kept in the `prior` Redis hash.
- Pages are trimmed oldest day first at 90% of Reddit's 512 KiB limit.

## Settings (per subreddit)

| Setting | Default | Notes |
Before installing, note the following.

- The wiki page shows the author's username for each action ("Post by u/name") and the moderator's reason text. Set the wiki page's permission to what you want visible, in the subreddit's wiki settings, before the first run.
- Human moderators are always shown as `HumanModerator`. This cannot be turned off. `AutoModerator` and `Reddit` are shown by name.
- The app stores rows for up to the retention period you set (default 90 days). A deleted post, comment, or account is not yet scrubbed from the app's storage or the wiki page. See [Deleted content](#deleted-content). Until that is fixed, use the shortest retention that works for you.
- The app writes to one wiki page that you name. It refuses `config/*`, `automoderator`, `usernotes`, and `toolbox`. Point it at a page that holds nothing else, because each run overwrites the page.

## What it does

- Every 20 minutes it reads up to 500 recent mod-log entries, stores the new ones, and rebuilds the wiki page. It writes only when the content changed.
- A removal and its removal reason on the same item show as one row.
- An approval shows only when it reverses a removal by Reddit or AutoModerator.
- Links point at the post or comment, never at a user profile. Email addresses in reasons become `[EMAIL]`. Pipe characters become spaces so a reason cannot break the table.
- Pages stop adding older days at 90% of Reddit's 512 KiB wiki limit and say how many days were trimmed.

## Setup

1. Install the app from the Reddit app directory, on your subreddit.
2. Open the app's subreddit settings and set each option below. Every setting is per subreddit.
3. Set the wiki page's visibility in your subreddit's wiki settings.
4. Open the subreddit menu and run **Modlog: Test connection**, then **Modlog: Publish now**.

| Setting | Default | What it does |
| --- | --- | --- |
| Wiki page name | `modlog-devvit` | `config/*`, `automoderator`, `usernotes`, `toolbox` are refused |
| Days shown on the wiki page | 90 | 1-365, capped at days kept |
| Days of history kept | 90 | 1-365 |
| Actions shown on the wiki page | removals, spam, `addremovalreason`, approvals | Python `DEFAULT_WIKI_ACTIONS` |
| Anonymize moderators | on | Cannot be turned off, as in the Python app |
| Ignored moderators | empty | Comma-separated; their actions are never stored |

## Menu (subreddit menu, moderators only)

- **Modlog: Publish now**: same as one cron run.
- **Modlog: Force rebuild**: rewrite the page from storage without fetching (Python `--force-wiki`).
- **Modlog: Test connection**: checks settings, mod-log read, wiki page and storage, shown in a toast.

## Develop

```bash
npm ci
npm run type-check
npm test
npm run build
```

Regenerate the Python golden after changing the fixture or the Python renderer:

```bash
uv run --no-project --python 3.13 --with praw==8.0.3 \
python -I test/parity/python_render.py test/parity/fixture.json .. > test/parity/golden.md
```
| Wiki page name | `modlog-devvit` | Page the log is written to. Lowercase letters, digits, `_`, `-`, `/`. Up to 128 characters. |
| Days shown on the wiki page | 90 | 1 to 365. Never more than the retention days. |
| Days of history kept | 90 | 1 to 365. Older entries are deleted from app storage each run. |
| Actions shown on the wiki page | removals, spam removals, removal reasons, approvals | Pick from 43 mod-log action types, such as `banuser`, `lock`, or `addnote`. At least one is required. |
| Anonymize moderators | on | Human moderators show as `HumanModerator`. Cannot be turned off. |
| Ignored moderators | empty | Comma-separated usernames. Their actions are never stored. |

Entries older than the retention days are skipped when read. The app only keeps and shows actions of the types you select, but it stores every non-ignored entry it reads.

## Moderator menu

The subreddit menu shows these items to moderators only.

- **Modlog: Publish now** runs one cycle immediately.
- **Modlog: Force rebuild** rewrites the page from stored entries without reading the log.
- **Modlog: Test connection** checks settings, mod-log access, the wiki page, and storage, then shows the result as a toast.

If a cycle is already running, the menu tells you to try again in a minute.

## Permissions

| Permission | Why |
| --- | --- |
| Reddit API (`reddit`) | Read the moderation log and read and write the one wiki page you name. |
| Redis (`redis`) | Keep the stored rows between runs, in Reddit-hosted storage for your install only. |

The app does not use HTTP fetch, payments, or any LLM. It sends no data off Reddit.

## Deleted content

The app does not yet remove a stored row when its post, comment, or account is deleted. The author's username, the post or comment link, and the reason text stay in app storage and on the wiki page until the retention period ends. Reddit's Devvit Rules require removal on deletion. This is a known gap. If you need an entry removed sooner, lower "Days of history kept" and run **Modlog: Force rebuild**, or edit the wiki page, or contact us.

## Privacy and terms

- [Privacy Policy](PRIVACY.md)
- [Terms of Use](TERMS.md)

## Support

Report problems or ask for data removal at <https://github.com/baker-scripts/RedditModLog/issues>. Do not post private information there. For a removal request that must stay private, say so in the issue and ask for a private channel.

To report an app that breaks Reddit's rules, use Reddit's own report flow.

## Changelog

- 0.0.0: first Devvit Web version. Cron publish, three menu items, six settings.

## License

[GPL-3.0](../LICENSE). Source: <https://github.com/baker-scripts/RedditModLog>. Developer documentation: [DEVELOPING.md](DEVELOPING.md).
50 changes: 50 additions & 0 deletions devvit/TERMS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Terms of Use: autowikimod

Effective 2026-11-03. This is a project document, not legal advice.

## 1. What this covers

autowikimod ("the app") copies a subreddit's moderation log to a wiki page. These terms apply to moderators who install and use it. By installing or using the app, you agree to them. If you do not agree, uninstall it. The app is not an official Reddit product and is not affiliated with or endorsed by Reddit, Inc.

## 2. Reddit's terms control

You must follow Reddit's User Agreement, Developer Terms, Devvit Rules, and Moderator Code of Conduct. If these terms conflict with Reddit's terms, Reddit's terms control.

## 3. The source code is separate

The app's source code is licensed under the GNU General Public License v3.0 (see [LICENSE](../LICENSE)). These terms add no restriction on the code. They do not limit your right to copy, modify, or redistribute it. They apply to using the hosted app on Reddit, not to the code. As GPL-3.0 sections 7 and 10 require, any term here that would add a further restriction on the code does not apply to the code.

## 4. Your responsibilities as a moderator

- You choose the settings, the wiki page, and who can read it.
- You are responsible for what the page shows about users and for your community's rules and local law.
- Point the app at a page that holds nothing else, because each run overwrites it.
- Do not use the app to harass, track, or expose users.

## 5. Privacy

See the [Privacy Policy](PRIVACY.md). It includes a known gap in how deleted content is handled.

## 6. No warranty

The app is provided "as is", without warranty of any kind, express or implied, including merchantability, fitness for a particular purpose, and non-infringement. This matches sections 15 and 16 of the GPL-3.0. As section 17 says, a court that cannot apply this disclaimer in full applies the law closest to an absolute waiver. You use it at your own risk. The log can be incomplete, late, or wrong, and is not a legal record.

## 7. Limit of liability

To the extent the law allows, the authors and contributors are not liable for any damages from using the app or being unable to use it. This includes lost data, an overwritten wiki page, and content shown to users. It covers direct, indirect, incidental, special, and consequential damages. This matches GPL-3.0 section 16. Nothing here limits liability that the law does not let you limit.

## 8. Availability, stopping, and changes

You may stop using the app at any time by uninstalling it. The app may stop working, change, or be removed, including by Reddit. The authors may update these terms in this repository. Continued use after a change means you accept it.

## 9. Trademarks

Reddit and the Reddit logo are trademarks of Reddit, Inc. The app uses neither.

## 10. Support

<https://github.com/baker-scripts/RedditModLog/issues>

## 11. Governing law

[needs a lawyer: no governing law or venue is set. Add one only with advice.]
Loading