Initialized openAPI doc
This commit is contained in:
47
.gitignore
vendored
Normal file
47
.gitignore
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
.DS_Store
|
||||
|
||||
################################
|
||||
# Node.js/React (web/) 專屬
|
||||
################################
|
||||
# 忽略所有 node_modules 資料夾
|
||||
node_modules
|
||||
# 忽略 npm 快取和日誌
|
||||
npm-debug.log*
|
||||
.yarn-integrity
|
||||
.pnp.*
|
||||
|
||||
# 忽略打包後的產物
|
||||
web/dist
|
||||
web/build
|
||||
|
||||
|
||||
################################
|
||||
# Go (server/ & bot/) 專屬
|
||||
################################
|
||||
# 忽略 Go 專案生成的執行檔
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
# 忽略跨平台編譯的產物
|
||||
server/bin/
|
||||
bot/bin/
|
||||
# 忽略測試快取
|
||||
*.test
|
||||
*.out
|
||||
|
||||
|
||||
################################
|
||||
# IDE/OS/Config 專屬
|
||||
################################
|
||||
# VS Code (常見)
|
||||
.vscode/
|
||||
|
||||
# JetBrains (GoLand/WebStorm)
|
||||
.idea/
|
||||
|
||||
# macOS 專屬檔案
|
||||
.DS_Store
|
||||
|
||||
# 環境變數與機敏檔案 (重要!)
|
||||
.env
|
||||
.env.local
|
||||
BIN
Group 21 - Golang Final Project Proposal.pdf
Normal file
BIN
Group 21 - Golang Final Project Proposal.pdf
Normal file
Binary file not shown.
16
Makefile
Normal file
16
Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
.PHONY: viewapi
|
||||
|
||||
# 定義要檢查的模組目錄
|
||||
NODE_MODULES_PATH := node_modules
|
||||
|
||||
viewapi:
|
||||
@echo "Bootstrapping API document viewer..."
|
||||
if [ ! -d "$(NODE_MODULES_PATH)" ]; then \
|
||||
echo "Dependencies not installed, installing swagger-ui-express, yamljs..."; \
|
||||
[ ! -f "package.json" ] && npm init -y > /dev/null; \
|
||||
npm install express swagger-ui-express yamljs; \
|
||||
else \
|
||||
echo "Dependencies already installed."; \
|
||||
fi && \
|
||||
echo "Booting up API document viewer..." && \
|
||||
node api/view-API-doc.js
|
||||
62
README-en.md
Normal file
62
README-en.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# 🤖 Memebot: Discord Meme Management System
|
||||
|
||||
Memebot is a project that combines a **Discord Bot** and a **Web Frontend interface** to provide an efficient and customizable platform for community members to store, manage, and use their meme images.
|
||||
|
||||
## 🎯 Project Goals
|
||||
|
||||
* Provide an efficient Discord bot implemented in **Go** that can trigger meme output based on keywords.
|
||||
* Build a **React + TypeScript** driven web interface to facilitate users in viewing, uploading, and managing memes and their custom aliases.
|
||||
* Implement a **Many-to-Many** relationship model: one image can have multiple aliases, and one alias can also correspond to multiple images.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ System Architecture (Topology)
|
||||
|
||||
The project utilizes a microservice concept, dividing the functionality into three main components: the Discord Bot, the Web Backend (API), and the Web Frontend, all interacting with a shared Database/Storage.
|
||||
|
||||
<div style="text-align:center;">
|
||||
<img src="https://hackmd.io/_uploads/H1eUrs-f-l.png" width="70%"/>
|
||||
</div>
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Discord Bot Features
|
||||
|
||||
The bot is responsible for two main functions: image management and automated responses.
|
||||
|
||||
### 1. Image Trigger and Response
|
||||
|
||||
* **Trigger Mechanism:** The bot monitors chat messages. Once it detects that the message content contains any pre-defined **Alias**, it triggers a response.
|
||||
* **Matching Algorithm:** For high-efficiency, real-time matching across a large set of aliases, we will use the **Aho-Corasick algorithm**.
|
||||
* **Output Logic:** If the alias `<alias>` is matched, the bot will **randomly select** and send one image associated with that `<alias>` to the channel.
|
||||
|
||||
### 2. Discord Commands (Slash Commands)
|
||||
|
||||
| Command | Description |
|
||||
| :--- | :--- |
|
||||
| `/image upload` | Uploads an image. **Must include an image attachment** (JPG/PNG/GIF). The bot generates a unique **Image ID** (e.g., `670bc00e94fd77cf6852afc7`) and stores it. |
|
||||
| `/image link alias: <alias> image: <image_ID>` | Adds or links an `<alias>` to the specified `<image_ID>`. |
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Web Frontend Interface
|
||||
|
||||
The interface is built using **React + TypeScript** with **Tailwind CSS** to provide a user-friendly, visual management tool that is more convenient than using Discord commands alone.
|
||||
|
||||
### Page Layout and Functions
|
||||
|
||||
* **Navbar:** Displays the Bot's name on the left and contains **[Upload Image]** and **[Login]** buttons on the right.
|
||||
* **Left Sidebar:** Serves as a **Table of Contents** for all **Aliases**. The last entry is for "**Images without aliases**".
|
||||
* **Search Bar:** Located at the top of the right content area, featuring **real-time feedback** search functionality.
|
||||
* **Content Area:** Displays image thumbnails grouped by their corresponding Alias.
|
||||
|
||||
### 1. 🔎 View and Management (Find)
|
||||
|
||||
* **Image Preview:** Clicking any image thumbnail will open a **floating window (Modal)**.
|
||||
* **Image Information:** The modal will display the image, its uploader, upload time, and all associated Aliases.
|
||||
* **Editing Features:** Users can edit Aliases or delete the image from this modal (login required).
|
||||
|
||||
### 2. ⬆️ Upload Image
|
||||
|
||||
Clicking the **[Upload Image]** button opens a modal for submission:
|
||||
104
README.md
Normal file
104
README.md
Normal file
@@ -0,0 +1,104 @@
|
||||
{%hackmd @penguin71630/theme %}
|
||||
|
||||
# Go Final Project
|
||||
|
||||
主題:Discord 機器人,功能是存放一些梗圖,然後讓 Discord user 可以自行新增圖片、自定義 alias、根據 alias 叫出圖片。
|
||||
|
||||
除了 Discord bot 之外,還會有一個前端的網頁可以方便使用者查看、編輯這些圖片。
|
||||
|
||||
|
||||
|
||||
## Topology
|
||||
<div style="text-align:center;">
|
||||
<img src="https://hackmd.io/_uploads/H1eUrs-f-l.png" width="70%"/>
|
||||
</div>
|
||||
|
||||
|
||||
## Discord Bot Command (Draft)
|
||||
|
||||
圖片與 alias 的對應:多對多,一張圖片可以有很多 aliases,一個 alias 也可以對應到很多張圖片。
|
||||
|
||||
輸出圖片是被動觸發的:只要在使用者的聊天訊息偵測到特定關鍵詞(聊天訊息包含任意一個 alias,以第一個匹配成功的為主,匹配方式我們打算使用 Aho-Corasick algorithm 因為我們準備 ICPC 的時候學過),假設該 alias 是 `<alias>`,bot 會從含有該 `<alias>` 的圖片隨機挑選一個輸出。
|
||||
|
||||
在 Discord 對話框輸入 `/image upload` 並附上一個圖片檔(可以是 JPG/PNG/GIF),bot 會為這張圖片生成隨機一個唯一的 ID(例:`670bc00e94fd77cf6852afc7`),並將這個圖片存在 Database。
|
||||
|
||||
在 Discord 對話框輸入 `/image link alias: <alias> image: <image_ID>`,會為該圖片新增一個 alias。
|
||||
|
||||
|
||||
|
||||
## Web Backend API (Draft)
|
||||
|
||||
- `GET /api/image/<image_ID>`:取得某張圖片檔案。
|
||||
- `GET /api/image-list`:列出 db 內所有圖片檔案名稱。
|
||||
- `GET /api/alias-list`:列出 db 內所有 alias。
|
||||
- `/auth/gen-access-url`:預計使用 token-JWT exchange based authentication 來作為使用後端 API 的身分驗證。
|
||||
|
||||
其實應該還要有很多,還沒想清楚。
|
||||
|
||||
|
||||
## Web Frontend
|
||||
|
||||
除了 Discord bot 之外,預計用 React + TypeScript(CSS 用 Tailwind)寫一個前端網頁。
|
||||
|
||||
前端網頁大概長這樣:
|
||||
```
|
||||
Memebot [Upload-Image] [Login]
|
||||
---------------------------------------------------------------
|
||||
[ sadge ] ( Search Bar )
|
||||
[ 不要吼我啦 ]
|
||||
[ 什麼都願意做 ] Alias: 什麼都願意做
|
||||
[ 好時代來臨力 ] [ 方形圖片框1 ] [ 方形圖片框2 ] [ 方形圖片框3 ]
|
||||
[ 野獸 ] [ 方形圖片框4 ]
|
||||
[ poop ]
|
||||
[ rrrrr ] Alias: 好時代來臨力
|
||||
[ 讓我看看 ] [ 方形圖片框1 ] [ 方形圖片框2 ]
|
||||
[ 我愛慕虛榮啦 ]
|
||||
[ 一輩子 ] Alias: 野獸
|
||||
[ 是又怎樣 ] [ 方形圖片框1 ] [ 方形圖片框2 ] [ 方形圖片框3 ]
|
||||
[ Img w/o alias ]
|
||||
```
|
||||
|
||||
上方是 Navbar:
|
||||
- Navbar 左側是我們的 Bot 名稱。
|
||||
- Navbar 右側是功能按鈕,目前只有 Upload Image、Login。
|
||||
|
||||
### Find
|
||||
|
||||
- 左側 sidebar 是所有 aliases 的 table of content。類似 HackMD、mdBook 瀏覽某篇文章的功能。
|
||||
- 最後一列是「尚未有暱稱的圖片」。
|
||||
- 右側緊接在 Navbar 底下是 search bar
|
||||
- search bar 是即時反饋,使用者不需要按 enter 就會自動列出符合當前條件的圖片們。
|
||||
- 右側在 search bar 下方是各個 alias 對應的圖片們。
|
||||
- 點一下圖片之後,會跳出一個浮動視窗,顯示這張圖片以及圖片相關資訊(包含誰upload 這張圖片、什麼時候 upload 這張圖片、alias 有哪些),以及可以在這裡編輯 alias、刪除圖片。
|
||||
|
||||
### Upload Image
|
||||
|
||||
使用者按下這個按鈕之後,跳出一個浮動視窗:
|
||||
|
||||
```
|
||||
======================================
|
||||
| |--------------------------------| |
|
||||
| | | |
|
||||
| | | |
|
||||
| | Drag your image here | |
|
||||
| | | |
|
||||
| | | |
|
||||
| |--------------------------------| |
|
||||
| |
|
||||
| Aliases of this image: |
|
||||
| [ alias_01 ] |
|
||||
| [ alias_02 ] |
|
||||
| [+] [ (Add a new alias) ] |
|
||||
| |
|
||||
| <Upload> |
|
||||
======================================
|
||||
```
|
||||
|
||||
### Login
|
||||
|
||||
驗證當前操作前端的 Discord User,未登入的話只能使用查看相關的操作,不能編輯、上傳、刪除。
|
||||
|
||||
這個有空再來做。
|
||||
|
||||
|
||||
|
||||
347
api/openapi.yml
Normal file
347
api/openapi.yml
Normal file
@@ -0,0 +1,347 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Discord MemeBot API
|
||||
description: >
|
||||
Go Final Project 後端 API 文件。
|
||||
包含圖片上傳、檢索、Alias 管理以及 Discord OAuth 認證流程。
|
||||
version: 1.0.0
|
||||
|
||||
servers:
|
||||
- url: http://localhost:8080
|
||||
description: 本地開發伺服器
|
||||
|
||||
# 定義 Tag 的順序與描述 (讓文件更漂亮)
|
||||
tags:
|
||||
- name: Image
|
||||
description: 圖片相關操作 (查詢、上傳、刪除、取得檔案)
|
||||
- name: Alias
|
||||
description: 全域 Alias 列表
|
||||
- name: Linking/Unlinking
|
||||
description: 圖片與 Alias 之間的關聯管理
|
||||
- name: Authentication
|
||||
description: 使用者身分驗證與 Token 交換
|
||||
|
||||
# 全域安全驗證
|
||||
security:
|
||||
- bearerAuth: []
|
||||
|
||||
paths:
|
||||
# ----------------------------------------------------------------
|
||||
# Image (圖片資源)
|
||||
# ----------------------------------------------------------------
|
||||
/api/images:
|
||||
get:
|
||||
tags:
|
||||
- Image
|
||||
summary: 取得圖片列表
|
||||
description: 列出圖片,支援關鍵字搜尋、分頁與無 Alias 過濾。
|
||||
parameters:
|
||||
- name: search
|
||||
in: query
|
||||
description: 搜尋 Alias 關鍵字 (模糊搜尋)
|
||||
schema:
|
||||
type: string
|
||||
- name: null_alias
|
||||
in: query
|
||||
description: 若為 true,只回傳沒有任何 alias 的圖片
|
||||
schema:
|
||||
type: boolean
|
||||
- name: limit
|
||||
in: query
|
||||
description: 分頁:每頁幾筆
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
- name: page
|
||||
in: query
|
||||
description: 分頁:第幾頁
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
responses:
|
||||
'200':
|
||||
description: 成功取得列表
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
images:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Image'
|
||||
|
||||
post:
|
||||
tags:
|
||||
- Image
|
||||
summary: 上傳圖片
|
||||
description: 上傳圖片並設定初始 aliases。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
imgfile:
|
||||
type: string
|
||||
format: binary
|
||||
description: 圖片檔案
|
||||
aliases:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: 初始 Alias 列表 (支援重複 key 傳送)
|
||||
required:
|
||||
- imgfile
|
||||
responses:
|
||||
'201':
|
||||
description: 圖片建立成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Image'
|
||||
|
||||
/api/images/{id}:
|
||||
get:
|
||||
tags:
|
||||
- Image
|
||||
summary: 取得單張圖片資訊 (Metadata)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Image'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- Image
|
||||
summary: 刪除圖片
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'204':
|
||||
description: 刪除成功 (No Content)
|
||||
|
||||
/api/images/{id}/file:
|
||||
get:
|
||||
tags:
|
||||
- Image
|
||||
summary: 取得圖片原始檔案 (Binary)
|
||||
description: 用於 <img src="...">
|
||||
security: [] # 若圖片公開可留空
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 圖片串流
|
||||
content:
|
||||
image/png:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
image/jpeg:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
image/gif:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Alias (全域列表)
|
||||
# ----------------------------------------------------------------
|
||||
/api/aliases:
|
||||
get:
|
||||
tags:
|
||||
- Alias
|
||||
summary: 取得所有 Alias
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
aliases:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["114514", "哼哼啊啊啊啊", "poop"]
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Linking/Unlinking (關聯管理)
|
||||
# ----------------------------------------------------------------
|
||||
/api/images/{id}/aliases:
|
||||
post:
|
||||
tags:
|
||||
- Linking/Unlinking
|
||||
summary: 新增單一 Alias (關聯)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
example: "先輩"
|
||||
responses:
|
||||
'200':
|
||||
description: 新增成功,回傳更新後的圖片資訊
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Image'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- Linking/Unlinking
|
||||
summary: 刪除單一 Alias (關聯)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: alias
|
||||
in: query
|
||||
description: 要刪除的 alias 文字
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "先輩"
|
||||
responses:
|
||||
'204':
|
||||
description: 刪除成功 (No Content)
|
||||
|
||||
put:
|
||||
tags:
|
||||
- Linking/Unlinking
|
||||
summary: 批次取代 Alias 列表
|
||||
description: 用於前端編輯視窗的 Save 功能,完全取代舊有列表。
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
aliases:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["先輩", "好時代", "114514"]
|
||||
responses:
|
||||
'200':
|
||||
description: 更新成功,回傳更新後的圖片資訊
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Image'
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Authentication (驗證)
|
||||
# ----------------------------------------------------------------
|
||||
/auth/gen-access-url:
|
||||
post:
|
||||
tags:
|
||||
- Authentication
|
||||
summary: JWT 交換 (Login)
|
||||
description: 使用 Discord OAuth Code 交換 Access Token
|
||||
security: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
description: Discord OAuth Code
|
||||
redirect_uri:
|
||||
type: string
|
||||
grant_type:
|
||||
type: string
|
||||
default: authorization_code
|
||||
required:
|
||||
- code
|
||||
responses:
|
||||
'200':
|
||||
description: 登入成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
token_type:
|
||||
type: string
|
||||
example: "Bearer"
|
||||
expires_in:
|
||||
type: integer
|
||||
example: 3600
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
Image:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "101"
|
||||
uploaded_user_id:
|
||||
type: string
|
||||
example: "konchin.shih"
|
||||
uploaded_at:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2023-10-20T12:00:00Z"
|
||||
aliases:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["野獸", "先輩", "114514"]
|
||||
url:
|
||||
type: string
|
||||
description: 圖片二進制檔案的 API 路徑
|
||||
example: "/api/images/101/file"
|
||||
28
api/view-API-doc.js
Normal file
28
api/view-API-doc.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const YAML = require('yamljs');
|
||||
const path = require('path');
|
||||
|
||||
// 設定你的 yaml 檔案路徑
|
||||
// 假設這個 js 檔在根目錄,而 yaml 在 api 資料夾內
|
||||
const filePath = path.join(__dirname, 'openapi.yml');
|
||||
|
||||
try {
|
||||
// 嘗試讀取檔案
|
||||
const swaggerDocument = YAML.load(filePath);
|
||||
|
||||
// 設定路由
|
||||
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
|
||||
|
||||
console.log('==================================================');
|
||||
console.log(`文件伺服器已啟動!`);
|
||||
console.log(`請用瀏覽器打開: http://localhost:3000/docs`);
|
||||
console.log('==================================================');
|
||||
|
||||
} catch (e) {
|
||||
console.error("讀取 yaml 檔案失敗,請檢查路徑是否正確:", filePath);
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
app.listen(3000);
|
||||
Reference in New Issue
Block a user