├── .gitignore ├── logo.png ├── package.json ├── .well-known ├── ai-plugin-todos.json └── ai-plugin.json ├── openapi.yaml ├── index.js ├── index-todos.js └── openapi-todos.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/midudev/chat-gpt-plugins-javascript/main/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-first-midu-chatgpt-plugin", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "cors": "2.8.5", 15 | "express": "4.18.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.well-known/ai-plugin-todos.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "v1", 3 | "name_for_human": "To Do Plugin", 4 | "name_for_model": "todo", 5 | "description_for_human": "Manage a ToDo List", 6 | "description_for_model": "Plugin for managing a ToDo List for ChatGPT. You can add, remove, view and edit items", 7 | "auth": { 8 | "type": "none" 9 | }, 10 | "api": { 11 | "type": "openapi", 12 | "url": "http://localhost:3000/openapi.yaml", 13 | "is_user_authenticated": false 14 | }, 15 | "logo_url": "http://localhost:3000/logo.png", 16 | "contact_email": "midudev@gmail.com", 17 | "legal_info_url": "https://midu.dev/legal" 18 | } -------------------------------------------------------------------------------- /.well-known/ai-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "v1", 3 | "name_for_human": "GitHub Search Repositories", 4 | "name_for_model": "github_search_repositories", 5 | "description_for_human": "Search repositories on GitHub using ChatGPT", 6 | "description_for_model": "Plugin for searching repositories on GitHub", 7 | "auth": { 8 | "type": "none" 9 | }, 10 | "api": { 11 | "type": "openapi", 12 | "url": "http://localhost:3000/openapi.yaml", 13 | "is_user_authenticated": false 14 | }, 15 | "logo_url": "http://localhost:3000/logo.png", 16 | "contact_email": "midudev@gmail.com", 17 | "legal_info_url": "https://midu.dev/legal" 18 | } -------------------------------------------------------------------------------- /openapi.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | 3 | info: 4 | title: ChatGPT Plugin GitHub Search Repositories API 5 | version: 1.0.0 6 | description: API Specification for ChatGPT Plugin GitHub Search Repositories API 7 | 8 | paths: 9 | /search: 10 | get: 11 | summary: Search GitHub Repositories using a query 12 | operationId: searchGitHub 13 | tags: 14 | - github 15 | parameters: 16 | - in: query 17 | name: q 18 | required: true 19 | description: Query to search GitHub Repositories 20 | schema: 21 | type: string 22 | responses: 23 | '200': 24 | description: Successful response 25 | content: 26 | application/json: 27 | schema: 28 | type: object 29 | properties: 30 | repos: 31 | type: array 32 | items: 33 | $ref: '#/components/schemas/Repository' 34 | 35 | components: 36 | schemas: 37 | Repository: 38 | type: object 39 | properties: 40 | name: 41 | type: string 42 | stars: 43 | type: integer 44 | url: 45 | type: string 46 | description: 47 | type: string 48 | required: 49 | - name 50 | - stars 51 | - url 52 | - description -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import express, { json } from 'express' 2 | import cors from 'cors' 3 | import fs from 'node:fs/promises' // fs -> file system -> sistema de archivos (leer) 4 | import path from 'node:path' 5 | import crypto from 'node:crypto' 6 | 7 | const PORT = process.env.PORT ?? 3000 8 | const app = express() 9 | 10 | app.use(cors( 11 | { 12 | methods: ['GET'], 13 | origin: [`https://localhost:${PORT}`, 'https://chat.openai.com'] 14 | } 15 | )) 16 | app.use(json()) 17 | 18 | app.use((req, res, next) => { 19 | console.log(`Request received: ${req.method} ${req.url}`) 20 | next() 21 | }) 22 | 23 | // 1. Preparar los endpoints para servir la información 24 | // que necesita el Plugin de ChatGPT 25 | app.get('/openapi.yaml', async (req, res, next) => { 26 | try { 27 | const filePath = path.join(process.cwd(), 'openapi.yaml') 28 | const yamlData = await fs.readFile(filePath, 'utf-8') 29 | res.setHeader('Content-Type', 'text/yaml') 30 | res.send(yamlData) 31 | } catch (e) { 32 | console.error(e.message) 33 | // esto no lo hagáis normalmente 34 | res.status(500).send({ error: 'Unable to fetch openapi.yaml manifest'}) 35 | } 36 | }) 37 | 38 | app.get('/.well-known/ai-plugin.json', (req, res) => { 39 | res.sendFile(path.join(process.cwd(), '.well-known/ai-plugin.json')) 40 | }) 41 | 42 | app.get('/logo.png', (req, res) => { 43 | res.sendFile(path.join(process.cwd(), 'logo.png')) 44 | }) 45 | 46 | // 2. Los endpoints de la API 47 | // para que funcione el plugin de ChatGPT con GitHub 48 | app.get('/search', async (req, res) => { 49 | const { q } = req.query 50 | 51 | const apiURL = `https://api.github.com/search/repositories?q=${q}` 52 | const apiResponse = await fetch(apiURL, { 53 | headers: { 54 | 'User-Agent': 'ChatGPT Plugin v.1.0.0 - @midudev', 55 | 'Accept': 'application/vnd.github.v3+json' 56 | } 57 | }) 58 | 59 | if (!apiResponse.ok) { 60 | return res.sendStatus(apiResponse.status) 61 | } 62 | 63 | console.log('te quedan:', apiResponse.headers.get('X-RateLimit-Remaining')) 64 | 65 | const json = await apiResponse.json() 66 | 67 | const repos = json.items.map(item => ({ 68 | name: item.name, 69 | description: item.description, 70 | stars: item.stargazers_count, 71 | url: item.html_url 72 | })) 73 | 74 | return res.json({ repos }) 75 | 76 | }) 77 | 78 | // 3. Iniciar el servidor 79 | app.listen(PORT, () => { 80 | console.log('ChatGPT Plugin is listening on port', PORT) 81 | }) -------------------------------------------------------------------------------- /index-todos.js: -------------------------------------------------------------------------------- 1 | import express, { json } from 'express' 2 | import cors from 'cors' 3 | import fs from 'node:fs/promises' // fs -> file system -> sistema de archivos (leer) 4 | import path from 'node:path' 5 | import crypto from 'node:crypto' 6 | 7 | const PORT = process.env.PORT ?? 3000 8 | const app = express() 9 | 10 | app.use(cors( 11 | { 12 | methods: ['GET'], 13 | origin: [`https://localhost:${PORT}`, 'https://chat.openai.com'] 14 | } 15 | )) 16 | app.use(json()) 17 | 18 | app.use((req, res, next) => { 19 | console.log(`Request received: ${req.method} ${req.url}`) 20 | next() 21 | }) 22 | 23 | // 1. Preparar los endpoints para servir la información 24 | // que necesita el Plugin de ChatGPT 25 | app.get('/openapi.yaml', async (req, res, next) => { 26 | try { 27 | const filePath = path.join(process.cwd(), 'openapi.yaml') 28 | const yamlData = await fs.readFile(filePath, 'utf-8') 29 | res.setHeader('Content-Type', 'text/yaml') 30 | res.send(yamlData) 31 | } catch (e) { 32 | console.error(e.message) 33 | // esto no lo hagáis normalmente 34 | res.status(500).send({ error: 'Unable to fetch openapi.yaml manifest'}) 35 | } 36 | }) 37 | 38 | app.get('/.well-known/ai-plugin.json', (req, res) => { 39 | res.sendFile(path.join(process.cwd(), '.well-known/ai-plugin.json')) 40 | }) 41 | 42 | app.get('/logo.png', (req, res) => { 43 | res.sendFile(path.join(process.cwd(), 'logo.png')) 44 | }) 45 | 46 | // 2. Los endpoints de la API 47 | // para que funcione el plugin de ChatGPT con los todos 48 | let TODOS = [ 49 | { id: crypto.randomUUID(), title: 'Ver el Twitch de midudev' }, 50 | { id: crypto.randomUUID(), title: 'Enviar el proyecto Hackathon InfoJobs' }, 51 | { id: crypto.randomUUID(), title: 'Crear plugin de ChatGPT' }, 52 | { id: crypto.randomUUID(), title: 'Mejorar el rendimiento de la web' }, 53 | ] 54 | 55 | app.get('/todos', (req, res) => { 56 | res.json({ todos: TODOS }) 57 | }) 58 | 59 | app.post('/todos', (req, res) => { 60 | const { title } = req.body 61 | const newTodo = { id: crypto.randomUUID(), title } 62 | 63 | TODOS.push(newTodo) // lo podrías hacer en una base de datos 64 | 65 | res.json(newTodo) 66 | }) 67 | 68 | app.get('/todos/:id', (req, res) => { 69 | const { id } = req.params 70 | const todo = TODOS.find(todo => todo.id === id) 71 | return res.json(todo) 72 | }) 73 | 74 | app.put('/todos/:id', (req, res) => { 75 | const { id } = req.params 76 | const { title } = req.body 77 | 78 | let newTodo = null 79 | 80 | TODOS.forEach((todo, index) => { 81 | if (todo.id === id) { 82 | newTodo = { ...todo, title } 83 | TODOS[index] = newTodo 84 | } 85 | }) 86 | 87 | return res.json(newTodo) 88 | }) 89 | 90 | app.delete('/todos/:id', (req, res) => { 91 | const { id } = req.params 92 | 93 | TODOS = TODOS.filter(todo => todo.id !== id) 94 | 95 | return res.json({ ok: true }) 96 | }) 97 | 98 | // 3. Iniciar el servidor 99 | app.listen(PORT, () => { 100 | console.log('ChatGPT Plugin is listening on port', PORT) 101 | }) -------------------------------------------------------------------------------- /openapi-todos.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | 3 | info: 4 | title: ChatGPT Plugin ToDo API 5 | version: 1.0.0 6 | description: API Specification for ChatGPT Plugin ToDo API 7 | 8 | paths: 9 | /todos: 10 | get: 11 | summary: Get all todos 12 | operationId: getTodos 13 | tags: 14 | - todos 15 | responses: 16 | '200': 17 | description: Succesful response 18 | content: 19 | application/json: 20 | schema: 21 | type: object 22 | properties: 23 | todos: 24 | type: array 25 | items: 26 | $ref: '#/components/schemas/Todo' 27 | post: 28 | summary: Create a todo 29 | operationId: createTodo 30 | tags: 31 | - todos 32 | requestBody: 33 | required: true 34 | description: Todo object to be created 35 | content: 36 | application/json: 37 | schema: 38 | $ref: '#/components/schemas/TodoInput' 39 | responses: 40 | '200': 41 | description: Succesful response 42 | content: 43 | application/json: 44 | schema: 45 | $ref: '#/components/schemas/Todo' 46 | 47 | /todos/{id}: 48 | get: 49 | summary: Get a todo by id 50 | operationId: getTodo 51 | tags: 52 | - todos 53 | parameters: 54 | - name: id 55 | in: path 56 | required: true 57 | description: Id of the todo to be retrieved 58 | schema: 59 | type: string 60 | responses: 61 | '200': 62 | description: Succesful response 63 | content: 64 | application/json: 65 | schema: 66 | $ref: '#/components/schemas/Todo' 67 | 68 | put: 69 | summary: Update a todo by id 70 | operationId: updateTodo 71 | tags: 72 | - todos 73 | parameters: 74 | - name: id 75 | in: path 76 | required: true 77 | description: Id of the todo to be updated 78 | schema: 79 | type: string 80 | requestBody: 81 | required: true 82 | description: Todo object to be updated 83 | content: 84 | application/json: 85 | schema: 86 | $ref: '#/components/schemas/TodoInput' 87 | responses: 88 | '200': 89 | description: Succesful response 90 | content: 91 | application/json: 92 | schema: 93 | $ref: '#/components/schemas/Todo' 94 | delete: 95 | summary: Delete a todo by id 96 | operationId: deleteTodo 97 | tags: 98 | - todos 99 | parameters: 100 | - name: id 101 | in: path 102 | required: true 103 | description: Id of the todo to be deleted 104 | schema: 105 | type: string 106 | responses: 107 | '200': 108 | description: Succesful response 109 | content: 110 | application/json: 111 | type: object 112 | properties: 113 | ok: 114 | type: boolean 115 | 116 | components: 117 | schemas: 118 | Todo: 119 | type: object 120 | properties: 121 | id: 122 | type: string 123 | title: 124 | type: string 125 | required: 126 | - id 127 | - title 128 | 129 | TodoInput: 130 | type: object 131 | properties: 132 | title: 133 | type: string 134 | required: 135 | - title --------------------------------------------------------------------------------