0732f2c34b
The bot-side half of TODO #4. Nothing federates yet; this establishes what would be eligible to, and proves the boundary before there is a hub that could leak across it. - Schema v5 adds visibility, defaulting to private. Upgrading an existing library therefore opts nothing into sharing, which a test on the legacy upgrade path pins down. - list_syncable() is the only function that will ever feed a hub, and it requires both public and approved. Tests force rows into states set_visibility refuses, so the filter is what protects the boundary rather than the caller being careful. - set_visibility refuses anything not approved: media still in the moderation queue cannot be marked shared. - /share lists memes with their current state and asks before making one public, since un-sharing stops future copies but cannot recall those already taken. The wording describes what sharing will mean once a hub exists, because by then warning is too late. - /list shows the same markers. Vault posts have no path into the sync set at all; being in a separate table is what guarantees it, and there is a test saying so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
137 lines
6.6 KiB
Markdown
137 lines
6.6 KiB
Markdown
# Memes Bot
|
|
|
|
A personal Telegram inline bot to store and search media (photos, GIFs, videos, voice, music, stickers, files) by keywords.
|
|
|
|
[فارسی](README.fa.md)
|
|
|
|
---
|
|
|
|
### Requirements
|
|
|
|
- A server with [Docker](https://docs.docker.com/engine/install/) installed
|
|
- A Telegram bot token from [@BotFather](https://t.me/BotFather)
|
|
|
|
### Setup
|
|
|
|
**1. Create the bot in BotFather**
|
|
- `/newbot` → get your token
|
|
- `/setinline` → enable inline mode, set a placeholder (e.g. `Search memes...`)
|
|
|
|
**2. Find your Telegram user ID**
|
|
|
|
You need your numeric Telegram user ID to whitelist yourself.
|
|
|
|
- **Easiest:** forward any message to [@userinfobot](https://t.me/userinfobot) — it replies with your ID
|
|
- **Alternative:** open Telegram Web, click your profile, the number in the URL is your ID
|
|
- **For multiple users:** repeat for each person and separate IDs with commas
|
|
|
|
**3. Clone and configure**
|
|
|
|
```bash
|
|
git clone https://git.goyban.com/goyban/memes_bot
|
|
cd memes_bot
|
|
cp .env.example .env
|
|
```
|
|
|
|
Edit `.env`:
|
|
|
|
```env
|
|
BOT_TOKEN=your_telegram_bot_token_here
|
|
ALLOWED_USERS=123456789,987654321
|
|
```
|
|
|
|
**4. Run**
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
The database will be saved in `./bot_db/media.db`.
|
|
|
|
### Usage
|
|
|
|
**Adding media** — send the bot a photo, GIF, video, voice message, music file, sticker, or any document, then type keywords when prompted (comma-separated or one per message), then `/done`.
|
|
|
|
Round video messages are stored as regular videos: Telegram has no inline result type for video notes, so keeping them as video is what makes them searchable.
|
|
|
|
**Searching inline** — in any chat type `@yourbotusername keyword` to search and send.
|
|
|
|
Telegram suggests these commands as you type `/`, with a short description beside each suggestion.
|
|
|
|
**Commands**
|
|
|
|
| Command | Description |
|
|
|---|---|
|
|
| `/start` | Start the bot and show usage instructions |
|
|
| `/menu` | Show the available commands |
|
|
| `/list` | Show all saved media |
|
|
| `/vault [search]` | Retrieve a whole post you saved |
|
|
| `/review` | Review pending submissions one by one (admin only) |
|
|
| `/edit [search]` | Select a meme and edit its keywords (admin only) |
|
|
| `/share [search]` | Mark a meme as shared or private (admin only) |
|
|
| `/delete <id>` | Remove an entry (get ID from `/list`; admin only) |
|
|
| `/cancel` | Cancel current add operation |
|
|
|
|
### The vault — saving whole posts
|
|
|
|
Send the bot media directly and it behaves as it always has: it extracts the media and asks for keywords.
|
|
|
|
**Forward** a post to it instead and it asks what you want:
|
|
|
|
- **Save whole post** — keeps the post intact in your vault, caption and all. An album stays an album.
|
|
- **Extract media** — the existing behaviour, adding the media to your searchable library.
|
|
|
|
Retrieve saved posts with `/vault [search]`, which matches your keywords and the original caption. Each result has a button to forward it back to you, and one to remove it.
|
|
|
|
> **Vault posts only come back inside this bot.** Telegram's inline mode cannot forward an existing message, so `@yourbotusername keyword` does not reach the vault. Retrieve the post in the bot chat, then forward it wherever you want.
|
|
|
|
The vault is per-user and private: it is stored separately from the meme library, every lookup is scoped to its owner, and nothing in it is ever moderated or shared.
|
|
|
|
Saving stores a pointer to the message in your chat with the bot rather than a copy of the media. If you clear your chat history with the bot, saved posts can no longer be retrieved — the bot tells you when that happens so you can forward them again.
|
|
|
|
### Making the bot public
|
|
|
|
Set `PUBLIC_MODE=true` to open the bot to anyone who meets a membership requirement, instead of the `ALLOWED_USERS` list:
|
|
|
|
```env
|
|
PUBLIC_MODE=true
|
|
ADMIN_USERS=123456789
|
|
REQUIRED_CHATS=@mychannel,@mygroup
|
|
REQUIRE_MODE=all
|
|
```
|
|
|
|
`REQUIRED_CHATS` accepts `@username` or numeric ids. **The bot must be an admin in each one**, otherwise it cannot check membership and everyone is refused. The bot verifies this at startup and logs an error naming any chat it cannot read. `REQUIRE_MODE=all` demands every chat; `any` accepts one. With `REQUIRED_CHATS` empty the bot is open to everyone.
|
|
|
|
Users who don't qualify get a message listing what to join. In inline search they see a tappable bar that opens the bot and explains the same thing, rather than empty results with no reason given.
|
|
|
|
> **Set `ADMIN_USERS` before turning this on.** With no `ADMIN_USERS`, a private bot treats its users as admins — convenient for a one-person bot, but in public mode that would mean everyone. Public mode therefore grants admin to nobody until `ADMIN_USERS` is set, and logs an error at startup if you forget.
|
|
|
|
Membership answers are cached for five minutes, since inline search would otherwise query Telegram on every keystroke. Refusals are cached for only thirty seconds, so a user who has just joined gets in almost immediately.
|
|
|
|
### Accepting submissions
|
|
|
|
Set `ACCEPT_SUBMISSIONS=true` in `.env` to let non-admin users send media too. Their submissions are stored but stay invisible — they don't appear in inline search or `/list` until a moderator approves them. Admins' own media is still saved directly.
|
|
|
|
Run `/review` to work through the queue. Each item is shown with its keywords and four buttons: approve, reject, fix keywords, or stop.
|
|
|
|
The queue keeps no cursor. `/review` always fetches the oldest pending item, so you can stop after three and come back tomorrow, and several moderators can review at the same time without stepping on each other — if two open the same item, the first decision wins and the second is told it was already handled.
|
|
|
|
> **Leave this off unless you mean it.** With `ALLOWED_USERS` empty, everyone is an allowed user, so turning submissions on would accept media from anyone who finds the bot.
|
|
|
|
### Sharing (groundwork)
|
|
|
|
Every meme is **private** by default. `/share [search]` lists your memes with 🔒 or 🌐 and lets you flip one. `/list` shows the same markers.
|
|
|
|
> **Nothing is shared yet.** This only records your intent. Federation — where independently run bots pool their shared memes — is not built, and no meme leaves this bot until it is. The wording in the confirmation describes what sharing *will* mean, because once a hub exists it is too late to start warning.
|
|
|
|
Two rules are enforced in the one query that will ever feed a hub: only **approved** and **public** media is eligible. Media awaiting moderation cannot be marked shared at all, and vault posts are in a separate table with no path there whatsoever.
|
|
|
|
Un-sharing stops future copies but cannot recall copies already taken, which is why the confirmation asks before making something public rather than after.
|
|
|
|
### Moving to another server
|
|
|
|
The only files you need to transfer are:
|
|
|
|
- `.env`
|
|
- `bot_db/media.db`
|