{"id":13427989,"url":"https://github.com/lencx/chat-todo-plugin","last_synced_at":"2025-04-09T22:18:29.208Z","repository":{"id":147211664,"uuid":"618238483","full_name":"lencx/chat-todo-plugin","owner":"lencx","description":"✅ ChatGPT Plugin for managing a TODO list","archived":false,"fork":false,"pushed_at":"2023-03-24T18:27:57.000Z","size":63,"stargazers_count":258,"open_issues_count":1,"forks_count":42,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-09T22:18:25.111Z","etag":null,"topics":["chat-plugin","chatgpt","chatgpt-plugins","openai","plugin","todo-list"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lencx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-03-24T03:12:01.000Z","updated_at":"2025-03-09T21:51:15.000Z","dependencies_parsed_at":"2023-07-03T23:48:54.068Z","dependency_job_id":null,"html_url":"https://github.com/lencx/chat-todo-plugin","commit_stats":{"total_commits":10,"total_committers":1,"mean_commits":10.0,"dds":0.0,"last_synced_commit":"995e815edf7d5983be9c61e086f63c6806068213"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lencx%2Fchat-todo-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lencx%2Fchat-todo-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lencx%2Fchat-todo-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lencx%2Fchat-todo-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lencx","download_url":"https://codeload.github.com/lencx/chat-todo-plugin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248119286,"owners_count":21050755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat-plugin","chatgpt","chatgpt-plugins","openai","plugin","todo-list"],"created_at":"2024-07-31T01:00:43.992Z","updated_at":"2025-04-09T22:18:29.185Z","avatar_url":"https://github.com/lencx.png","language":"Python","funding_links":[],"categories":["Plugins:","Productivity","Browser-extensions","Python"],"sub_categories":["note: for unverified plugins, please enter website domains"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg width=\"180\" src=\"./logo.svg\" alt=\"ChatGPT TODO List\"\u003e\n  \u003ch1 align=\"center\"\u003echat-todo-plugin\u003c/h1\u003e\n  \u003cp align=\"center\"\u003eChatGPT Plugin for managing a TODO list\u003c/p\u003e\n\u003c/p\u003e\n\n- [ChatGPT plugins](https://openai.com/blog/chatgpt-plugins)\n- [ChatGPT plugins Doc](https://platform.openai.com/docs/plugins/introduction)\n- [OpenAPI Specification](https://swagger.io/specification)\n\n---\n\n- [开发指南：ChatGPT 插件开发（上）](https://mp.weixin.qq.com/s/AmNkiLOqJo7tEJZPX34oeg)\n- [开发指南：ChatGPT 插件开发（下）](https://mp.weixin.qq.com/s/8EE3y4hU5Rp0rCCDPBEL2w)\n\n## Plugin development\n\n```bash\n[plugin-repo]\n|- main.py\n|- manifest.json\n|- openapi.yaml\n|- logo.png\n`- ... # other\n```\n\n### manifest.json\n\n[plugin-manifest](https://platform.openai.com/docs/plugins/getting-started/plugin-manifest): Every plugin requires a ai-plugin.json file, which needs to be hosted on the API’s domain.\n\n```json\n{\n  \"schema_version\": \"v1\",\n  \"name_for_human\": \"TODO Plugin (service http)\",\n  \"name_for_model\": \"todo\",\n  \"description_for_human\": \"Plugin for managing a TODO list, you can add, remove and view your TODOs.\",\n  \"description_for_model\": \"Plugin for managing a TODO list, you can add, remove and view your TODOs.\",\n  \"auth\": {\n    \"type\": \"service_http\",\n    \"authorization_type\": \"bearer\",\n    \"verification_tokens\": {\n      \"openai\": \"\u003cYOUR_OPENAI_KEY\u003e\"\n    }\n  },\n  \"api\": {\n    \"type\": \"openapi\",\n    \"url\": \"https://\u003cYOUR_REPO\u003e.\u003cYOUR_OWNER\u003e.repl.co/openapi.yaml\",\n    \"is_user_authenticated\": false\n  },\n  \"logo_url\": \"https://\u003cYOUR_REPO\u003e.\u003cYOUR_OWNER\u003e.repl.co/logo.png\",\n  \"contact_email\": \"\u003cYOUR_EMAIL\u003e\",\n  \"legal_info_url\": \"http://www.example.com/legal\"\n}\n```\n\n- `\u003cYOUR_OPENAI_KEY\u003e`: your OpenAI API key\n- `\u003cYOUR_REPO\u003e`: your replit app name\n- `\u003cYOUR_OWNER\u003e`: your replit username\n- `\u003cYOUR_EMAIL\u003e`: your email\n\n### openapi.yaml\n\n[openapi-definition](https://platform.openai.com/docs/plugins/getting-started/openapi-definition): OpenAPI specification to document the API. The model in ChatGPT does not know anything about your API other than what is defined in the OpenAPI specification and manifest file. This means that if you have an extensive API, you need not expose all functionality to the model and can choose specific endpoints.\n\n```yaml\nopenapi: 3.0.1\ninfo:\n  title: TODO Plugin\n  description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username \"global\".\n  version: 'v1'\nservers:\n  # product: http://www.example.com\n  - url: http://localhost:5002\npaths:\n  /todos/{username}:\n    get:\n      operationId: getTodos\n      summary: Get the list of todos\n      parameters:\n      - in: path\n        name: username\n        schema:\n            type: string\n        required: true\n        description: The name of the user.\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/getTodosResponse'\n# ...\n```\n\n### main.py\n\n```py\nimport os\nimport quart\nimport quart_cors\nfrom quart import Quart, jsonify, request\n\nPORT = 5002\nTODOS = {}\n# Get authentication key from environment variable\nSERVICE_AUTH_KEY = os.environ.get(\"SERVICE_AUTH_KEY\")\n\n\n# Create a Quart app and enable CORS\napp = quart_cors.cors(\n  Quart(__name__),\n  allow_origin=[\n    f\"http://localhost:{PORT}\",\n    \"https://chat.openai.com\",\n  ]\n)\n\n\n# Add a before_request hook to check for authorization header\n@app.before_request\ndef assert_auth_header():\n  auth_header = request.headers.get(\"Authorization\")\n  print(auth_header)\n  # check if the header is missing or incorrect, and return an error if needed\n  if not auth_header or auth_header != f\"Bearer {SERVICE_AUTH_KEY}\":\n        return jsonify({\"error\": \"Unauthorized\"}), 401\n\n\n# Add a route to get all todos\n@app.route(\"/todos\", methods=[\"GET\"])\nasync def get_todos():\n  return jsonify(TODOS)\n\n\n# Add a route to get all todos for a specific user\n@app.route(\"/todos/\u003cstring:username\u003e\", methods=[\"GET\"])\nasync def get_todo_user(username):\n    todos = TODOS.get(username, [])\n    return jsonify(todos)\n\n\n# Add a route to add a todo for a specific user\n@app.route(\"/todos/\u003cstring:username\u003e\", methods=[\"POST\"])\nasync def add_todo(username):\n    request_data = await request.get_json()\n    todo = request_data.get(\"todo\", \"\")\n    TODOS.setdefault(username, []).append(todo)\n    return jsonify({\"status\": \"success\"})\n\n\n# Add a route to delete a todo for a specific user\n@app.route(\"/todos/\u003cstring:username\u003e\", methods=[\"DELETE\"])\nasync def delete_todo(username):\n    request_data = await request.get_json()\n    todo_idx = request_data.get(\"todo_idx\", -1)\n    if 0 \u003c= todo_idx \u003c len(TODOS.get(username, [])):\n        TODOS[username].pop(todo_idx)\n    return jsonify({\"status\": \"success\"})\n\n\n@app.get(\"/logo.png\")\nasync def plugin_logo():\n  filename = 'logo.png'\n  return await quart.send_file(filename, mimetype='image/png')\n\n\n@app.get(\"/.well-known/ai-plugin.json\")\nasync def plugin_manifest():\n  host = request.headers['Host']\n  with open(\"manifest.json\") as f:\n    text = f.read()\n    text = text.replace(\"PLUGIN_HOSTNAME\", f\"https://{host}\")\n    return quart.Response(text, mimetype=\"text/json\")\n\n\n@app.get(\"/openapi.yaml\")\nasync def openapi_spec():\n  host = request.headers['Host']\n  with open(\"openapi.yaml\") as f:\n    text = f.read()\n    text = text.replace(\"PLUGIN_HOSTNAME\", f\"https://{host}\")\n    return quart.Response(text, mimetype=\"text/yaml\")\n\n\ndef main():\n  app.run(debug=True, host=\"0.0.0.0\", port=PORT)\n\n\nif __name__ == \"__main__\":\n  main()\n```\n\n## Plugin Deploy (Replit)\n\n- Open [Replit](https://replit.com) and click the `Create Repl` button.\n- When the pop-up window appears, click the `Import from GitHub` button in the top right corner.\n- In the GitHub URL field, enter `https://github.com/lencx/chat-todo-plugin` and select `Python` as the language.\n- Then click the `Import from GitHub` button in the bottom right corner and wait for the initialization to complete.\n- Click the `Run` button and wait for the execution to finish.\n\n\u003c!-- ## Local Development\n\n```bash\n# install dependencies\npoetry install\n\n# run the service\npoetry run python main.py\n``` --\u003e\n\n## TODO API\n\n### all list\n\n```bash\ncurl -X GET http://0.0.0.0:5002/todos \\\n -H 'Content-Type: application/json' \\\n -H 'Authorization: Bearer $SERVICE_AUTH_KEY'\n```\n\n### specific user\n\n```bash\ncurl -X GET http://0.0.0.0:5002/todos/lencx \\\n -H 'Content-Type: application/json' \\\n -H 'Authorization: Bearer $SERVICE_AUTH_KEY'\n```\n\n### specific user: add\n\n```bash\ncurl -X POST http://0.0.0.0:5002/todos/lencx \\\n -H 'Content-Type: application/json' \\\n -H 'Authorization: Bearer $SERVICE_AUTH_KEY' \\\n -d '{ \"todo\": \"hello\" }'\n```\n\n### specific user: delete\n\n```bash\ncurl -X DELETE http://0.0.0.0:5002/todos/lencx \\\n -H 'Content-Type: application/json' \\\n -H 'Authorization: Bearer $SERVICE_AUTH_KEY' \\\n -d '{ \"todo_idx\": 0 }'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flencx%2Fchat-todo-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flencx%2Fchat-todo-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flencx%2Fchat-todo-plugin/lists"}