初始提交

初始提交目前已有的文档和基础设置。
此提交还不包含 .gitignore 等文件。
This commit is contained in:
2026-04-22 13:34:05 +08:00
commit 891acdfbfd
28 changed files with 27737 additions and 0 deletions

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/SuanDocs.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

9
.idea/dictionaries/project.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>Astrbot</w>
<w>Suan</w>
<w>njupt</w>
</words>
</dictionary>
</component>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/SuanDocs.iml" filepath="$PROJECT_DIR$/.idea/SuanDocs.iml" />
</modules>
</component>
</project>

31
.vitepress/cache/deps/_metadata.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
"hash": "b10f48f1",
"configHash": "243b5d46",
"lockfileHash": "d693947e",
"browserHash": "320f5485",
"optimized": {
"vue": {
"src": "../../../node_modules/.pnpm/vue@3.5.32/node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "7d2bde70",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../node_modules/.pnpm/@vue+devtools-api@8.1.1/node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "b6a43c68",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../node_modules/.pnpm/@vueuse+core@14.2.1_vue@3.5.32/node_modules/@vueuse/core/dist/index.js",
"file": "vitepress___@vueuse_core.js",
"fileHash": "ba7f1e27",
"needsInterop": false
}
},
"chunks": {
"chunk-64NPA4UK": {
"file": "chunk-64NPA4UK.js"
}
}
}

12950
.vitepress/cache/deps/chunk-64NPA4UK.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

3
.vitepress/cache/deps/package.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

346
.vitepress/cache/deps/vue.js vendored Normal file
View File

@@ -0,0 +1,346 @@
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-64NPA4UK.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};

7
.vitepress/cache/deps/vue.js.map vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

36
.vitepress/config.mts Normal file
View File

@@ -0,0 +1,36 @@
import { defineConfig } from 'vitepress'
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Suan Projects",
description: "AI Projects of Mango Suan",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'README', link: '/readme' }
],
sidebar: [
{
text: 'NJUPT Suan API',
items: [
{ text: '介绍', link: '/suan-api/introduction' },
{ text: '部署', link: '/suan-api/deploy' },
{ text: 'Playwright', link: '/suan-api/playwright' },
{ text: 'LLM 工具', link: '/suan-api/llm-tools' },
]
},
{
text: 'Astrbot 插件(已归档)',
items: [
{ text: '介绍', link: '/astrbot-plugin/introduction' },
{ text: '图像渲染', link: '/astrbot-plugin/image-render' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
]
}
})

View File

@@ -0,0 +1,53 @@
# 图像渲染
NJUPT Suan 提供的工具中包含图像渲染的需求。在现在的版本中,图像是通过 `wkhtmltoimage` 渲染的。
## 为什么?
造成芒果帆帆的这一决策的主要原因是,目前 Astrbot 的一个使用较为广泛的插件
[BiliVideo](https://github.com/storyAura/astrbot_plugin_biliVideo)
的图片渲染技术路线是 `wkhtmltopdf`
`wkhtmltopdf``wkhtmltoimage` 疑似是同一种东西,在芒果帆帆尝试安装 `wkhtmltopdf` 的时候两个就一起安装起来了。
## 如何安装?
安装这个东西是一个相当复杂且难绷的过程。根据芒果帆帆拷打 AI 得到的结果,`wkhtmltopdf` 是一个开源项目,但是已经存档,理由是
**「社区认为其功能已经较为完善,因此不再需要进行开发维护。」**
由于该软件包已经停止维护,最新的软件包源中很可能不再包含它,比如 Debian 13。因此芒果帆帆在 Debian 13 上不得不下载 Debian 12 的软件包来安装。
:::warning
命令由 AI 生成,请谨慎使用。
:::
```bash
# 0. 到临时目录里下载适配于 Debian 12 的软件包版本
cd /tmp
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
# 1. 安装依赖(如果还没安装的话)
sudo apt-get install -y ca-certificates fontconfig libfreetype6 libjpeg62-turbo libpng16-16 libssl3 libstdc++6 libx11-6 libxcb1 libxext6 libxrender1 xfonts-75dpi xfonts-base zlib1g
# 2. 安装下载的 deb 包
sudo apt install /tmp/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
# 或者使用 dpkg
# sudo dpkg -i /tmp/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
```
## 这是好东西吗?
这显然并不是。
NJUPT Suan 现在使用 `wkhtmltoimage` 来将 HTML 渲染为 PNG 图像,为此,需要先在代码中拼接 HTML 文本,并写入临时文件,然后调用命令。
由于 `wkhtmltoimage` 已经不再维护,加上其历史包袱(基于早期的 webkit 引擎),已经不支持许多现代的 CSS 特性。
这会导致基于现代 CSS 设计的页面的渲染效果意外地差(或者说是样式无效),因此在 vibe coding 时,芒果帆帆不得不告诉 AI 避免使用新的 CSS 特性、
简化颜色(避免使用渐变)以及使用系统自带字体。
芒果帆帆现在对于 BiliVideo 竟然会使用这套技术路线感到奇怪。按理说Astrbot 内拥有将 Markdown 文本渲染为图片的方法,
BiliVideo 也并没有独特的需要额外实现渲染的需求(其渲染功能用于生成视频的总结图片),但 NJUPT Suan 是真的需要做课程表的表格。
如果 NJUPT Suan 还有未来的话,会尝试更换渲染方案。

View File

@@ -0,0 +1,32 @@
# NJUPT Suan 插件介绍
**NJUPT Suan** - **芒果酸**
## 这是什么
这是一个 Astrbot 插件。
[Astrbot](https://astrbot.app/) 是一个跨平台 AI 助手,让 AI 直接工作在聊天软件中。你也可以把它当成一个聊天机器人,或者一个不能聊天只负责执行任务的工具人。
NJUPT Suan 当中的 `Suan` 是芒果酸酸的酸,本插件为 Astrbot 中运行的 LLM 提供了与 NJUPT 有关的工具。
## 功能介绍
### 生成课程表
NJUPT-MCP 包含了[能够返回课程表数据的工具](/njupt-mcp/llm-tools.md)。NJUPT Suan 可以根据 NJUPT-MCP 的返回数据渲染课程表图片,
目前版本的渲染效果如下。
![单日课表](/image/astrbot-plugin-schedule-1.png)
![单周课表](/image/astrbot-plugin-schedule-2.png)
上方预览效果是在插件设置中启用了忽略空行空列之后的效果。如果显示完全的话,那么完全没有课的行(时间段)和列(天)都会显示完整。
图片的标题由 LLM 决定,因此你可以通过提示词来让 LLM 生成更适合的标题。
此功能被包装为 LLM 工具,供 LLM 主动调用,需要在 Astrbot 中使用支持工具调用的模型。
## 技术路线
Astrbot 是使用 Python 构建的,其插件也使用 Python因此 NJUPT Suan 也使用 Python 构建。

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

25
index.md Normal file
View File

@@ -0,0 +1,25 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Suan Projects"
text: "AI Projects of Mango Suan"
tagline: 中文名叫做芒果酸
actions:
- theme: brand
text: README.md
link: /readme
- theme: alt
text: Mango Gitea
link: https://gitea.mangofanfan.cn/SuanDev
features:
- title: Feature A
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
- title: Feature B
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
- title: Feature C
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
---

10
package.json Normal file
View File

@@ -0,0 +1,10 @@
{
"devDependencies": {
"vitepress": "2.0.0-alpha.17"
},
"scripts": {
"docs:dev": "vitepress dev --host --port 5123",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
}
}

1401
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

21
readme.md Normal file
View File

@@ -0,0 +1,21 @@
# README 自述文件
## 简介
**芒果酸**、**芒果酸酸**、**Mango Suan** 都是本项目的代称。本项目包含以下子项目:
* **NJUPT-Suan-API** - API 与 MCP 服务器,向调用客户端以及 LLM 工具返回数据;
* **astrbot_plugin_njupt_suan** - Astrbot 插件,需要结合 njupt-mcp 使用,提升交互体验。
* **SuanDocs** - 文档项目,本文档站点。
本项目的有关仓库均位于 **[Mango Gitea](https://gitea.mangofanfan.cn)** 上,这是芒果帆帆自己的 Gitea 实例,用于存放这些代码仓库。
当芒果帆帆认为某个仓库的代码已经足够见人时,会将仓库公开至 GitHub以方便任何人更方便地使用。
项目中的部分代码由 Kimi code 完成,芒果帆帆在其中起到一个吉祥物的作用。因此你也无需认真对待这些代码。
## 反馈
自然也可以在 Mango Gitea 的对应仓库内提交 issue。在 Gitea 中叫做工单。
如果芒果帆帆已经把东西扔到 GitHub 上了,那么在 GitHub 的仓库里提交 issue 也是可以的啦。

62
suan-api/deploy.md Normal file
View File

@@ -0,0 +1,62 @@
# 部署
酸 API 同时具备 API 和 MCP 后端的功能。因此,酸 API 不支持 stdio 格式的 MCP 调用。
## 部署 Suan API 示例
目前的 Suan API 在设计上即不允许(或者说不适合)公开部署、公共服务,因此你需要自行部署 Suan API 实例。
### 使用 uv 部署
### 使用 Docker 部署
### 从源码部署
:::warning 性能警告
直接从 `main.py` 入口运行可能会导致一些潜在的性能问题,表现为 Suan API 会占用较多的性能,而这可能不是你希望看到的。
等芒果帆帆再完善完善,然后争取提供其他更方便的部署选项吧~
:::
使用 git 命令从 Mango Gitea 上拉取源代码:
```bash
git clone https://gitea.mangofanfan.cn/SuanDev/NJUPT-Suan-API.git
```
这会将项目的源代码克隆到 `NJUPT-Suan-API` 目录中。
然后,你需要 uv 和 pnpm 来启动项目,请确保环境中已经安装了它们。
`NJUPT-Suan-API` 目录下运行:
```bash
uv sync
```
这将会构建 Python 虚拟环境,包括安装 Python 3.13(如果没有)和安装项目使用的依赖。
```bash
uv run playwright install chromium
```
这将会安装 chromium 供 playwright 使用。Suan API 通过这个 chromium 来模拟真实用户行为,以及截取课程表图片。
安装 chromium 可能需要一段**比较长的时间**。大体上这是能够成功的,只是需要一段**比较长的时间**。
然后(或者同时),在 `NJUPT-Suan-API/webui` 目录下运行:
```bash
pnpm install
pnpm build
```
这将会安装 WebUI 所使用的依赖,然后构建 WebUI。这一般是很快的。
现在,回到 `NJUPT-Suan-API` 目录下,使用以下命令启动项目:
```bash
uv run main.py
```
NJUPT Suan API 应当可以正常启动。

24
suan-api/introduction.md Normal file
View File

@@ -0,0 +1,24 @@
# NJUPT-Suan-API 介绍
## 这是什么
NJUPT 是 **南京邮电大学** 或者 **Nanjing University of Posts and Telecommunications** 的意思。
Suan 是本项目的代号,芒果酸或者芒果酸酸。
API 是这个东西的工作,作为 API以及 MCP。
MCP 是模型上下文协议,用于给 LLM 提供可供调用的工具。
## 特别说明
由于在网络上缺少与南京邮电大学相关的 Web API已有的项目[NJUPT-API](https://github.com/gaoliang/NJUPT-API))也已经年久失修,
芒果帆帆复刻这些 API 的过程也并不顺利。
经过芒果帆帆的社工活动可得,我邮并不对外提供查询课表、跑操次数等数据的接口,因此 NJUPT-Suan-API 不得不另辟蹊径来获得课表数据。
## 技术路线
NJUPT-Suan-API 使用 Python 实现了 API 和 MCP 后端,主要使用了 FastAPI 和 FastMCP所有功能一式两份在 API 和 MCP 中分别提供。
另外提供 WebUI 管理面板用来便于管理。管理面板使用 Vue 设计。出于一些考量,管理面板不是可选的模块。

127
suan-api/llm-tools.md Normal file
View File

@@ -0,0 +1,127 @@
# LLM 工具
## 课表工具
### get_course_schedule_json
```python
@mcp.tool(
name="get_course_schedule_json",
title="获取课表 JSON",
description="以 JSON 格式获取整学期整周全部课表数据,可选择是否压缩",
annotations=ToolAnnotations(
title="获取课表 JSON",
readOnlyHint=True, # 只读取数据,不修改
destructiveHint=False, # 非破坏性操作
idempotentHint=True, # 幂等:重复调用结果相同
openWorldHint=False, # 不依赖外部世界状态
),
)
async def get_course_schedule_json(compressed: bool = False) -> list[dict] | list[tuple]:
"""以 JSON 格式获取整学期整周全部课表
Args:
compressed: 是否压缩为紧凑的元组格式,默认为 False
Returns:
未压缩时compressed=False
课程字典列表,每个课程包含:
- name: 课程名称
- teacher: 授课教师(可能为 None
- classroom: 教室(可能为 None
- weeks: 周数list[int]
- day: 星期几int
- classes: 当日第几节课list[int]
压缩时compressed=True
元组列表 (name, teacher, classroom, weeks_str, day, classes)
其中 weeks_str 为压缩后的周数字符串(如 "1-17"
失败时返回空列表
"""
```
LLM 在调用时可以选择是否压缩。
请注意,[NJUPT Suan](/astrbot-plugin/introduction.md) 提供的渲染课表图片的工具需要经过压缩的课程数据,否则调用时很可能会由于参数过长而失败。
### get_week_course_schedule_json
```python
@mcp.tool(
name="get_week_course_schedule_json",
title="获取指定周课表",
description="以 JSON 格式获取指定教学周的全部课表数据,可选择是否压缩",
annotations=ToolAnnotations(
title="获取指定周课表",
readOnlyHint=True, # 只读取数据,不修改
destructiveHint=False, # 非破坏性操作
idempotentHint=True, # 幂等:重复调用结果相同
openWorldHint=False, # 不依赖外部世界状态
),
)
async def get_week_course_schedule_json(week: int, compressed: bool = False) -> list[dict] | list[tuple]:
"""以 JSON 格式获取指定教学周的全部课表
Args:
week: 教学周数,范围通常为 1-20
compressed: 是否压缩为紧凑的元组格式,默认为 False
Returns:
未压缩时compressed=False
指定周的课程字典列表,每个课程包含:
- name: 课程名称
- teacher: 授课教师(可能为 None
- classroom: 教室(可能为 None
- weeks: 周数list[int]
- day: 星期几int (1-7)
- classes: 当日第几节课list[int]
压缩时compressed=True
元组列表 (name, teacher, classroom, weeks_str, day, classes)
其中 weeks_str 为压缩后的周数字符串
该周无课程或参数错误时返回空列表
"""
```
同上。
### get_week_day_course_schedule_json
```python
@mcp.tool(
name="get_week_day_course_schedule_json",
title="获取指定周星期课表",
description="以 JSON 格式获取指定教学周和星期的课表数据,可选择是否压缩",
annotations=ToolAnnotations(
title="获取指定周星期课表",
readOnlyHint=True, # 只读取数据,不修改
destructiveHint=False, # 非破坏性操作
idempotentHint=True, # 幂等:重复调用结果相同
openWorldHint=False, # 不依赖外部世界状态
),
)
async def get_week_day_course_schedule_json(week: int, day: int, compressed: bool = False) -> list[dict] | list[tuple]:
"""以 JSON 格式获取指定教学周和星期的课表
Args:
week: 教学周数,范围通常为 1-20
day: 星期几1=星期一2=星期二,...7=星期日
compressed: 是否压缩为紧凑的元组格式,默认为 False
Returns:
未压缩时compressed=False
指定周和星期的课程字典列表,每个课程包含:
- name: 课程名称
- teacher: 授课教师(可能为 None
- classroom: 教室(可能为 None
- weeks: 周数list[int]
- day: 星期几int (1-7)
- classes: 当日第几节课list[int]
压缩时compressed=True
元组列表 (name, teacher, classroom, weeks_str, day, classes)
其中 weeks_str 为压缩后的周数字符串
该时段无课程或参数错误时返回空列表
"""
```
同上。
此三个工具的返回格式是统一的。

3
suan-api/playwright.md Normal file
View File

@@ -0,0 +1,3 @@
# Playwright
Playwright 是一个用于浏览器自动化的工具Suan API 通过 playwright 来规避教务系统和统一身份认证的反爬虫措施,顺便用它来截图。