Create docs.md
This commit is contained in:
255
docs.md
Normal file
255
docs.md
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
|
||||||
|
# Documentation / Документация
|
||||||
|
|
||||||
|
## EN
|
||||||
|
|
||||||
|
### 1) How the bot works
|
||||||
|
|
||||||
|
1. Poll Mastodon notifications for `mention` type (`/api/v1/notifications`).
|
||||||
|
2. For each mention:
|
||||||
|
- Extract plain text from HTML content
|
||||||
|
- Remove all mentions like `@giffer` / `@giffer@domain` / `@ giffer`
|
||||||
|
- Detect `nsfw` keyword
|
||||||
|
- Parse remaining text into tags (supports quotes and `-tag`)
|
||||||
|
3. Query Furbooru (Philomena API) for GIF images:
|
||||||
|
- SFW query adds: `safe, animated, gif`
|
||||||
|
- NSFW query adds: `animated, gif` (plus `filter_id` if configured)
|
||||||
|
4. Pick a random candidate GIF result.
|
||||||
|
5. Upload media to Mastodon:
|
||||||
|
- Try GIF representations in descending size: `full → large → medium → small → thumb`
|
||||||
|
- If Mastodon rejects GIF with 422 “not supported” / size limits:
|
||||||
|
- Convert GIF → MP4 using ffmpeg
|
||||||
|
- Upload MP4 instead
|
||||||
|
6. Wait for Mastodon media processing to finish (poll `/api/v1/media/:id`):
|
||||||
|
- prevents 422 “processing not finished” when posting the status
|
||||||
|
7. Post reply status with uploaded media:
|
||||||
|
- SFW: normal reply
|
||||||
|
- NSFW: `sensitive=True` and `spoiler_text="NSFW"` (+ configurable visibility)
|
||||||
|
8. Save state:
|
||||||
|
- `last_seen_notif_id`
|
||||||
|
- list of processed `status_id` (to avoid replying twice after restarts)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2) Query syntax
|
||||||
|
|
||||||
|
The bot converts the mention text to Furbooru tags:
|
||||||
|
|
||||||
|
- Spaces become multiple tags:
|
||||||
|
`cute fluffy tail` → `cute, fluffy, tail`
|
||||||
|
- Commas are also supported:
|
||||||
|
`cute, fluffy, tail`
|
||||||
|
- Quoted phrases stay together as one tag:
|
||||||
|
`"rainbow dash"` → `rainbow dash`
|
||||||
|
- Negative tags are supported:
|
||||||
|
`-gore` → `-gore`
|
||||||
|
- `random` / `rnd` are ignored and treated as no user tags (random result).
|
||||||
|
|
||||||
|
NSFW mode:
|
||||||
|
- Any mention containing the word `nsfw` enables NSFW mode and removes that word from the tag list.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3) Furbooru API notes
|
||||||
|
|
||||||
|
Endpoint used:
|
||||||
|
- `GET /api/v1/json/search/images?q=...&per_page=50&page=1`
|
||||||
|
|
||||||
|
The bot filters results:
|
||||||
|
- `format == "gif"`
|
||||||
|
- has usable `representations` URL(s)
|
||||||
|
- chooses randomly among candidates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4) ALT text generation
|
||||||
|
|
||||||
|
ALT text is generated from Furbooru tags:
|
||||||
|
- removes rating/system tags like `safe`, `nsfw`, `animated`, `gif`
|
||||||
|
- keeps up to 20 tags
|
||||||
|
- prefix:
|
||||||
|
- `Animated GIF: ...`
|
||||||
|
- `NSFW animated GIF: ...`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5) Rate limiting & anti-spam
|
||||||
|
|
||||||
|
Two layers:
|
||||||
|
|
||||||
|
1) Per-user cooldown:
|
||||||
|
- If the same user mentions the bot again within `USER_COOLDOWN_SEC`, bot skips.
|
||||||
|
|
||||||
|
2) Global token bucket:
|
||||||
|
- `GLOBAL_RATE_PER_SEC` tokens refill rate
|
||||||
|
- `GLOBAL_BURST` max burst
|
||||||
|
- applies to Furbooru requests
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6) Prevent replying twice (state)
|
||||||
|
|
||||||
|
The bot persists state to `STATE_FILE` (default `giffer_state.json`):
|
||||||
|
- `last_seen_notif_id` (so it won’t re-fetch old notifications after restart)
|
||||||
|
- `processed_status_ids` (cache to skip duplicates even if server repeats notifications)
|
||||||
|
|
||||||
|
If you delete the state file, the bot may reply to old mentions again.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7) Windows reliability notes
|
||||||
|
|
||||||
|
This project includes hardening for Windows:
|
||||||
|
- `socket.setdefaulttimeout(SOCKET_DEFAULT_TIMEOUT)` to prevent rare indefinite hangs
|
||||||
|
- `requests.Session` with retries
|
||||||
|
- explicit connect/read timeouts
|
||||||
|
- Mastodon API calls are wrapped with a watchdog timeout (thread executor)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8) Troubleshooting
|
||||||
|
|
||||||
|
**Bot replies “media processing not finished” / 422**
|
||||||
|
- Increase `MEDIA_PROCESS_MAX_WAIT` (e.g. 90)
|
||||||
|
- Your instance may be slow during peak hours
|
||||||
|
|
||||||
|
**GIF rejected: “1440x1440 GIF files are not supported”**
|
||||||
|
- Expected on some instances
|
||||||
|
- Bot will fallback to smaller GIF representations
|
||||||
|
- Then fallback to MP4 conversion
|
||||||
|
|
||||||
|
**MP4 conversion fails**
|
||||||
|
- Install bundled ffmpeg:
|
||||||
|
- `py -m pip install imageio-ffmpeg`
|
||||||
|
- Or install system ffmpeg and ensure it’s in PATH
|
||||||
|
|
||||||
|
**Bot replies to old mentions after restart**
|
||||||
|
- Check `STATE_FILE` exists and is writable
|
||||||
|
- Don’t delete `giffer_state.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RU
|
||||||
|
|
||||||
|
### 1) Как работает бот
|
||||||
|
|
||||||
|
1. Опрос уведомлений Mastodon типа `mention` (`/api/v1/notifications`).
|
||||||
|
2. Для каждого упоминания:
|
||||||
|
- Достаём текст из HTML
|
||||||
|
- Удаляем любые упоминания (`@giffer`, `@giffer@домен`, `@ giffer`)
|
||||||
|
- Определяем наличие `nsfw`
|
||||||
|
- Парсим оставшееся в теги (есть кавычки и `-тег`)
|
||||||
|
3. Запрос на Furbooru (Philomena API) за GIF:
|
||||||
|
- SFW добавляет: `safe, animated, gif`
|
||||||
|
- NSFW добавляет: `animated, gif` (и `filter_id`, если задан)
|
||||||
|
4. Выбор случайного подходящего результата.
|
||||||
|
5. Загрузка медиа в Mastodon:
|
||||||
|
- Пробуем GIF (full → large → medium → small → thumb)
|
||||||
|
- Если инстанс отклоняет GIF с 422 (“not supported”/лимиты):
|
||||||
|
- Конвертим GIF → MP4 через ffmpeg
|
||||||
|
- Заливаем MP4
|
||||||
|
6. Ждём завершения обработки медиа в Mastodon (`/api/v1/media/:id`):
|
||||||
|
- предотвращает 422 “обработка не окончена” при публикации
|
||||||
|
7. Публикуем ответ:
|
||||||
|
- SFW: обычный ответ
|
||||||
|
- NSFW: `sensitive=True` и `spoiler_text="NSFW"` (+ настраиваемая видимость)
|
||||||
|
8. Сохраняем состояние:
|
||||||
|
- `last_seen_notif_id`
|
||||||
|
- кэш `status_id` (чтобы не отвечать повторно после рестарта)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2) Синтаксис запроса (теги)
|
||||||
|
|
||||||
|
- Пробелы = несколько тегов:
|
||||||
|
`cute fluffy tail` → `cute, fluffy, tail`
|
||||||
|
- Запятые тоже можно:
|
||||||
|
`cute, fluffy, tail`
|
||||||
|
- Кавычки сохраняют фразу как один тег:
|
||||||
|
`"rainbow dash"` → `rainbow dash`
|
||||||
|
- Минус-теги работают:
|
||||||
|
`-gore` → `-gore`
|
||||||
|
- `random` / `rnd` игнорируются (рандомный результат).
|
||||||
|
|
||||||
|
NSFW-режим:
|
||||||
|
- Если в тексте есть слово `nsfw`, включается NSFW и это слово убирается из тегов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3) Про Furbooru API
|
||||||
|
|
||||||
|
Используем:
|
||||||
|
- `GET /api/v1/json/search/images?q=...&per_page=50&page=1`
|
||||||
|
|
||||||
|
Фильтруем результаты:
|
||||||
|
- `format == "gif"`
|
||||||
|
- есть рабочие ссылки `representations`
|
||||||
|
- выбираем случайно
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4) ALT-текст
|
||||||
|
|
||||||
|
ALT строится из тегов Furbooru:
|
||||||
|
- выкидываются системные/рейтинг-теги `safe`, `nsfw`, `animated`, `gif`
|
||||||
|
- берём до 20 тегов
|
||||||
|
- префикс:
|
||||||
|
- `Animated GIF: ...`
|
||||||
|
- `NSFW animated GIF: ...`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5) Антиспам / Rate-limit
|
||||||
|
|
||||||
|
1) Кулдаун на пользователя (`USER_COOLDOWN_SEC`)
|
||||||
|
2) Глобальный token-bucket (`GLOBAL_RATE_PER_SEC`, `GLOBAL_BURST`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6) Защита от повторных ответов (state)
|
||||||
|
|
||||||
|
Файл `STATE_FILE` (по умолчанию `giffer_state.json`) хранит:
|
||||||
|
- `last_seen_notif_id`
|
||||||
|
- `processed_status_ids`
|
||||||
|
|
||||||
|
Если удалить state-файл — бот может снова отвечать на старые упоминания.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7) Особенности Windows
|
||||||
|
|
||||||
|
Усиленная защита от “вечных зависаний”:
|
||||||
|
- `socket.setdefaulttimeout(...)`
|
||||||
|
- `requests.Session` с retry
|
||||||
|
- явные таймауты connect/read
|
||||||
|
- watchdog-таймаут вокруг вызовов Mastodon API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8) Типовые проблемы
|
||||||
|
|
||||||
|
**422 “обработка файлов не окончена”**
|
||||||
|
- увеличь `MEDIA_PROCESS_MAX_WAIT` (например до 90)
|
||||||
|
- инстанс может быть медленным
|
||||||
|
|
||||||
|
**“1440x1440 GIF files are not supported”**
|
||||||
|
- ожидаемо для некоторых инстансов
|
||||||
|
- бот уйдёт на меньшие версии GIF, потом на MP4
|
||||||
|
|
||||||
|
**Не конвертится в MP4**
|
||||||
|
- поставь `imageio-ffmpeg`:
|
||||||
|
- `py -m pip install imageio-ffmpeg`
|
||||||
|
- или установи ffmpeg в систему и добавь в PATH
|
||||||
|
|
||||||
|
**После рестарта отвечает на старые упоминания**
|
||||||
|
- проверь, что `giffer_state.json` не удаляется и доступен на запись
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9) Рекомендуемая структура репо
|
||||||
|
|
||||||
|
- `giffer_bot.py`
|
||||||
|
- `README.md`
|
||||||
|
- `docs.md`
|
||||||
|
- `.gitignore` (исключить `config.env`, `giffer_state.json`, `*.log`)
|
||||||
|
- `LICENSE`
|
||||||
Reference in New Issue
Block a user