# Reaction Stickers Your character. Ten reactions. One JPG preview. This is the existing `make-reaction-stickers` workflow packaged for reuse in your own chat app, with a small static demo. It uses the host app’s image-generation tool and your account’s access and limits. The package is free to download; it does not supply a model or promise free/unlimited generation. ## Try it without installing 1. Open the demo and select your own reference or the sample robot. 2. Keep the ten everyday reactions, or enter ten of your own. Labels are optional. 3. Choose a JPG preview or a Telegram sticker set. The Telegram choice can carry an optional pack title and explains the required review and publisher setup. 4. Copy the prompt and open ChatGPT. 5. Attach your own character reference or the downloaded robot reference sheet, paste the prompt, and send. 6. Review the generated JPG and ask for specific repairs if needed. For a Telegram set, the chat then prepares individual PNGs and the exact pack for your review before publication. The website only prepares text. It does not send the prompt or a reference automatically, call a model, collect uploads, use analytics, or store entered text. The Telegram choice does not publish from the page: it asks your chat to follow the skill's review and upload workflow if available. Generation happens after you send the prompt in your chat app, under that app’s data handling and usage limits. A browser extension or browser’s own form restoration remains outside this page’s control. ## Install the skill in ChatGPT Download `make-reaction-stickers.zip` from the demo. 1. In ChatGPT, open **Plugins → Skills → Create → Upload from your computer**. 2. Select the downloaded skill and complete the scan/install flow. If the picker requires an individual skill file, extract the ZIP and select `make-reaction-stickers/SKILL.md`; the demo also offers the raw file. 3. Start a new chat, attach a character reference, and send: > Use make-reaction-stickers. Standard grouping. [OpenAI’s skill instructions](https://help.openai.com/en/articles/20001066-skills-in-chatgpt), checked September 27, 2026, describe skills as available to eligible Business, Enterprise, Healthcare, and Edu accounts, subject to workspace settings and product availability. Your surface may differ. If Skills or Upload is unavailable, use the demo’s standalone prompt instead. This release has local package validation; installation in a recipient’s ChatGPT account has not been tested. ## Install in Codex 1. Extract the skill ZIP into `$CODEX_HOME/skills`, or `~/.codex/skills` with the default configuration. The result should be `skills/make-reaction-stickers/SKILL.md` with `agents/openai.yaml` alongside it in its subfolder. Preserve any existing version before replacing it. 2. Start a new chat with image generation available and attach your reference. 3. Send: **Use $make-reaction-stickers. Standard grouping.** ## Install the plugin package The plugin ZIP contains the same skill under `reaction-stickers/skills/` and the existing `.codex-plugin/plugin.json` compatibility manifest. The local Telegram upload script is bundled in the skill. No server, app connection, hook, or credential is bundled. Telegram publishing can use a separate authenticated Bunch connection or your own bot in a local Python environment. For local desktop Work or Codex, extract the ZIP. In a local chat that can access the extracted folder, ask the built-in plugin-creator: > Register this existing Reaction Stickers plugin folder in my personal marketplace so I can install it. Preserve its skill and manifest content, and use the folder I attached. Refresh or restart the app, select your local source in the Plugins Directory, install Reaction Stickers, and test in a new chat. See [OpenAI’s local plugin packaging guide](https://developers.openai.com/plugins/build/plugins). A ZIP download does not itself register a marketplace or install a plugin. No universal public-directory listing is claimed. Where direct skill upload is available, it is the simpler route. ## Existing behavior The ten-reaction preview behavior is unchanged from v0.1.0; v0.3.0 adds an optional Telegram upload through your own bot, alongside the connected Bunch route. Defaults remain: | Row | Left | Right | | --- | --- | --- | | 1 | HELLO | LOVE | | 2 | LAUGH | THANKS | | 3 | YES | NO | | 4 | SAD | ANGRY | | 5 | THINKING | SLEEPY | One generation produces a 2-column × 5-row portrait preview; the agent checks and repairs it, then delivers an actual JPEG. The skill respects custom reactions, language, labels, style, layout, and model choices. The deliberately small demo exposes ten custom reactions, optional labels, and character notes; use the skill directly for other layouts/counts. A JPG preview is not ten transparent files or an installed Telegram pack. On an explicit Telegram-set request, the skill guides sticker-ready PNG creation and can publish the reviewed pack through connected Bunch tools or the included local script. One request can begin the process after setup; the static demo itself cannot upload. Preview generation needs no Telegram account. ## Upload a Telegram set with your own bot This route does not need Bunch. Create a bot with [Telegram's BotFather](https://core.telegram.org/bots/features#botfather), then message that bot from the Telegram account that should own the set. Keep the token in your local environment, never in chat or a saved manifest. Install Pillow locally with `python3 -m pip install Pillow`. The script needs local network access to Telegram only when showing user IDs or publishing. Set `TELEGRAM_BOT_TOKEN` in your local environment. Run `python3 skills/make-reaction-stickers/scripts/upload_telegram_set.py --show-chat-ids` to see numeric IDs of recent users who messaged your bot. Choose your own ID and set `TELEGRAM_USER_ID` locally. If the bot uses a webhook or Telegram returns no recent message, obtain your own ID through your existing bot setup instead. Do not send either value to us. Put approved transparent PNGs and `pack.json` in one local folder. Use relative file paths: ```json { "title": "Robot Reactions", "slug": "robot_reactions", "stickers": [ {"path": "hello.png", "emoji_list": ["👋"]}, {"path": "love.png", "emoji_list": ["❤️"]} ] } ``` Include the rest of the approved stickers in the intended order. Each file needs transparency, a 512px side, no side larger than 512px, and size at most 512 KiB. The script checks all files before any network request. It never extracts stickers automatically from the JPG preview. ```sh python3 skills/make-reaction-stickers/scripts/upload_telegram_set.py --manifest pack.json python3 skills/make-reaction-stickers/scripts/upload_telegram_set.py --manifest pack.json --publish ``` The first command is an offline review. The second checks the bot identity and unused set name, uploads the PNGs in one Telegram `createNewStickerSet` request, then reads the created set back. Telegram requires the final set name to end in `_by_`; the script appends that suffix. Publication makes the sticker set shareable. If a request ends uncertainly, inspect the exact set before retrying; do not change the slug just to force another upload. The script creates new static sets only. [Telegram's Bot API](https://core.telegram.org/bots/api#createnewstickerset) documents the creation call. ## Source boundary and extraction The inspected Bunch project separates three concerns: - `src/domain/sticker-pack.ts` holds private direction-board data and CSV formatting. - `src/app/stickers/page.tsx` builds a ChatGPT handoff using that board and canonical private references. Its default intents are yes, no, applause, thanks, sorry, laugh, love, confused, congrats, and bye. It asks for pose approval before final art. These defaults are distinct from the standalone preview skill. - `src/server/telegram-sticker-mcp.ts`, `telegram-stickers.ts`, and `telegram-sticker-skill.ts` publish already-rendered PNGs through authenticated Bunch access. They do not supply a standalone image-generation model. The reusable generation workflow was already extracted into `make-reaction-stickers`. This distribution builds on that existing extraction. It isolates the demo’s prompt composition in `demo/prompt.js`. The build derives the default reaction order and base prompt from `SKILL.md`, so the demo does not maintain a second copy of those defaults. No Bunch repository changes, private records, reference images, or credentials are included. The sample robot was newly generated for this demo, then used as the visual source for a matching front/side/back reference sheet. Both are included in `demo/assets/`; the reference has its own download button. Neither uses private character assets. The sample is not a live test of generation in a recipient account. The displayed sample is fixed; changing a prompt does not regenerate it. ## Run or host the demo Python 3.9+ builds the downloads. Node.js 18+ runs the focused JavaScript tests. Neither is a production server dependency. From this project folder: ```sh python3 scripts/build.py python3 -m http.server 8765 --bind 127.0.0.1 --directory demo ``` Open `http://127.0.0.1:8765`. You can also open `demo/index.html` directly; if clipboard access is blocked, the copy button selects the text for manual copying. To publish, upload the entire `demo/` folder to any static host. Preserve its relative paths, including `downloads/`. No build framework, database, API keys, image-generation backend, environment variables, or paid inference account is needed for the website. Static-host bandwidth/storage terms still apply. The ArcadeProfile edition is linked from https://www.thearcades.me/projects/reaction-stickers, with its demo at https://www.thearcades.me/reaction-stickers. For your own copy, choose any static hosting destination. For production hosts supporting headers, also set `Content-Security-Policy: frame-ancestors 'none'` and `X-Content-Type-Options: nosniff`. The page itself ships a restrictive CSP with `connect-src 'none'`. ## Verify or update ```sh python3 scripts/build.py python3 -m unittest discover -s tests -p 'test_*.py' node --test tests/prompt.test.cjs ``` The tests check ZIP contents and source parity, absence of unexpected bundled files, deterministic builds, download/link resolution, shared defaults, custom reaction validation, label choices, and literal user text. After changing the skill, rebuild downloads and repeat the checks. Its table/scaffold extraction deliberately fails if the source format changes unexpectedly. Use the built-in skill-creator and plugin-creator validators when available. For the UI, also inspect a rendered desktop and narrow viewport, use the form by keyboard, check visible focus, verify copy/downloads, and test 200% text/zoom reflow. Automated checks cannot establish visual character fidelity or successful installation in another account. ### Project layout - `skills/make-reaction-stickers/`: canonical existing skill and UI metadata. - `.codex-plugin/plugin.json`: existing plugin manifest. - `demo/`: self-contained static site; only this folder needs hosting. - `scripts/build.py`: deterministic package builder, using Python’s standard library. - `tests/`: focused Python and Node tests, with no installed dependencies. The plugin ZIP contains only its manifest, skill (including the local upload script), and this guide. Demo/build/test files belong to the full source project. `demo/downloads/SHA256SUMS.txt` provides file checksums.