Skip to content

Commit ffbc65a

Browse files
committed
Merge branch 'refactoring-v3-mvp' of github.com:lss233/chatgpt-mirai-qq-bot into refactoring-v3-mvp
2 parents 5fcf8e7 + 1738bc6 commit ffbc65a

File tree

6 files changed

+80
-44
lines changed

6 files changed

+80
-44
lines changed

.github/workflows/run-tests.yml

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,11 @@ jobs:
1414

1515
steps:
1616
- uses: actions/checkout@v3
17-
18-
- name: Set up Python
19-
uses: actions/setup-python@v4
20-
with:
21-
python-version: '3.10'
22-
23-
- name: Install dependencies
17+
- name: Set up Docker
18+
uses: docker/setup-docker-action@v4
19+
- name: Build Docker image
2420
run: |
25-
python -m pip install --upgrade pip
26-
pip install pytest
27-
pip install -r requirements.txt
28-
29-
- name: Run tests
21+
docker build -t test-image .
22+
- name: Run tests in Docker
3023
run: |
31-
python -m pytest -v
24+
docker run test-image sh -c "python -m pip install pytest && python -m pytest -v"

Dockerfile

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,35 @@ FROM python:3.10.13-slim-bullseye
22

33
ENV DEBIAN_FRONTEND=noninteractive
44

5-
COPY ./fonts/sarasa-mono-sc-regular.ttf /usr/share/fonts/
5+
COPY ./data/fonts/sarasa-mono-sc-regular.ttf /usr/share/fonts/
66

7-
RUN apt-get update && \
8-
apt install --no-install-recommends xvfb binutils build-essential qtbase5-dev wkhtmltopdf ffmpeg dbus -yq && \
7+
RUN apt-get -yqq update && \
8+
apt-get -yqq install --no-install-recommends xvfb binutils build-essential qtbase5-dev wkhtmltopdf ffmpeg dbus curl jq unzip && \
99
(strip --remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5 || true) && \
10-
apt-get clean && \
11-
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
10+
apt-get -yq clean && \
11+
apt-get -yq purge --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
1212
rm -rf /var/lib/apt/lists/*
1313

1414
RUN export DBUS_SESSION_BUS_ADDRESS=`dbus-daemon --fork --config-file=/usr/share/dbus-1/session.conf --print-address`
1515

1616
RUN mkdir -p /app
17+
1718
WORKDIR /app
1819

1920
COPY requirements.txt /app
20-
RUN pip install --no-cache-dir -r requirements.txt && \
21+
22+
RUN LATEST_RELEASE_URL=$(curl -s https://api.github.com/repos/DarkSkyTeam/chatgpt-for-bot-webui/releases | jq -r '.[0].assets[] | select(.name == "dist.zip") | .browser_download_url') \
23+
&& curl -L -o dist.zip "$LATEST_RELEASE_URL" \
24+
&& unzip dist.zip -d web \
25+
&& rm dist.zip && \
26+
pip install --no-cache-dir -r requirements.txt && \
2127
pip cache purge && \
2228
python -c "from pycloudflared import try_cloudflare; try_cloudflare(-1)" || true
2329

24-
RUN apt-get remove --purge -yq binutils
30+
RUN apt-get -yqq remove --purge binutils unzip curl jq
2531

2632
COPY . /app
2733

34+
EXPOSE 8080
35+
2836
CMD ["/bin/bash", "/app/docker/start.sh"]

framework/im/manager.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import asyncio
2-
from typing import Dict
2+
from typing import Dict, Type
33

44
from framework.config.config_loader import pydantic_validation_wrapper
55
from framework.config.global_config import GlobalConfig, IMConfig
@@ -60,7 +60,7 @@ def update_adapter_config(self, name: str, config: IMConfig):
6060
:param name: adapter 的名称
6161
:param config: adapter 的配置
6262
"""
63-
self.get_adapter_config(name).config = config
63+
self.get_adapter_config(name).config = config.model_dump()
6464

6565
def delete_adapter(self, name: str):
6666
"""
@@ -88,10 +88,7 @@ def start_adapters(self, loop=None):
8888
adapter_config = config_class(**im.config)
8989

9090
# 创建 adapter 实例
91-
with self.container.scoped() as scoped_container:
92-
scoped_container.register(config_class, adapter_config)
93-
adapter = Inject(scoped_container).create(adapter_class)()
94-
self.adapters[im.name] = adapter
91+
adapter = self.create_adapter(im.name, adapter_class, adapter_config)
9592
if im.enable:
9693
tasks.append(asyncio.ensure_future(self._start_adapter(im.name, adapter), loop=loop))
9794
if len(tasks) > 0:
@@ -127,14 +124,14 @@ def get_adapter(self, key: str) -> IMAdapter:
127124

128125
async def _start_adapter(self, key: str, adapter: IMAdapter):
129126
logger.info(f"Starting adapter: {key}")
130-
adapter.is_running = True
131127
await adapter.start()
128+
adapter.is_running = True
132129
logger.info(f"Started adapter: {key}")
133130

134131
async def _stop_adapter(self, key: str, adapter: IMAdapter):
135132
logger.info(f"Stopping adapter: {key}")
136-
adapter.is_running = False
137133
await adapter.stop()
134+
adapter.is_running = False
138135
logger.info(f"Stopped adapter: {key}")
139136

140137
def stop_adapter(self, adapter_id: str, loop: asyncio.AbstractEventLoop):
@@ -158,3 +155,12 @@ def is_adapter_running(self, key: str) -> bool:
158155

159156
return key in self.adapters and getattr(self.adapters[key], "is_running", False)
160157

158+
def create_adapter(self, name: str, adapter_class: Type[IMAdapter], adapter_config: IMConfig) -> IMAdapter:
159+
with self.container.scoped() as scoped_container:
160+
scoped_container.register(adapter_config.__class__, adapter_config)
161+
adapter = Inject(scoped_container).create(adapter_class)()
162+
adapter.is_running = False
163+
self.adapters[name] = adapter
164+
return adapter
165+
166+

framework/web/api/dispatch/routes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async def create_rule():
7575
return jsonify({"error": "Rule ID already exists"}), 400
7676

7777
# 检查工作流是否存在
78-
if not workflow_registry.get_workflow(rule_config.workflow_id):
78+
if not workflow_registry.get(rule_config.workflow_id):
7979
return jsonify({"error": "Workflow not found"}), 400
8080

8181
# 检查规则类型是否存在
@@ -139,7 +139,7 @@ async def update_rule(rule_id: str):
139139
return jsonify({"error": "Rule not found"}), 404
140140

141141
# 检查工作流是否存在
142-
if not workflow_registry.get_workflow(rule_config.workflow_id):
142+
if not workflow_registry.get(rule_config.workflow_id):
143143
return jsonify({"error": "Workflow not found"}), 400
144144

145145
# 检查规则类型是否存在

framework/web/api/im/routes.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,41 +64,47 @@ async def get_adapter(adapter_id: str):
6464
async def create_adapter():
6565
"""创建新的适配器"""
6666
data = await request.get_json()
67-
adapter_config = IMAdapterConfig(**data)
67+
adapter_info = IMAdapterConfig(**data)
6868

6969
config: GlobalConfig = g.container.resolve(GlobalConfig)
7070
registry: IMRegistry = g.container.resolve(IMRegistry)
7171
manager: IMManager = g.container.resolve(IMManager)
7272

7373
# 检查适配器类型是否存在
74-
if adapter_config.adapter not in registry.get_all_adapters():
74+
if adapter_info.adapter not in registry.get_all_adapters():
7575
return jsonify({"error": "Invalid adapter type"}), 400
7676

7777
# 检查ID是否已存在
78-
if manager.has_adapter(adapter_config.name):
78+
if manager.has_adapter(adapter_info.name):
7979
return jsonify({"error": "Adapter ID already exists"}), 400
8080

8181
# 更新配置
82-
config.ims.append(adapter_config)
82+
config.ims.append(adapter_info)
83+
adapter_class = registry.get_all_adapters()[adapter_info.adapter]
84+
adapter_config_class = registry.get_config_class(adapter_info.adapter)
85+
adapter_config = adapter_config_class(**adapter_info.config)
86+
manager.create_adapter(adapter_info.name, adapter_class, adapter_config)
87+
manager.start_adapter(adapter_info.name, asyncio.get_event_loop())
8388

8489
# 保存配置到文件
8590
ConfigLoader.save_config_with_backup("config.yaml", config)
8691

8792
return IMAdapterResponse(adapter=IMAdapterStatus(
88-
name=adapter_config.name,
89-
adapter=adapter_config.adapter,
93+
name=adapter_info.name,
94+
adapter=adapter_info.adapter,
9095
is_running=False,
91-
config=adapter_config.config
96+
config=adapter_info.config
9297
)).model_dump()
9398

9499
@im_bp.route('/adapters/<adapter_id>', methods=['PUT'])
95100
@require_auth
96101
async def update_adapter(adapter_id: str):
97102
"""更新适配器配置"""
98103
data = await request.get_json()
99-
adapter_config = IMAdapterConfig(**data)
104+
adapter_info = IMAdapterConfig(**data)
105+
registry: IMRegistry = g.container.resolve(IMRegistry)
100106

101-
if adapter_id != adapter_config.name:
107+
if adapter_id != adapter_info.name:
102108
return jsonify({"error": "Adapter ID mismatch"}), 400
103109

104110
config: GlobalConfig = g.container.resolve(GlobalConfig)
@@ -110,8 +116,11 @@ async def update_adapter(adapter_id: str):
110116
return jsonify({"error": "Adapter not found"}), 404
111117

112118
# 更新配置
113-
manager.update_adapter_config(adapter_id, adapter_config.config)
119+
adapter_config_class = registry.get_config_class(adapter_info.adapter)
120+
adapter_config = adapter_config_class(**adapter_info.config)
121+
manager.update_adapter_config(adapter_id, adapter_config)
114122

123+
115124
# 保存配置到文件
116125
ConfigLoader.save_config_with_backup("config.yaml", config)
117126

@@ -123,9 +132,10 @@ async def update_adapter(adapter_id: str):
123132

124133
return IMAdapterResponse(adapter=IMAdapterStatus(
125134
name=adapter_id,
126-
adapter=adapter_config.adapter,
135+
adapter=adapter_info.adapter,
127136
is_running=is_running,
128-
config=adapter_config.config
137+
config=adapter_info.config
138+
129139
)).model_dump()
130140

131141
@im_bp.route('/adapters/<adapter_id>', methods=['DELETE'])

framework/web/app.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
from quart_cors import cors
55
from hypercorn.config import Config
66
from hypercorn.asyncio import serve
7+
from werkzeug.exceptions import NotFound
78
from pathlib import Path
8-
9+
import os
910
from framework.logger import get_logger, HypercornLoggerWrapper
1011
from framework.ioc.container import DependencyContainer
1112
from .auth.routes import auth_bp
@@ -21,7 +22,24 @@
2122
from framework.web.auth.services import AuthService, FileBasedAuthService
2223

2324
def create_app(container: DependencyContainer) -> Quart:
24-
app = Quart(__name__, static_folder='web', static_url_path='')
25+
app = Quart(__name__)
26+
app.static_folder = '../../web'
27+
28+
@app.route('/')
29+
async def index():
30+
print("Serving index.html")
31+
return await app.send_static_file('index.html')
32+
@app.route('/<path:path>')
33+
async def serve_static(path):
34+
if path.startswith('backend-api'):
35+
raise NotFound()
36+
try:
37+
return await app.send_static_file(path)
38+
except Exception as e:
39+
return await app.send_static_file('index.html')
40+
41+
42+
2543
app = cors(app) # 启用CORS支持
2644

2745
# 注册蓝图
@@ -40,6 +58,7 @@ async def inject_container():
4058
g.container = container
4159

4260
app.container = container
61+
4362
return app
4463

4564
class WebServer:

0 commit comments

Comments
 (0)