Skip to content

Instantly share code, notes, and snippets.

@ChrisRomp
Created March 17, 2026 15:32
Show Gist options
  • Select an option

  • Save ChrisRomp/13906c2b52f12399f34f720e83e4532d to your computer and use it in GitHub Desktop.

Select an option

Save ChrisRomp/13906c2b52f12399f34f720e83e4532d to your computer and use it in GitHub Desktop.
Meme Generator skill for GitHub Copilot CLI / copilot-bridge -- generate and send memes inline using the imgflip API
#!/usr/bin/env python3
"""Search and generate memes via the imgflip API.
Usage:
Search: python3 imgflip.py search <query>
Generate: python3 imgflip.py generate <template_id> [--text0 "Top"] [--text1 "Bottom"]
List: python3 imgflip.py list [--limit N]
Credentials are read from environment variables IMGFLIP_USERNAME and IMGFLIP_PASSWORD.
Output is JSON to stdout for easy parsing by the agent.
"""
import argparse
import json
import os
import sys
import urllib.request
import urllib.parse
API_BASE = "https://api.imgflip.com"
# Common templates for quick reference
COMMON_TEMPLATES = {
"aliens": "101470",
"ancient aliens": "101470",
"one does not simply": "61579",
"drake": "181913649",
"two buttons": "87743020",
"distracted boyfriend": "112126428",
"batman slapping robin": "438680",
"fry": "61520",
"not sure if": "61520",
"roll safe": "89370399",
"uno draw 25": "217743513",
"expanding brain": "93895088",
"waiting skeleton": "4087833",
"woman yelling at cat": "188390779",
"most interesting man": "61532",
"pigeon": "100777631",
"is this a pigeon": "100777631",
"tuxedo pooh": "178591752",
"buff doge": "247375501",
"always has been": "252600902",
"running away balloon": "131087935",
"left exit": "124822590",
"change my mind": "129242436",
"this is fine": "55311130",
"surprised pikachu": "155067746",
"bernie sanders": "222403160",
}
def _request(url, data=None):
"""Make an HTTP request with proper headers."""
req = urllib.request.Request(url, data=data, method="POST" if data else "GET")
req.add_header("User-Agent", "copilot-bridge-meme-skill/1.0")
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
def get_memes(limit=20):
"""Fetch popular meme templates from imgflip."""
data = _request(f"{API_BASE}/get_memes")
if not data.get("success"):
return {"error": data.get("error_message", "Unknown error")}
memes = data["data"]["memes"][:limit]
return {"memes": [{"id": m["id"], "name": m["name"], "box_count": m["box_count"]} for m in memes]}
def search_memes(query):
"""Search meme templates by name. Checks common aliases first, then fetches from API."""
query_lower = query.lower().strip()
# Check common aliases first
if query_lower in COMMON_TEMPLATES:
return {"results": [{"id": COMMON_TEMPLATES[query_lower], "name": query_lower, "source": "alias"}]}
# Search the API
data = _request(f"{API_BASE}/get_memes")
if not data.get("success"):
return {"error": data.get("error_message", "Unknown error")}
results = []
for m in data["data"]["memes"]:
if query_lower in m["name"].lower():
results.append({"id": m["id"], "name": m["name"], "box_count": m["box_count"]})
# Also check aliases
for alias, tid in COMMON_TEMPLATES.items():
if query_lower in alias and not any(r["id"] == tid for r in results):
results.append({"id": tid, "name": alias, "source": "alias"})
return {"results": results}
def generate_meme(template_id, text0="", text1="", boxes=None):
"""Generate a meme image. Returns the URL of the generated image."""
username = os.environ.get("IMGFLIP_USERNAME", "")
password = os.environ.get("IMGFLIP_PASSWORD", "")
if not username or not password:
return {"error": "IMGFLIP_USERNAME and IMGFLIP_PASSWORD must be set in environment"}
params = {
"template_id": template_id,
"username": username,
"password": password,
}
if boxes:
for i, box_text in enumerate(boxes):
params[f"boxes[{i}][text]"] = box_text
else:
params["text0"] = text0
params["text1"] = text1
encoded = urllib.parse.urlencode(params).encode()
result = _request(f"{API_BASE}/caption_image", data=encoded)
if not result.get("success"):
return {"error": result.get("error_message", "Unknown error")}
return {"url": result["data"]["url"], "page_url": result["data"]["page_url"]}
def main():
parser = argparse.ArgumentParser(description="imgflip meme generator")
subparsers = parser.add_subparsers(dest="command", required=True)
# search
search_parser = subparsers.add_parser("search", help="Search meme templates")
search_parser.add_argument("query", help="Search query")
# generate
gen_parser = subparsers.add_parser("generate", help="Generate a meme")
gen_parser.add_argument("template_id", help="Template ID or common name")
gen_parser.add_argument("--text0", default="", help="Top text")
gen_parser.add_argument("--text1", default="", help="Bottom text")
gen_parser.add_argument("--boxes", nargs="+", help="Multiple text boxes (for 3+ box memes)")
# list
list_parser = subparsers.add_parser("list", help="List popular templates")
list_parser.add_argument("--limit", type=int, default=20, help="Number of templates")
args = parser.parse_args()
if args.command == "search":
result = search_memes(args.query)
elif args.command == "generate":
# Resolve common names to IDs
tid = args.template_id
if tid.lower() in COMMON_TEMPLATES:
tid = COMMON_TEMPLATES[tid.lower()]
result = generate_meme(tid, args.text0, args.text1, args.boxes)
elif args.command == "list":
result = get_memes(args.limit)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
name meme-generator
description Generate and send memes inline in chat using the imgflip API. Use this skill whenever the user asks for a meme, wants a funny image response, asks you to "meme" something, requests a reaction image, or says things like "make me a meme", "that needs a meme", "what's the meme for that", or describes a situation that calls for a humorous image macro. Also trigger when the user asks to search for meme templates or wants to know what memes are available.

Meme Generator

Generate memes via the imgflip API and deliver them inline in chat.

How It Works

Use the scripts/imgflip.py script for all meme operations. Source credentials from .env before running:

source <workspace>/.env

Search for a template

When the user describes a meme but you're not sure of the template ID, search first:

python3 <skill-path>/scripts/imgflip.py search "drake"

The script knows common aliases (e.g., "aliens", "fry", "pigeon", "this is fine") and also searches the imgflip API for matches.

Generate a meme

Standard 2-box meme (top/bottom text):

python3 <skill-path>/scripts/imgflip.py generate <template_id> --text0 "Top text" --text1 "Bottom text"

You can also use a common name instead of an ID:

python3 <skill-path>/scripts/imgflip.py generate "drake" --text0 "Writing code yourself" --text1 "Having an AI write memes for you"

For memes with 3+ text boxes (like Expanding Brain):

python3 <skill-path>/scripts/imgflip.py generate 93895088 --boxes "Level 1" "Level 2" "Level 3" "Level 4"

Deliver to chat

After generating, download the image and send it:

curl -s -o <workspace>/.temp/meme.jpg "<url-from-generate>"

Then use the send_file tool with path .temp/meme.jpg and an optional message.

Picking the Right Template

Match the user's intent to the meme format:

  • Comparing two things (one better): Drake, Tuxedo Pooh, Buff Doge vs. Cheems
  • Something obvious people ignore: Left Exit 12, Running Away Balloon
  • Pretending not to notice: This Is Fine
  • Conspiracy / over-explanation: Ancient Aliens, Expanding Brain, Roll Safe
  • Unexpected realization: Surprised Pikachu, Futurama Fry
  • Calling something out: Woman Yelling at Cat, Distracted Boyfriend
  • Ultimatum: UNO Draw 25, Two Buttons
  • Stating facts: Change My Mind, One Does Not Simply
  • Reaction: Batman Slapping Robin, Always Has Been

If the user describes a vibe but doesn't name a template, pick the best fit. If you're unsure, suggest 2-3 options.

Tips

  • Leave text0 empty for memes that work better with only bottom text (like Ancient Aliens)
  • Keep text short; long captions get squished
  • The generated image URL is publicly accessible but not indexed; share freely in chat
  • Images with low views are auto-deleted by imgflip after a while
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment