Compare commits

...

1372 Commits

Author SHA1 Message Date
手瓜一十雪
323dc71d4e Improve Satori WebSocket logging and event handling
Enhanced debug logging in the Satori WebSocket server for better traceability of client events and authentication. Improved handling of client identification, including more robust checks and detailed logs for token validation. Removed unused MessageUnique logic from NapCatSatoriAdapter and added additional debug logs for event emission and message processing. Added a new onNtMsgSyncContactUnread method stub in NodeIKernelMsgListener.
2026-01-14 18:47:10 +08:00
手瓜一十雪
b0d88d3705 Refactor Satori actions with schema validation and router
Refactored all Satori action classes to use TypeBox schemas for payload validation and unified action naming via a new router. Added schema-based parameter checking to the SatoriAction base class. Introduced new actions for guild and member approval, and login retrieval. Centralized action name constants and types in a new router module. Enhanced event and message APIs with more structured event types and parsing logic. Added helper utilities for XML parsing. Updated exports and registration logic to support the new structure.
2026-01-14 17:52:38 +08:00
手瓜一十雪
32c0c93f3b Remove redundant private modifiers from constructor params
Eliminated unnecessary 'private' access modifiers from constructor parameters in OneBotProtocolAdapter and SatoriProtocolAdapter. This change clarifies parameter usage and avoids creating unused private fields.
2026-01-14 17:23:02 +08:00
手瓜一十雪
ea399c8017 Add protocol enable/disable and config management APIs
Introduces persistent protocol enable/disable state and related API endpoints in the backend, and adds frontend support for toggling protocols and managing protocol configs. Refactors protocol config storage to use per-protocol files, updates ProtocolManager to handle config and status, and enhances the Satori protocol UI with unified card components and improved state refresh. Removes the obsolete PROTOCOL_REFACTOR.md documentation.
2026-01-14 17:04:13 +08:00
手瓜一十雪
26d38bebe7 Refactor imports and add generic protocol config API
Replaced all '@/napcat-satori/...' imports with relative paths for consistency and compatibility. Added generic protocol config get/set handlers and routes in the web UI backend to support extensible protocol configuration management. Improved error handling and default value logic for Satori protocol configuration.
2026-01-14 16:01:29 +08:00
手瓜一十雪
506358e01a Refactor protocol management with napcat-protocol package
Introduced the new napcat-protocol package to unify protocol adapter management for OneBot and Satori. Updated napcat-framework and napcat-shell to use ProtocolManager instead of direct adapter instantiation. Added protocol info definitions to napcat-common, and integrated protocol configuration and management APIs into the web UI backend and frontend. This refactor improves maintainability, extensibility, and encapsulation of protocol logic, while maintaining backward compatibility.
2026-01-14 15:41:47 +08:00
手瓜一十雪
7cd0e5b2a4 Add isActive property to plugin adapters
Introduces an isActive getter to OB11PluginAdapter and OB11PluginMangerAdapter, which returns true only if the adapter is enabled and has loaded plugins. Updates event emission logic to use isActive instead of isEnable, ensuring events are only sent to active adapters.
2026-01-14 13:18:37 +08:00
手瓜一十雪
76447a385f Add onLoginRecordUpdate method to listener
Introduces the onLoginRecordUpdate method to NodeIKernelLoginListener, preparing for future handling of login record updates.
2026-01-14 13:13:18 +08:00
手瓜一十雪
5047b03303 Refactor network adapter activation and message handling
Introduces isActive property to network adapters for more accurate activation checks, refactors message dispatch logic to use only active adapters, and improves heartbeat management for WebSocket adapters. Also sets default enableWebsocket to false in config and frontend forms, and adds a security dialog for missing tokens in the web UI.
2026-01-14 13:11:17 +08:00
手瓜一十雪
fbccf8be24 Make emoji_likes_list optional in OB11Message
Changed the OB11Message interface to make emoji_likes_list optional and updated GetMsg to initialize emoji_likes_list as an empty array before populating it. This prevents errors when the field is missing and improves type safety.
2026-01-13 17:08:31 +08:00
手瓜一十雪
37ae17b53f Remove unused imports and update method params
Removed the unused 'readFileSync' import from ffmpeg-addon-adapter.ts. Updated parameter names in convertToNTSilkTct method of ffmpeg-exec-adapter.ts to use underscores, indicating unused variables.
2026-01-13 17:01:00 +08:00
手瓜一十雪
35566970fd Update pnpm-lock.yaml 2026-01-13 16:57:00 +08:00
手瓜一十雪
e70cd1eff7 Update QQ download links to version 44343
Updated Windows and Linux QQ download links in default.md and release_note_prompt.txt to point to version 9.9.26-44343 and 3.2.23-44343, replacing previous 40990 links.
2026-01-13 16:54:57 +08:00
手瓜一十雪
fbd3241845 Improve version info UI and update model config
Refined the system info version comparison layout for better responsiveness and readability, especially on smaller screens. Updated the OpenRouter model name in the release workflow and improved dark mode text color handling in sidebar menu items.
2026-01-13 16:50:46 +08:00
手瓜一十雪
cf69ccdbc9 Add emoji likes list support to message types
Introduces the emojiLikesList property to RawMessage and maps it to the new emoji_likes_list field in OB11Message, which is populated in the GetMsg action. Also updates type definitions for stricter typing and consistency.
2026-01-13 16:43:00 +08:00
Makoto
f3de4d48d3 feat: add settings field to group notice API response (#1505)
* feat: add settings field to group notice API response

- Add settings field to GroupNotice interface
- Include announcement configuration options (is_show_edit_card, remind_ts, tip_window_type, confirm_required)
- Fixes #1503

* refactor: make settings field optional for backward compatibility

- Mark settings as optional in GroupNotice interface
- Mark settings as optional in WebApiGroupNoticeFeed type
- Prevents runtime errors when processing older or malformed notices
- Addresses code review feedback on PR #1505

* Update GetGroupNotice.ts

---------

Co-authored-by: 手瓜一十雪 <nanaeonn@outlook.com>
2026-01-13 16:32:03 +08:00
手瓜一十雪
17d5110069 Add convertToNTSilkTct to FFmpeg adapters and update usage (#1517)
Introduces the convertToNTSilkTct method to FFmpeg adapter interfaces and implementations, updating audio conversion logic to use this new method for Silk format conversion. Refactors FFmpegService to rename convertFile to convertAudioFmt and updates related usages. Removes 'audio-worker' entry from vite configs in napcat-framework and napcat-shell. Also fixes a typo in appid.json.

Remove silk-wasm dependency and refactor audio handling

Eliminated the silk-wasm package and related code, including audio-worker and direct Silk encoding/decoding logic. Audio format conversion and Silk detection are now handled via FFmpeg adapters. Updated related OneBot actions and configuration files to remove all references to silk-wasm and streamline audio processing.
2026-01-13 16:18:32 +08:00
手瓜一十雪
c5de5e00fc Add mappings for version 3.2.23-44343 (arm64 and x64)
Updated napi2native.json to include send and recv addresses for 3.2.23-44343 on both arm64 and x64 architectures.
2026-01-09 15:33:15 +08:00
手瓜一十雪
ea7cd7f7e1 Add new version mappings to external JSON files
Updated appid.json, napi2native.json, and packet.json to include mappings for versions 9.9.26-44343 and 3.2.23-44343, including x64 and arm64 variants. This ensures compatibility with the latest application versions and platforms.
2026-01-09 13:35:15 +08:00
手瓜一十雪
cc23599776 Enhance HTTP debug UI with command palette and UI improvements
Added a new CommandPalette component for quick API selection and execution (Ctrl/Cmd+K). Refactored the HTTP debug page to use the command palette, improved tab and panel UI, and enhanced the code editor's appearance and theme integration. Updated OneBotApiDebug to support imperative methods for request body and sending, improved response panel resizing, and made various UI/UX refinements across related components.
2026-01-04 20:38:08 +08:00
手瓜一十雪
c6ec2126e0 Refactor theme font handling and preview logic
Moved font configuration to be managed via theme.css, eliminating the need for separate font initialization and caching. Updated backend to generate @font-face rules and font variables in theme.css. Frontend now uses a dedicated style tag for real-time font preview in the theme config page, and removes legacy font cache logic for improved consistency.
2026-01-04 18:48:16 +08:00
手瓜一十雪
f1756c4d1c Optimize version fetching and update logic
Introduces lazy loading for release and action artifact versions, adds support for nightly.link mirrors, and improves artifact retrieval reliability. Removes unused loginService references, refactors update logic to handle action artifacts, and streamlines frontend/backend API parameters for version selection.
2026-01-04 12:41:21 +08:00
手瓜一十雪
4940d72867 Update release workflow
Updates the release workflow to download and include NapCat.Shell.Windows.OneKey.zip in the release artifacts.
2026-01-03 18:37:17 +08:00
手瓜一十雪
91e0839ed5 Add upload_file option for file upload actions
Introduces an 'upload_file' boolean option to group and private file upload actions, allowing control over whether files are uploaded to group storage or sent directly. Updates the NTQQFileApi and OneBotFileApi to support this option and adjusts file handling logic accordingly.
2026-01-03 16:25:38 +08:00
手瓜一十雪
334c4233e6 Update message retrieval and parsing logic
Changed the protocol fallback logic to pass an additional argument to parseMessageV2 and updated message retrieval to use getMsgHistory instead of getMsgsByMsgId. This improves compatibility and ensures correct message fetching.
2026-01-03 16:05:03 +08:00
手瓜一十雪
71bb4f68f3 Improve senderUin handling in sendMsg method
If senderUin is missing or '0', attempt to retrieve it using senderUid before returning. This ensures messages are not dropped when senderUid is available but senderUin is not.
2026-01-03 16:01:24 +08:00
手瓜一十雪
47983e2915 Add PTT element type to message element filters
Updated the filtering logic in SendMsgBase to include ElementType.PTT alongside FILE, VIDEO, and ARK types. This ensures PTT elements are handled consistently with other single-element message types.
2026-01-03 15:38:13 +08:00
手瓜一十雪
ae42eed6e2 Fix font reset on unmount with unsaved changes
Added a ref to track unsaved changes and updated the cleanup logic to only restore the saved font settings if there are unsaved changes. This prevents the font from being unintentionally reset when the page is refreshed or the component is unmounted without changes.
2026-01-03 15:36:42 +08:00
手瓜一十雪
cb061890d3 Enhance artifact handling and display for action builds
Extended artifact metadata to include workflow run ID and head SHA. Updated backend to filter artifacts by environment and provide additional metadata. Improved frontend to display new artifact details and adjusted UI for better clarity.
2026-01-03 15:28:18 +08:00
手瓜一十雪
31feec26b5 Update release.yml 2026-01-03 15:11:58 +08:00
手瓜一十雪
e93cd3529f Update pr-build.yml 2026-01-03 15:10:03 +08:00
手瓜一十雪
1ad700b935 Update release workflow and documentation prompts
Refactored the release workflow to add semantic version validation, improved commit and file diff collection, and enhanced release note generation with more context and formatting. Updated release note and default documentation prompts for clarity, conciseness, and better user guidance. Fixed owner typo in workflow and improved error handling for missing tags.
2026-01-03 15:01:10 +08:00
手瓜一十雪
68c8b984ad Refactor update logging to use logger interface
Replaces all console logging in the update process with the ILogWrapper logger interface for consistent logging. Updates applyPendingUpdates to require a logger parameter and propagates this change to all relevant initialization code. Also removes duplicate and unnecessary lines in workflow YAML files.
2026-01-03 14:51:56 +08:00
手瓜一十雪
8eb1aa2fb4 Refactor GitHub tag fetching and mirror management
Replaces legacy tag fetching logic in napcat-common with a new mirror.ts module that centralizes GitHub mirror configuration, selection, and tag retrieval. Updates helper.ts to use the new mirror system and semver comparison, and exports compareSemVer for broader use. Updates workflows and scripts to generate and propagate build version information, and improves build status comment formatting for PRs. Also updates release workflow to use a new OpenAI key and model.
2026-01-03 14:42:24 +08:00
Makoto
2d3f4e696b feat: Add OB11GroupGrayTipEvent for detecting forged gray tip attacks (#1492)
* feat: Add OB11GroupGrayTipEvent for detecting forged gray tip attacks

- Add new OB11GroupGrayTipEvent class to report unknown gray tip messages
- Modify parseOtherJsonEvent to detect forged gray tips by checking senderUin
- Real system gray tips have senderUin='0', forged ones expose attacker's QQ
- Include message_id in event for downstream recall capability
- Add try/catch for JSON.parse to handle malformed content
- Use Number() for consistent type conversion

* fix: simplify logWarn to match upstream style

* fix: remove extra closing brace that broke class structure

* fix: add validation for malformed title gray tip events
2026-01-02 20:55:24 +08:00
时瑾
b241881c74 fix: 修复用户ID类型转换错误并移除不必要的标签渲染 2026-01-02 20:50:13 +08:00
时瑾
aecf33f4dc fix: close #1488 2026-01-02 17:07:39 +08:00
时瑾
dd4374389b fix: close #1435 (#1485)
* fix: close #1435

* fix: 优化视频缩略图生成和清理逻辑,处理文件不存在的情况
2026-01-01 21:41:01 +08:00
时瑾
100efb03ab fix: close #1477 (#1484) 2026-01-01 21:40:49 +08:00
时瑾
ce9482f19d feat: 优化webui界面和文件管理器 (#1472) 2026-01-01 21:40:39 +08:00
手瓜一十雪
4e37b002f9 Add support for version 9.9.26-44175 and fix import type
Added entries for version 9.9.26-44175 in appid.json, napi2native.json, and packet.json to support the new version. Also updated the import of createActionMap in napcat-plugin/index.ts to use a type-only import.
2026-01-01 10:32:59 +08:00
Nepenthe
7e7262415b 更新插件示例,修复插件打包问题 (#1486)
* fix: 修复打包错误

* fix: 完善插件模板

* Update index.ts
2025-12-31 13:58:55 +08:00
时瑾
3365211507 ci: 添加构建结果评论中的下载链接和获取 artifacts 列表功能 2025-12-29 03:14:17 +08:00
时瑾
05b38825c0 ci: 使用 type 导入 PullRequest 和 BuildStatus 类型 2025-12-29 03:01:21 +08:00
时瑾
95f4a4d37e ci: pr build 2025-12-29 02:55:11 +08:00
时瑾
cd495fc7a0 fix: close #1467 close #1471 2025-12-27 16:27:54 +08:00
时瑾
656279d74b fix: close #1463 2025-12-27 00:20:59 +08:00
手瓜一十雪
377c780d1a Comment out manual chunking for @heroui in Vite config
The manual chunking logic for '@heroui' modules in the Vite configuration has been commented out. This may be to simplify chunk splitting or address build issues related to custom chunking.
2025-12-24 19:31:52 +08:00
手瓜一十雪
aefa8985b1 Remove vite-plugin-font and related dependencies
This update removes vite-plugin-font and its associated dependencies from pnpm-lock.yaml. This likely reflects a cleanup of unused packages or a migration away from font-related build tooling.
2025-12-24 18:30:55 +08:00
手瓜一十雪
b034940dfd Remove vite-plugin-font from dependencies
Deleted the vite-plugin-font package from both the root and frontend package.json files as it is no longer required.
2025-12-24 18:29:17 +08:00
手瓜一十雪
cb8e10cc7e Add sw_template.js to build and improve service worker loading
Updated the Vite config to copy sw_template.js to the static assets during build. Modified backend to load sw_template.js from the static directory if available, falling back to the source assets if not. This ensures the service worker template is correctly served in production builds.
2025-12-24 18:20:51 +08:00
手瓜一十雪
afed164ba1 Add background-aware styling to sidebar and usage pie
Updated sidebar, navigation list, and usage pie components to adjust their styles based on the presence of a custom background image. This improves visual integration when a background image is set, ensuring text and UI elements remain readable and aesthetically consistent.
2025-12-24 18:14:04 +08:00
手瓜一十雪
a34a86288b Refactor font handling and theme config, switch to CodeMirror editor
Replaces Monaco editor with CodeMirror in the frontend, removing related dependencies and configuration. Refactors font management to support multiple formats (woff, woff2, ttf, otf) and dynamic font switching, including backend API and frontend theme config UI. Adds gzip compression middleware to backend. Updates theme config to allow font selection and custom font upload, and improves theme preview and color customization UI. Cleans up unused code and improves sidebar and terminal font sizing responsiveness.
2025-12-24 18:02:54 +08:00
手瓜一十雪
50bcd71144 Remove unused dependencies and optimize Monaco workers
Removed @simplewebauthn/browser, framer-motion, and react-responsive from dependencies as they are no longer used. Updated Monaco editor configuration to only load the JSON worker for improved performance, falling back to the basic editor worker for other languages. Refactored the new version tip UI to use Chip and Spinner instead of Button and removed unused react-icons import. Also updated Vite config to stop sharing react-icons.
2025-12-24 15:32:21 +08:00
手瓜一十雪
fa3a229827 Refactor dashboard and components, remove echarts
Replaces echarts-based usage pie chart with a custom SVG implementation, removing the echarts dependency. Improves caching for version and system info requests, simplifies page background to static elements, and switches dashboard state to use localStorage for persistence. Also removes polling from hitokoto and updates button styling in system info.
2025-12-24 13:56:34 +08:00
手瓜一十雪
e56b912bbd Remove debug log from emoji_like event handler
Eliminated a console.log statement in the 'event:emoji_like' event handler to clean up debug output.
2025-12-22 17:08:32 +08:00
手瓜一十雪
da0dd01460 Remove debug console.log statements from DebugAdapter
Eliminated several console.log statements used for debugging in the DebugAdapter and DebugAdapterManager classes to clean up console output.
2025-12-22 16:29:05 +08:00
手瓜一十雪
578dda2f17 feat: 支持免配置调试 2025-12-22 16:27:06 +08:00
手瓜一十雪
649165bf00 Redesign OneBot API debug UI and improve usability
Refactored the OneBot API debug interface for a more modern, tabbed layout with improved sidebar navigation, request/response panels, and better mobile support. Enhanced code editor, response display, and message construction modal. Updated system info and status display for cleaner visuals. Improved xterm font sizing and rendering logic for mobile. WebSocket debug page now features a unified header, status bar, and clearer connection controls. Overall, this commit provides a more user-friendly and visually consistent debugging experience.
2025-12-22 15:21:45 +08:00
手瓜一十雪
c4f7107038 Refactor update dialog for new version notification
Replaces the old update confirmation and toast logic with a new dialog-based update flow, including detailed status feedback (idle, updating, success, error) and improved user guidance. Removes react-hot-toast dependency and introduces a dedicated UpdateDialogContent component for clearer update progress and error handling.
2025-12-22 14:10:23 +08:00
手瓜一十雪
7f81bf45ee Revert "Refactor UI components for consistent styling"
This reverts commit 7e6035d98b.
2025-12-22 14:04:26 +08:00
手瓜一十雪
7e6035d98b Refactor UI components for consistent styling
Unified card and component styles across the frontend by removing background image logic and related conditional classes. Updated color schemes, shadows, and spacing for a more consistent appearance. Improved error handling and response structure in the backend update handler.
2025-12-22 13:34:59 +08:00
手瓜一十雪
2405cb03d8 Improve background and text styling in NetworkItemDisplay
Adjusted background opacity and hover effects for better visual consistency. Updated text color logic to enhance readability based on background presence.
2025-12-22 13:07:08 +08:00
手瓜一十雪
32d3ff6998 Add showType prop and remove ScrollShadow usage
Added an optional showType prop to NetworkDisplayCardProps in common_card.tsx. Removed the ScrollShadow component and replaced it with a standard div in nav_list.tsx to simplify the layout.
2025-12-22 12:32:48 +08:00
手瓜一十雪
84f0e0f9a0 Refactor UI for network cards and improve theming
Redesigned network display cards and related components for a more modern, consistent look, including improved button styles, card layouts, and responsive design. Added support for background images and dynamic theming across cards, tables, and log views. Enhanced input and select components with unified styling. Improved file table responsiveness and log display usability. Refactored OneBot API debug and navigation UI for better usability and mobile support.
2025-12-22 12:27:56 +08:00
手瓜一十雪
8697061a90 Refactor UI styles for improved consistency and clarity
Unified card backgrounds, borders, and shadows across components for a more consistent look. Enhanced table, tab, and button styles for clarity and accessibility. Improved layout and modal structure in OneBot API debug, added modal for struct display, and optimized WebSocket debug connection logic. Updated file manager, logs, network, and terminal pages for visual consistency. Refactored interface definitions for stricter typing and readability.
2025-12-22 10:38:23 +08:00
手瓜一十雪
872a3e0100 Add @heroui/divider to frontend dependencies
Added the @heroui/divider package (version ^2.2.21) to the napcat-webui-frontend dependencies in package.json and updated pnpm-lock.yaml accordingly.
2025-12-20 18:10:32 +08:00
手瓜一十雪
4fcbdc4d89 Remove music player and related context/hooks
Deleted the audio player component, songs context, and use-music hook, along with all related code and configuration. Updated affected components and pages to remove music player dependencies and UI. Also improved sidebar, background, and about page UI, and refactored site config icons to use react-icons.
2025-12-20 18:07:16 +08:00
手瓜一十雪
176af14915 Add 42941 version mappings to external JSON files
Added new entries for version 42941 to appid.json, napi2native.json, and packet.json, including mappings for x64 and arm64 architectures. This update ensures support for the latest client versions and their corresponding identifiers and packet mappings.
2025-12-05 18:29:10 +08:00
手瓜一十雪
81cf1fd98e Update wording in usage instructions in README
Clarified the instructions regarding support for integration, basic, and underlying framework issues to improve user understanding.
2025-12-01 13:28:18 +08:00
手瓜一十雪
5189099146 Add pnpm-lock.yaml and update .gitignore
Added pnpm-lock.yaml to track dependencies and removed it from .gitignore in the napcat-webui-frontend package to enable version control of the lock file.
2025-11-30 18:08:22 +08:00
手瓜一十雪
7fc17d45ba Add support for 9.9.25-42905 and 6.9.86-42905 versions
Updated appid.json, napi2native.json, and packet.json to include entries for versions 9.9.25-42905 (x64/Win) and 6.9.86-42905 (arm64/Mac), adding corresponding appid, qua, send, and recv values.
2025-11-30 12:56:24 +08:00
手瓜一十雪
c54f74609e Update version keys from 9.9.23-42744 to 9.9.25-42744
Renamed version keys in appid.json, napi2native.json, and packet.json from 9.9.23-42744(-x64) to 9.9.25-42744(-x64) to reflect the new version. Associated values remain unchanged.
2025-11-28 17:25:28 +08:00
手瓜一十雪
a2d7ac4878 Add support for new app and protocol versions
Updated appid.json, napi2native.json, and packet.json to include entries for versions 9.9.23-42744 and 6.9.86-42744, as well as their corresponding protocol mappings for x64 and arm64 architectures.
2025-11-26 19:43:14 +08:00
手瓜一十雪
fd0afa3b25 Add support for 9.9.23-42430-x64 in napi2native and packet
Updated napi2native.json and packet.json to include send and recv addresses for version 9.9.23-42430-x64, enabling compatibility with this new version.
2025-11-26 19:15:02 +08:00
手瓜一十雪
7685cc3dfc Prefix version numbers with 'v' in system info
Updated the display of current and latest version numbers in the system info component to include a 'v' prefix for consistency and clarity.
2025-11-25 23:10:10 +08:00
手瓜一十雪
f9c0b9d106 Remove leading 'v' from latest tag in getLatestTag
Updated the getLatestTag function to strip a leading 'v' character from the latest tag before returning it. This ensures tag values are returned without the 'v' prefix.
2025-11-25 23:09:53 +08:00
手瓜一十雪
d31f0a45b4 Fix version tag formatting and error handling
Update packet.ts to prefix napCatVersion with 'v' in the error message link. In vite-plugin-version.js, improve formatting of catch blocks, ensure returned tags do not include a leading 'v', and standardize fallback version to '0.0.0'.
2025-11-25 23:03:55 +08:00
huan-yp
7c701781a1 Fix URL formatting in error message for QQ version (#1396) 2025-11-25 12:59:37 +08:00
时瑾
3c612e03ff feat: close #1394 2025-11-24 12:47:04 +08:00
手瓜一十雪
f27db01145 Update onMSFSsoError signature with code and desc
The onMSFSsoError method now accepts a numeric code and a string description as parameters instead of a single unknown argument. This change clarifies the expected input for error handling.
2025-11-22 20:30:02 +08:00
手瓜一十雪
ae97cfba03 Refine types in storage clean listener and service
Updated method signatures in NodeIKernelStorageCleanListener and NodeIKernelStorageCleanService to use more specific types and parameter names. This improves type safety and code clarity, particularly for cache scanning and listener methods.
2025-11-22 19:57:18 +08:00
手瓜一十雪
162ddc1bf5 fix: #1392 & Remove update functionality from VersionInfo component
Eliminated the update button and related logic from the VersionInfo component in the about page. This includes removing the useRequest hook for updating, the toast notifications, and the Button component, simplifying the component to only display version information.
2025-11-22 18:31:42 +08:00
手瓜一十雪
afb6ef421a Add Passkey (WebAuthn) authentication support
Introduces Passkey (WebAuthn) registration and authentication to both backend and frontend. Backend adds new API endpoints, middleware exceptions, and a PasskeyHelper for credential management using @simplewebauthn/server. Frontend integrates @simplewebauthn/browser, updates login and config pages for Passkey registration and login flows, and adds related UI and controller methods.
2025-11-22 16:00:32 +08:00
手瓜一十雪
173a165c4b Add latest version check and update prompt to UI
Introduces backend and frontend logic to fetch the latest NapCat version tag from multiple sources, exposes a new API endpoint, and adds a UI prompt to notify users of new versions with an update button. Also includes minor code style improvements in dialog context.
2025-11-22 13:52:49 +08:00
手瓜一十雪
d525f9b03d Refactor and standardize share and message history APIs
Standardized field names (e.g., 'reverseOrder' to 'reverse_order', 'phoneNumber' to 'phone_number') and added new action names and classes for sharing contacts and group cards (SendArkShare, SendGroupArkShare). Deprecated old action names, updated API schemas and routes, and ensured backward compatibility for legacy fields. Updated frontend API definitions to match backend changes.
2025-11-22 13:14:46 +08:00
zeus-x99
f2ba789cc0 fix(webui-backend): 仅在启用 WebUI 时检测/更新默认密码 (#1387)
在初始化 WebUI 时,先判断  并直接返回,确保禁用状态下不再检测或更新默认密码 token。
2025-11-20 14:38:59 +08:00
手瓜一十雪
2cdc9bdc09 Add backend and frontend support for NapCat auto-update
Introduces backend API and router for updating NapCat, including update logic and pending update application on startup. Adds frontend integration with update button and request handling. Refactors system info component to remove legacy new version tip. Updates types and runtime to track working environment for update selection. Implements lazy loading for pty in unixTerminal to avoid early initialization.
2025-11-19 21:05:08 +08:00
手瓜一十雪
c123b34d5f Update ffmpeg addon binary for Darwin ARM64
Replaces the ffmpegAddon.darwin.arm64.node binary with a new version, likely to include bug fixes or performance improvements for ARM64 macOS systems.
2025-11-18 20:16:03 +08:00
手瓜一十雪
d25b43ebf2 Update ffmpeg native binaries for Linux
Replaces ffmpegAddon.linux.arm64.node and ffmpegAddon.linux.x64.node with new versions. This may include bug fixes, performance improvements, or compatibility updates for native ffmpeg functionality.
2025-11-18 17:36:09 +08:00
时瑾
8fe4a9e6ac 更新 Bug 反馈模板,增强对不当内容的说明和合规性要求 2025-11-16 15:55:25 +08:00
手瓜一十雪
09da80aad5 Remove unused dependencies from package.json
Deleted the 'dependencies' section from napcat-qrcode/package.json, removing references to napcat-core, napcat-common, napcat-onebot, and napcat-webui-backend. This streamlines the package configuration and may indicate these dependencies are no longer required.
2025-11-16 11:02:21 +08:00
手瓜一十雪
3d3f718fd5 Refactor interfaces and decouple backend dependencies
Introduced new interface definitions in napcat-common for logging, status, and subscription. Refactored napcat-webui-backend to use these interfaces, decoupling it from napcat-core and napcat-onebot. Moved OneBot config schema to backend and updated imports. Updated framework and shell to pass subscriptions to InitWebUi. Improved type safety and modularity across backend and shared packages.
2025-11-16 10:58:30 +08:00
手瓜一十雪
6068abdec0 Update VSCode settings for formatting and file nesting
Enhanced .vscode/settings.json with file nesting patterns, formatting preferences, auto-save behavior, and import specifier options for JavaScript and TypeScript. Removed old debug source map overrides to streamline workspace configuration.
2025-11-15 17:17:24 +08:00
手瓜一十雪
3957d7af5a Use environment variables for secret keys in dev and backend
Set fixed secret keys for JWT and WebUI in development environment via environment variables. Updated backend to use NAPCAT_WEBUI_SECRET_KEY and NAPCAT_WEBUI_JWT_SECRET_KEY from environment if available, improving configurability and security.
2025-11-15 17:00:52 +08:00
手瓜一十雪
a2837974fe Add VSCode launch and TailwindCSS config files
Added .vscode/launch.json for Node.js debugging and .vscode/tailwindcss.json for TailwindCSS directive support in VSCode. These files improve development workflow and editor integration.
2025-11-15 16:51:46 +08:00
手瓜一十雪
6f8edfe570 清理重复的 Vitest configuration file
Deleted vitest.config.ts, which contained test environment and path alias settings. This may indicate a change in testing strategy or migration away from Vitest.
2025-11-15 16:42:50 +08:00
手瓜一十雪
0b655db4dd Add test step to build workflow
Inserts 'pnpm test' into both build jobs in the GitHub Actions workflow to ensure tests are run during CI before building artifacts.
2025-11-15 16:25:06 +08:00
手瓜一十雪
d800466a30 Move inversify and reflect-metadata to napcat-core
Transferred 'inversify' and 'reflect-metadata' dependencies from the root package.json to packages/napcat-core/package.json to better scope dependencies and improve project organization.
2025-11-15 16:23:03 +08:00
手瓜一十雪
fa80441e36 Add ESLint config and update code style
Introduced a new eslint.config.js using neostandard and added related devDependencies. Updated codebase for consistent formatting, spacing, and function declarations. Minor refactoring and cleanup across multiple files to improve readability and maintain code style compliance.
2025-11-15 16:21:59 +08:00
手瓜一十雪
1990761ad6 Update main entry and tsconfig for JS support
Changed package.json main and exports to use index.js instead of index.cjs. Updated tsconfig.json to allow JavaScript files and expanded include patterns to support both JS and TS files.
2025-11-15 16:09:26 +08:00
手瓜一十雪
ef63812391 Add napcat-test package and Vitest setup
Introduces the napcat-test package with initial SHA-1 stream tests, configuration files, and scripts for running tests. Updates root package.json to include test commands and Vitest dependencies, and adds Vitest configuration at the root and package level for test environment setup.
2025-11-15 16:05:09 +08:00
手瓜一十雪
0f033b0ac8 Remove performance monitor module
Deleted performance-monitor.ts from napcat-common, removing the function statistics and reporting utility. This may indicate a refactor or replacement of performance monitoring functionality.
2025-11-15 15:13:56 +08:00
手瓜一十雪
9fdef3cde9 Revert "Update default model in release workflow"
This reverts commit d32ccc6eb5.
2025-11-15 14:56:34 +08:00
手瓜一十雪
20e8643193 Fix import paths for require_dlopen module
Updated import statements in prebuild-loader.ts and windowsPtyAgent.ts to use relative path './index' for require_dlopen. This resolves incorrect module resolution issues.
2025-11-15 14:43:08 +08:00
手瓜一十雪
8645ed4d9d Update tsconfig to include typeRoots and format paths
Added 'typeRoots' to specify custom type definitions directory and reformatted the 'paths' property for better readability. This improves TypeScript type resolution and project maintainability.
2025-11-15 14:40:06 +08:00
手瓜一十雪
c0b9817ff5 Add type checking to build workflow
Incorporates 'pnpm run typecheck' into the build steps for both frontend and shell jobs to ensure type safety during CI builds.
2025-11-15 14:01:11 +08:00
手瓜一十雪
b147e57c1c Refactor TypeScript configs to use shared base
Introduced tsconfig.base.json for shared TypeScript configuration and updated all package tsconfig.json files to extend from it, reducing duplication and improving maintainability. Also updated typecheck script in package.json and fixed import in prebuild-loader.ts.
2025-11-15 14:00:27 +08:00
手瓜一十雪
ad4a108781 feat: 大规模去耦合
Moved various helper, event, and utility files from napcat-common to napcat-core/helper for better modularity and separation of concerns. Updated imports across packages to reflect new file locations. Removed unused dependencies from napcat-common and added them to napcat-core where needed. Also consolidated type definitions and cleaned up tsconfig settings for improved compatibility.
2025-11-15 13:36:33 +08:00
手瓜一十雪
df824d77ae feat: 所有的类型检查 2025-11-15 12:57:19 +08:00
手瓜一十雪
19888d52dc Remove unused test utility files
Deleted test.ts and test2.ts from utils as they are no longer needed for the project.
2025-11-15 12:02:07 +08:00
手瓜一十雪
4dc8b3ed3b Refactor FileApi usage to obContext in OneBot
Replaced references to `core.apis.FileApi` with `obContext.apis.FileApi` across actions and message API to ensure consistent context usage. Added FileApi to the ApiListType and initialized it in NapCatOneBot11Adapter. This improves maintainability and context handling for file-related operations.
2025-11-15 11:20:01 +08:00
手瓜一十雪
8df54d5cd3 feat: 去core耦合onebot
Moved file element creation methods (for file, picture, video, and ptt) from napcat-core/apis/file.ts to a new OneBotFileApi class in napcat-onebot/api/file.ts. Updated package.json dependencies to remove unused packages and fix workspace references. This improves separation of concerns and modularity between core and onebot-specific logic.
2025-11-15 11:17:57 +08:00
手瓜一十雪
aa982b3071 Improve version folder selection in loadNapCat.cjs
Enhanced logic to handle multiple or missing version folders by selecting the most recently modified folder if more than one exists, and providing clearer error messages. Also updated .vscode/settings.json to add source map path overrides for additional packages.
2025-11-15 10:58:33 +08:00
手瓜一十雪
8e71dec63a Bind TypedEventEmitter to DI container and update usage
Added TypedEventEmitter to the dependency injection container and exposed it in ServiceBase. Updated OlPushService to use the injected event emitter instead of directly importing appEvent. Also performed minor code style improvements for consistency.
2025-11-15 10:48:59 +08:00
手瓜一十雪
31bb1e5dee Add emoji like event handling to core and onebot
Introduces a typed event emitter for app events in napcat-core, specifically for emoji like events in groups. OlPushService now emits 'event:emoji_like' when a group reaction is detected. napcat-onebot listens for this event and emits corresponding OneBot events. Refactors and adds missing type definitions and improves method formatting for consistency.
2025-11-15 10:45:02 +08:00
手瓜一十雪
75e1e8dd79 Improve error handling in release workflow
Enhances the OpenRouter API call step by adding error handling for both curl and jq failures. If the API call or response parsing fails, the workflow now falls back to using a default release note template.
2025-11-14 23:00:20 +08:00
手瓜一十雪
d32ccc6eb5 Update default model in release workflow
Changed the OPENROUTER_MODEL environment variable from 'kimi-k2-0905-turbo' to 'glm-4.6-turbo' in the release workflow configuration.
2025-11-14 22:45:17 +08:00
手瓜一十雪
7b3e94d568 Add raw response output to release workflow
Added echo statements to display the raw API response in the release workflow for improved debugging and visibility.
2025-11-14 22:35:58 +08:00
手瓜一十雪
5cfe479044 Refactor createListenerFunction type and usage
Simplifies the generic type signature of createListenerFunction and updates the way createEventFunction is called, removing unnecessary type parameters and using a direct function call with TypeScript ignore for type checking.
2025-11-14 22:24:50 +08:00
手瓜一十雪
f04ffa5dc6 Add service handler registration and DI support
Introduces dependency injection via Inversify and reflect-metadata, adds a service handler registry for packet handling, and updates core initialization to auto-register and bind service handlers. Also updates Vite configs and auto-include logic to support protocol service files.
2025-11-14 22:20:33 +08:00
手瓜一十雪
a2a73ce2dd Add dev build script and improve Vite config
Introduces a 'build:shell:dev' script for development builds and updates napcat-shell's Vite config to conditionally enable source maps in development mode. This enhances build flexibility for development and production environments.
2025-11-14 21:25:29 +08:00
手瓜一十雪
66d02eeb6a Enable source maps and improve debugging support
This commit enables source maps in napcat-shell's Vite config for better debugging, adds source map path overrides to VSCode settings, and updates nodeTest.ps1 to launch with the Node.js inspector. The autoIncludeTSPlugin transform now returns a source map for improved breakpoint support in VSCode. Also adds a sources.txt file listing project and dependency sources.
2025-11-14 21:21:49 +08:00
手瓜一十雪
b99c0ca437 Set fetch-depth to 0 in release workflow
Configures the checkout step in the release workflow to use fetch-depth: 0, ensuring the full git history is available for subsequent steps.
2025-11-14 20:06:01 +08:00
手瓜一十雪
019b90984d Add napcat-vite dependency and plugin integration
Added 'napcat-vite' as a workspace dependency in package.json and integrated its version plugin into the Vite configuration. This enables version management features provided by napcat-vite for the framework.
2025-11-14 19:54:33 +08:00
手瓜一十雪
5043a49779 feat: 装饰器与装饰器路由注册 2025-11-14 19:49:13 +08:00
手瓜一十雪
36aa08a8f5 Replace nap-proto-core with napcat-protobuf package
Switched all imports from '@napneko/nap-proto-core' to the new 'napcat-protobuf' package across napcat-core and related packages. Updated dependencies and references to support the new package structure, improving maintainability and workspace integration.
2025-11-14 16:19:26 +08:00
手瓜一十雪
8bc8df32f9 Update type declarations and remove ts-ignore comments
Added 'types' field to package.json and updated tsconfig.json to include .d.ts files for better type support in napcat-pty. Removed unnecessary @ts-ignore comments from terminal_manager.ts to improve code clarity.
2025-11-14 15:00:40 +08:00
手瓜一十雪
bc183ae002 Remove unnecessary ts-ignore comments and improve typings
Removed redundant // @ts-ignore comments from converter.ts and http-server.ts. Enhanced type safety in event.ts by refining generic parameters for createListenerFunction and createEventFunction.
2025-11-14 14:53:21 +08:00
手瓜一十雪
b85f9197e3 Remove unused path aliases from tsconfig files
Deleted redundant or unused path alias entries from tsconfig.json in napcat-core, napcat-onebot, and napcat-plugin packages to simplify TypeScript configuration and avoid confusion.
2025-11-14 14:37:43 +08:00
手瓜一十雪
c8fd66fa9b Refactor imports to use package names instead of aliases
Replaced all path alias imports (e.g., '@/napcat-core') with direct package imports (e.g., 'napcat-core/index') across napcat-common, napcat-core, and napcat-webui-backend. This improves compatibility with tooling and workspace resolution, and aligns with standard TypeScript/Node.js import practices.
2025-11-14 14:34:27 +08:00
手瓜一十雪
6e9f448a0c Add feature request issue template
Introduces a new GitHub issue template for submitting feature requests and improvement suggestions for NapCat. The template guides users to provide relevant version information, detailed descriptions, background, and expected outcomes.
2025-11-14 13:15:01 +08:00
手瓜一十雪
142016778f Rename workflow job to node-shell-docker
Changed the job name from 'trigger-napcat-release' to 'node-shell-docker' in the trigger-docker-publish.yml workflow for improved clarity.
2025-11-14 13:12:21 +08:00
手瓜一十雪
159fb8cd3a Rename workflow job from Build-LiteLoader to Build-Framework
Updated workflow job names and dependencies in auto-release.yml, build.yml, and release.yml from 'Build-LiteLoader' to 'Build-Framework' for clarity and consistency. Also updated bug report template to reference version location in WebUI instead of settings page.
2025-11-14 13:05:29 +08:00
手瓜一十雪
01c911e178 Add workflows to trigger NapCat release builds
Introduces two new GitHub Actions jobs: one to trigger NapCat AppImage builds and another for NapCat Linux Node Loader releases. Both jobs fetch the latest NapCat version tag and use fixed QQ AppImage URLs for x86_64 and arm64 architectures.
2025-11-14 13:02:16 +08:00
手瓜一十雪
8b3ea8dcef Add workflow to trigger framework Docker publish
Introduces a new job in the GitHub Actions workflow to trigger the docker-image publish workflow for the NapCat.Docker.Framework repository. This enables automated publishing of the framework Docker image alongside the existing NapCat-Docker image.
2025-11-14 12:48:46 +08:00
手瓜一十雪
fe8b270ab3 Add workflow to trigger Docker publish on release
Introduces a GitHub Actions workflow that triggers the NapCat-Docker docker-publish workflow when a release is published. This automates Docker image publishing upon new releases.
2025-11-14 12:44:26 +08:00
手瓜一十雪
f02ae5894f Update QQ x64 download URL in auto-release workflow
Replaces dynamic retrieval of the QQ x64 download URL with a static direct link in the auto-release GitHub Actions workflow. This change improves reliability by avoiding proxy and parsing steps.
2025-11-14 12:39:15 +08:00
手瓜一十雪
a6a0b408af Update QQ x64 download URL in release workflow
Changed the QQ x64 config JS URL to use a proxy service instead of the direct CDN link in the auto-release workflow. This may help bypass access restrictions or improve reliability.
2025-11-14 12:37:32 +08:00
手瓜一十雪
e9856ac80f Refactor version API to GetNapCatVersion endpoint
Replaces the PackageInfo API and related frontend usage with a dedicated GetNapCatVersion endpoint that returns only the NapCat version string. Updates backend handler, helper, types, router, and frontend components to use the new API for improved clarity and separation of concerns.
2025-11-14 12:10:57 +08:00
手瓜一十雪
f553f9dc8d fix: webui version 2025-11-14 11:51:23 +08:00
手瓜一十雪
5608638e9a Update OpenRouter API URL and model in workflow
Changed the OpenRouter API endpoint and model in the auto-release GitHub Actions workflow to use new values. This may reflect a migration to a different service or model for automated releases.
2025-11-13 21:02:28 +08:00
手瓜一十雪
a53c20767a Update artifact zipping to exclude parent folder
Changed the zip commands in the auto-release workflow to zip the contents of each artifact directory rather than the directory itself. This ensures the resulting zip files do not include an extra parent folder.
2025-11-13 20:59:31 +08:00
手瓜一十雪
a92bef5b33 Update release note prompt and workflow wording
Clarified instructions in release_note_prompt.txt to use the provided version number and updated the example version. Modified auto-release.yml to specify '当前真正的版本' in the user content for improved clarity.
2025-11-13 20:54:19 +08:00
手瓜一十雪
a9a3b6ec6e Copy QQNT.dll in auto-release workflow
Adds a step to copy QQNT.dll from napcat-develop to the output directory in the auto-release workflow. Ensures the DLL is included in release artifacts.
2025-11-13 20:51:29 +08:00
手瓜一十雪
20d41fff9e Update output directory and file copy paths in release workflow
Changed output directory from 'napcat' to 'NapCat.Shell.Windows.Node' and updated all related file copy and artifact upload paths to match. This aligns the workflow with the new directory structure and ensures correct packaging of release artifacts.
2025-11-13 20:45:51 +08:00
手瓜一十雪
0b4d7e1346 Remove unused dependencies from package.json
Deleted workspace dependencies from napcat-vite/package.json, likely because they are no longer required or managed elsewhere.
2025-11-13 20:09:44 +08:00
手瓜一十雪
46b9049a24 Update user content format in auto-release workflow
Changed the user content string in the auto-release workflow to use '当前版本' instead of 'TAG' for improved clarity in release notes.
2025-11-13 20:04:31 +08:00
手瓜一十雪
521f4dc365 Include Windows Node shell in release workflow
Adds NapCat.Shell.Windows.Node to the release process by updating dependencies, packaging steps, and release artifacts in auto-release.yml.
2025-11-13 20:02:42 +08:00
手瓜一十雪
04b507d749 Add debug output for OpenRouter request body
Prints the OpenRouter API request body in the workflow using jq for easier debugging and inspection of outgoing requests.
2025-11-13 19:59:36 +08:00
手瓜一十雪
5638127813 Add default prompt and update release workflow
Added .github/prompt/default.md with deployment instructions and download links. Updated auto-release.yml to copy NapCat.Shell directory contents instead of unzipping the archive.
2025-11-13 19:58:10 +08:00
手瓜一十雪
30a7797ba9 Reduce aria2c parallelism in auto-release workflow
Changed aria2c download options from 16 connections to 1 in the auto-release workflow. This may help avoid issues with rate limiting or unstable downloads from the Node.js distribution server.
2025-11-13 19:53:05 +08:00
手瓜一十雪
d09a82b1b8 Add Windows packaging workflow and NapCat entry files
Introduces a new GitHub Actions job to automate packaging NapCat for Windows, including downloading dependencies and assembling artifacts. Adds napcat.bat and index.js entry files for Windows distribution in packages/napcat-develop.
2025-11-13 19:48:15 +08:00
手瓜一十雪
85b5c881ba Add napcat-develop package and update scripts
Introduces the napcat-develop package with its own package.json and tsconfig.json. Updates build and dev scripts in the root package.json, modifies loadNapCat.cjs to adjust paths and output directories, and updates nodeTest.ps1 to use the correct script path.
2025-11-13 19:30:33 +08:00
手瓜一十雪
eebce222cf Update release note prompt formatting and examples
Changed the commit id placement in update items for clarity and added more example update entries to guide contributors in writing release notes.
2025-11-13 19:20:13 +08:00
手瓜一十雪
ec5ca5d89a Improve tag handling in auto-release workflow
Switches tag retrieval to use the GitHub API and sorts tags with jq for more reliable ordering. Adds explicit GITHUB_OWNER and GITHUB_REPO variables, improves previous tag selection logic, and ensures tags are fetched before generating release notes. Also adds more informative logging for debugging.
2025-11-13 19:16:12 +08:00
手瓜一十雪
31a7767ae4 Improve previous tag detection in auto-release workflow
Replaces 'git describe' with logic to find the previous tag by sorting all tags by creation date. This ensures accurate detection of the previous tag for release note generation and adds error handling if no previous tag is found.
2025-11-13 19:06:06 +08:00
手瓜一十雪
fec024334a Update release note prompt formatting instructions
Clarified that output must strictly follow the NapCat release note format and example. Updated constraints and replaced the example section with a real example for better guidance.
2025-11-13 19:03:28 +08:00
手瓜一十雪
2ad2af4d7c Trigger release workflow on tag push
Changed the workflow trigger from pushes to the main branch to pushes of any tag. This enables releases to be automatically created when a new tag is pushed.
2025-11-13 18:57:37 +08:00
手瓜一十雪
2a160d296f Remove tag trigger from auto-release workflow
The workflow will no longer be triggered by tag pushes, only by pushes to the main branch.
2025-11-13 18:57:20 +08:00
手瓜一十雪
e43f229e04 Update import paths to use direct module references
Changed import statements from alias-based paths (e.g., '@/napcat-common/store') to direct module references (e.g., 'napcat-common/src/store') in Proxy.ts, Status.ts, Data.ts, and SignToken.ts for improved compatibility and clarity.
2025-11-13 18:56:51 +08:00
手瓜一十雪
9158ebc136 Update workflow name to AI RELEASE NapCat
Renamed the GitHub Actions workflow from 'Build Action' to 'AI RELEASE NapCat' for improved clarity and identification.
2025-11-13 18:55:01 +08:00
手瓜一十雪
d758fe3a2b Improve release note generation in workflow
Enhances the release note generation step to compare commits between the previous and current tags, includes commit bodies and authors, and formats output for better readability. Also adds more robust extraction and output handling for the generated release notes.
2025-11-13 18:53:19 +08:00
手瓜一十雪
4abd0668a3 Refactor auto-release workflow for clarity and reliability
Renamed workflow, improved job and step naming, and streamlined artifact zipping and release note generation. Switched to using softprops/action-gh-release for release creation and asset upload, removed manual commit list preparation, and enhanced error handling for release note generation. Updated permissions and environment variables for consistency.
2025-11-13 18:46:48 +08:00
手瓜一十雪
0181700c3b feat: 自动化version打包 2025-11-13 18:31:55 +08:00
手瓜一十雪
6083e9cfcc feat: 以后仅维护napCatVersion 2025-11-13 16:14:31 +08:00
手瓜一十雪
5fec190c70 Remove package-lock.json
Deleted the package-lock.json file, possibly to reset dependency lock state or switch package managers. Ensure to regenerate lock file if needed for consistent dependency management.
2025-11-13 16:12:00 +08:00
手瓜一十雪
e1743ae5e4 Switch build and release workflows to npm install
Replaces pnpm install with npm install --omit=dev in both framework and shell build steps for build and release workflows. Removes package-lock.json after installation to avoid including it in artifacts.
2025-11-13 15:57:05 +08:00
手瓜一十雪
ded921c55e Update pnpm install flags in CI workflows
Replaces '--production' with '--prod --shamefully-hoist' in build and release GitHub Actions workflows to improve dependency installation compatibility.
2025-11-13 15:53:37 +08:00
手瓜一十雪
57e717e898 Update artifact paths in build and release workflows
Artifacts for NapCat.Framework and NapCat.Shell are now moved to 'framework-dist' and 'shell-dist' directories before upload. This change standardizes output locations and updates the upload paths accordingly in both build.yml and release.yml.
2025-11-13 15:49:11 +08:00
手瓜一十雪
55f21c6caa Install pnpm globally in build and release workflows
Added 'npm i -g pnpm' to both build and release GitHub Actions workflows to ensure pnpm is available before running installation and build commands.
2025-11-13 15:42:05 +08:00
手瓜一十雪
4360775eff refactor: 整体重构 (#1381)
* feat: pnpm new

* Refactor build and release workflows, update dependencies

Switch build scripts and workflows from npm to pnpm, update build and artifact paths, and simplify release workflow by removing version detection and changelog steps. Add new dependencies (silk-wasm, express, ws, node-pty-prebuilt-multiarch), update exports in package.json files, and add vite config for napcat-framework. Also, rename manifest.json for framework package and fix static asset copying in shell build config.
2025-11-13 15:39:42 +08:00
手瓜一十雪
e2486606f9 feat: 正式终止once支持 2025-11-13 11:22:46 +08:00
Mlikiowa
f8eb368cdb release: v4.9.42 2025-11-13 02:48:59 +00:00
手瓜一十雪
9ce51fb082 fix: #1380 2025-11-13 10:47:40 +08:00
Mlikiowa
89e50be1e9 release: v4.9.41 2025-11-13 01:28:44 +00:00
手瓜一十雪
1259dcea5b Add new app, packet, and native mappings for 42086 builds
Updated appid.json, packet.json, and napi2native.json to include mappings for new 42086 builds across multiple platforms. Added sendMsfRequest method to NodeIKernelMSFService interface and made minor formatting improvements. Commented out a debug method in NodeIDependsAdapter.
2025-11-13 09:28:17 +08:00
手瓜一十雪
b4900066b3 fix: #1367 2025-11-12 13:50:35 +08:00
Mlikiowa
28acd94cbd release: v4.9.40 2025-11-12 05:38:39 +00:00
手瓜一十雪
7aedacb27f feat: png video截图 2025-11-12 13:38:08 +08:00
手瓜一十雪
c9b45ec1a2 Fix busiId comparison in parsePrivateMsgEvent
Ensure busiId is compared as a string in parsePrivateMsgEvent to handle cases where busiId may not be a string type. This prevents potential mismatches due to type differences.
2025-11-12 12:54:09 +08:00
Mlikiowa
54cacc30e4 release: v4.9.38 2025-11-12 04:20:16 +00:00
手瓜一十雪
9b26fc99d3 fix 2025-11-12 12:19:39 +08:00
手瓜一十雪
204846b404 Add success log messages to packet handler
Added log statements to indicate successful loading and initialization in NativePacketHandler. This improves traceability and debugging by providing clear feedback in the logs.
2025-11-12 12:04:48 +08:00
手瓜一十雪
3d654791b9 Add platform and arch to native module path
Prepends process.platform and process.arch to the native MoeHoo module filename, ensuring the correct binary is loaded for the current environment.
2025-11-12 12:00:36 +08:00
手瓜一十雪
63f42f1592 Improve native packet handler loading and error checks
Adds a 'loaded' flag to track native module loading status and improves error handling during initialization. The constructor now attempts to load the native module and logs warnings or errors if loading fails, while the init method checks the loaded status before proceeding.
2025-11-12 11:59:06 +08:00
Mlikiowa
0ab0b939da release: v4.9.36 2025-11-11 12:30:58 +00:00
手瓜一十雪
522a123f9a Update native MoeHoo binaries for Linux
Replaces MoeHoo.linux.arm64.node and MoeHoo.linux.x64.node with new versions. This may include bug fixes, performance improvements, or compatibility updates for the native packet module.
2025-11-11 20:30:33 +08:00
Mlikiowa
ec9f8d6e12 release: v4.9.35 2025-11-10 04:04:07 +00:00
手瓜一十雪
3c750c75a9 Add Linux native modules for arm64 and x64
Added prebuilt native binaries MoeHoo.linux.arm64.node and MoeHoo.linux.x64.node to support Linux on arm64 and x64 architectures.
2025-11-10 12:03:42 +08:00
手瓜一十雪
5b2b1f499b Remove Linux native packet binaries
Deleted MoeHoo.linux.arm64.node and MoeHoo.linux.x64.node from src/native/packet. These binaries are no longer needed or are being replaced.
2025-11-10 12:02:58 +08:00
手瓜一十雪
531ffcd55d Update MoeHoo.linux.x64.node binary
Replaces the MoeHoo.linux.x64.node native module with a new version. Details of the changes depend on the updated binary implementation.
2025-11-10 12:02:45 +08:00
Mlikiowa
068e4d8bb5 release: v4.9.33 2025-11-09 04:54:38 +00:00
手瓜一十雪
5dc33e78ad Add napiloader scripts and binaries, update Vite config
Introduces napiloader batch scripts and binaries (napiloader.dll, napimain.exe) for Windows integration. Updates vite.config.ts to include napiloader files in the Framework build output and refactors plugin target lists for improved distribution packaging.
2025-11-09 12:54:15 +08:00
Mlikiowa
d76a2170a0 release: v4.9.32 2025-11-08 04:26:49 +00:00
手瓜一十雪
ec2af3120c Refactor FFmpeg file conversion logic and API
Unified file conversion in FFmpegAddonAdapter to use decodeAudioToFmt for all formats, updated FFmpeg interface and service to support new conversion method, and added adapter name checks in GetRecord and DownloadFileRecordStream for optimized conversion flow. Updated native addon binaries to support these changes.
2025-11-08 12:22:51 +08:00
Mlikiowa
8de49a3109 release: v4.9.30 2025-11-08 02:30:01 +00:00
手瓜一十雪
dbb5a0022e Update ffmpeg native binaries for all platforms
Replaces ffmpegAddon binaries for Darwin ARM64, Linux ARM64, Linux x64, and Windows x64 with new versions. Ensures compatibility and includes latest native changes.
2025-11-08 10:28:59 +08:00
Mlikiowa
cb8c8d6b57 release: v4.9.29 2025-11-07 12:01:08 +00:00
手瓜一十雪
93c140ed4e Improve error message for group notice image upload
Enhanced the error message when group notice image upload fails to include the error details from the upload API response.
2025-11-07 20:00:17 +08:00
手瓜一十雪
457b072f0e Update NTQQ build version check to 41679
Changed the minimum NTQQ build version required for buddy list operations from 40990 to 41679 in NTQQFriendApi. Ensures compatibility with newer NTQQ builds.
2025-11-07 19:57:07 +08:00
手瓜一十雪
1869493473 Update getBuddyV2ExWithCate for NTQQ build compatibility
Modified getBuddyV2ExWithCate to conditionally call getBuddyListV2 with an extra boolean argument for NTQQ builds >= 40990, ensuring compatibility with different API signatures.
2025-11-07 19:50:11 +08:00
Mlikiowa
f33c66ce15 release: v4.9.28 2025-11-07 11:35:58 +00:00
手瓜一十雪
e8d6f86458 feat: 修复一些问题 2025-11-07 19:35:35 +08:00
手瓜一十雪
a000ffdf0d fix: buddylist 2025-11-07 19:29:39 +08:00
Mlikiowa
202338a160 release: v4.9.27 2025-11-07 09:05:14 +00:00
手瓜一十雪
e3eb129a52 Update ffmpeg native binaries for all platforms
Replaces ffmpegAddon binaries for Darwin ARM64, Linux ARM64, Linux x64, and Windows x64 with new versions. Ensures compatibility and includes latest native changes.
2025-11-07 17:04:29 +08:00
手瓜一十雪
7654e9f2bb Update ffmpeg native binaries for all platforms
Replaces ffmpegAddon binaries for Darwin ARM64, Linux ARM64, Linux x64, and Windows x64. Ensures compatibility and includes latest native code updates.
2025-11-07 12:54:36 +08:00
Mlikiowa
a60c03f42f release: v4.9.26 2025-11-06 15:15:20 +00:00
手瓜一十雪
60aae228a1 feat: add Node NapCat Test 2025-11-06 17:57:57 +08:00
手瓜一十雪
b1417f9b56 feat: support node test 2025-11-06 10:57:54 +08:00
手瓜一十雪
eeeaddbb60 Remove development guide from README
Deleted the section detailing code checks and TypeScript error notes prior to code submission. This streamlines the README and removes internal development instructions.
2025-11-05 12:24:21 +08:00
Mlikiowa
b1109022bb release: v4.9.25 2025-11-04 13:55:32 +00:00
手瓜一十雪
1807789511 Add send and recv mappings for 3.2.21-41857-arm64
Updated napi2native.json to include the 'send' and 'recv' addresses for the 3.2.21-41857-arm64 version, which were previously missing.
2025-11-04 21:55:09 +08:00
手瓜一十雪
49a5b631c2 Add support for 41857 app versions and update mappings
Added new entries for version 41857 in appid.json, napi2native.json, and packet.json for Windows, Linux, and Mac platforms. Updated mappings for send/recv addresses to support the latest application versions.
2025-11-04 21:23:41 +08:00
Mlikiowa
e7aaec81e2 release: v4.9.24 2025-11-04 09:53:36 +00:00
phelogges
05f8e8f3c3 fix: 修复了Unix终端打开失败的bug (#1355)
Bug复现:
mlikiowa/napcat-docker:v4.9.23,登陆账号后,在WebUI中打开系统终端失败,查看容器日志报错如下
Failed to create terminal: TypeError: Cannot read properties of undefined (reading 'fork')
    at new UnixTerminal (file:///app/napcat/napcat.mjs:67721:22)
    at spawn (file:///app/napcat/napcat.mjs:67873:10)
    at TerminalManager.createTerminal (file:///app/napcat/napcat.mjs:67963:17)
    at CreateTerminalHandler (file:///app/napcat/napcat.mjs:68069:36)
    at Layer.handleRequest (/app/napcat/node_modules/router/lib/layer.js:152:17)
    at next (/app/napcat/node_modules/router/lib/route.js:157:13)
    at Route.dispatch (/app/napcat/node_modules/router/lib/route.js:117:3)
    at handle (/app/napcat/node_modules/router/index.js:435:11)
    at Layer.handleRequest (/app/napcat/node_modules/router/lib/layer.js:152:17)
    at /app/napcat/node_modules/router/index.js:295:15

定位到源码https://github.com/NapNeko/NapCatQQ/blob/main/src/pty/prebuild-loader.ts#L5
注意到源码中的pty.node路径与容器中实际不符,修改为正确的路径

验证测试:
笔者没有重新构建,而是保持代码逻辑,反过来将pty.node的路径复制到代码中要求的位置,测试发现bug修复

Extra:
注意到native模块中不止有pty模块,还有ffmpeg等其他模块,笔者没有继续看其他模块的加载情况了,如有必要可能需要确认一并load路径
2025-11-04 17:51:46 +08:00
Mlikiowa
e760876470 release: v4.9.23 2025-11-03 15:18:57 +00:00
手瓜一十雪
e0ec4d4ebb Refactor busiId type and comparisons in group API
Changed the type of jsonGrayTipElement.busiId to always be a string in element types. Updated related group API logic to compare busiId as a string directly, improving type consistency and reducing unnecessary conversions.
2025-11-03 23:18:34 +08:00
Mlikiowa
79fa0ade0d release: v4.9.22 2025-11-03 15:13:03 +00:00
手瓜一十雪
652b5d6118 Enhance GrayTip JSON event handling and types
Added the XmlToJsonParam interface and extended the GrayTipElement type to support additional JSON event fields. Updated group API logic to handle busiId as string or number and improved event type checks for robustness.
2025-11-03 23:11:48 +08:00
手瓜一十雪
f4dedf4803 Refactor Store to use per-key timers for expiration
Simplifies the Store implementation by removing batch expiration scanning and using per-key setTimeout timers for key expiration. This change improves code clarity and ensures more precise key expiration handling.
2025-11-03 17:06:44 +08:00
时瑾
06f6a542f5 refactor: 优化eslint配置,提升代码质量 (#1341)
* feat: 统一并标准化eslint

* lint: napcat.webui

* lint: napcat.webui

* lint: napcat.core

* build: fix

* lint: napcat.webui

* refactor: 重构eslint

* Update README.md
2025-11-03 16:30:45 +08:00
Mlikiowa
d5b8f886d6 release: v4.9.21 2025-11-02 13:33:28 +00:00
手瓜一十雪
97b6dccc30 fix: 修复 linux 音频转换问题 2025-11-02 21:33:03 +08:00
Mlikiowa
a755487e22 release: v4.9.20 2025-11-02 03:30:09 +00:00
手瓜一十雪
5407392f08 Update appid, packet, and napi2native configs for 41785
Added new entries for version 41785 in appid.json, napi2native.json, and packet.json to support updated app and protocol versions. Also updated the napi2native.darwin.arm64.node binary to match the new version.
2025-11-02 11:29:45 +08:00
Mlikiowa
3359c5ded9 release: v4.9.19 2025-11-01 15:23:54 +00:00
手瓜一十雪
caaf8be3b2 Refactor PCM conversion to return result and sample rate
Updated the FFmpeg adapter interfaces and implementations so that PCM conversion methods now return an object containing the conversion result and sample rate, instead of a Buffer. Adjusted audio processing logic to accommodate this change and improved error logging. Updated native ffmpeg addon binaries.
2025-11-01 23:23:15 +08:00
手瓜一十雪
28ce5d3cb4 Add storeID and otherBusinessInfo to PttElement
Extended the PttElement interface and related code to include storeID and otherBusinessInfo fields, supporting additional metadata for PTT elements. Also fixed minor formatting issues in function parameter spacing.
2025-11-01 22:49:21 +08:00
手瓜一十雪
3dd56c711e Throw original error in sendMsg method
Replaces the re-throwing of a new Error with the original error object in the sendMsg method, preserving the original error stack and type for better debugging.
2025-11-01 22:32:26 +08:00
Mlikiowa
511fb82ce0 release: v4.9.18 2025-11-01 13:54:19 +00:00
手瓜一十雪
1e5524a009 Add message sequence support for emoji like events
Updated group API and OB11GroupMsgEmojiLikeEvent to include an optional message sequence (msgSeq/messageSeq) parameter. This allows more precise identification of messages when handling emoji like events in group chats.
2025-11-01 21:53:53 +08:00
手瓜一十雪
d5e6afc7b9 feat: 支持不是自己的表情回应 2025-11-01 21:00:34 +08:00
Mlikiowa
91e633b0fb release: v4.9.17 2025-11-01 10:43:55 +00:00
手瓜一十雪
397b9880b9 feat: arm64 enable neon 2025-11-01 18:43:30 +08:00
Mlikiowa
54eb26ba67 release: v4.9.16 2025-11-01 08:19:18 +00:00
手瓜一十雪
355f7fb4a0 Update napi2native mappings for new and existing versions
Added mappings for versions 6.9.82-40824-arm64 and 6.9.82-40768-arm64. Updated 'send' addresses for versions 6.9.82-40990-arm64 and 6.9.83-41679-arm64 to reflect new offsets.
2025-11-01 16:18:51 +08:00
手瓜一十雪
325c455e38 Update native Linux ARM64 binary
Replaced the napi2native.linux.arm64.node binary with a new version. This may include bug fixes, performance improvements, or compatibility updates for ARM64 Linux systems.
2025-11-01 16:14:18 +08:00
Mlikiowa
a7a9792efe release: v4.9.15 2025-11-01 06:08:11 +00:00
时瑾
4bab93e545 fix: close #1334 2025-11-01 14:07:41 +08:00
手瓜一十雪
3c60997b1c Improve error handling in NCoreInitShell session creation
Refactored session creation logic to add nested try-catch blocks. Now logs specific errors for both StartupSession and Session creation failures, and throws if session creation fails.
2025-11-01 11:11:10 +08:00
Mlikiowa
bf684d9166 release: v4.9.14 2025-10-31 08:53:45 +00:00
手瓜一十雪
5c4ee30f37 Update ffmpeg native binaries for Linux
Replaced ffmpegAddon.linux.arm64.node and ffmpegAddon.linux.x64.node with new versions. This likely includes bug fixes, performance improvements, or compatibility updates for the native ffmpeg bindings.
2025-10-31 16:53:11 +08:00
Mlikiowa
346374b442 release: v4.9.13 2025-10-31 07:15:49 +00:00
手瓜一十雪
cc8a387bde re: glibc 2025-10-31 15:12:46 +08:00
Mlikiowa
962685ade6 release: v4.9.11 2025-10-31 06:43:40 +00:00
手瓜一十雪
36fdaac406 feat: 适配41697 2025-10-31 14:42:46 +08:00
Mlikiowa
db16db911b release: v4.9.10 2025-10-31 04:38:12 +00:00
手瓜一十雪
bb84dfcc27 Add new version mappings to external JSON configs
Updated appid.json, napi2native.json, and packet.json to include new version entries for 6.8.83-41679, 6.9.82-40990-arm64, and 6.9.83-41679-arm64. These changes add support for additional client versions and architectures.
2025-10-31 12:37:42 +08:00
手瓜一十雪
3e71e541e6 Update qqnt.json to version 9.9.22-40990
Bumped version, verHash, linuxVersion, linuxVerHash, and buildVersion fields in qqnt.json to reflect the new release 9.9.22-40990.
2025-10-31 00:11:28 +08:00
Mlikiowa
bcc856d583 release: v4.9.9 2025-10-30 16:08:57 +00:00
手瓜一十雪
1eaf480a7d Add napi2native mapping for 9.9.23-41679-x64
Introduced new send and recv address mappings for version 9.9.23-41679-x64 in napi2native.json.
2025-10-31 00:08:26 +08:00
手瓜一十雪
86123af7fc Update session instantiation and appid format
Changed session creation to use the constructor instead of the create() method in base.ts. Updated the appid.json key format from '9.9.23.41679' to '9.9.23-41679'. Added a constructor signature to NodeIQQNTWrapperSession interface. Updated NapCatWinBootHook.dll binary.
2025-10-31 00:06:31 +08:00
Mlikiowa
c3cd2aaf89 release: v4.9.8 2025-10-30 14:28:31 +00:00
手瓜一十雪
599afdc7ba Add entries for version 9.9.23.41679 in appid and packet
Updated appid.json and packet.json to include new entries for version 9.9.23.41679, specifying appid, qua, and packet send/recv values for the new version.
2025-10-30 22:24:05 +08:00
手瓜一十雪
ffe54af8d9 Fix addon path resolution and error handling
Corrects the construction of the ffmpeg addon filename and improves error handling when the addon is not found. Also simplifies the isAvailable method by removing redundant existence checks.
2025-10-30 22:13:57 +08:00
Mlikiowa
a7b30ef844 release: v4.9.7 2025-10-30 13:56:19 +00:00
手瓜一十雪
50b8cb14dc Comment out packet logging in initialization
Disabled the console logging of all packets in both NCoreInitFramework and NCoreInitShell by commenting out the nativePacketHandler.onAll debug statements. This reduces console noise during normal operation.
2025-10-30 21:55:50 +08:00
Mlikiowa
0b18f868bc release: v4.9.6 2025-10-30 13:49:11 +00:00
手瓜一十雪
c9acb9ae28 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-10-30 21:44:59 +08:00
手瓜一十雪
2e1506a05d Update native binaries for all major platforms
Rebuilt and replaced the napi2native.node binaries for Darwin ARM64, Linux ARM64, Linux x64, and Windows x64. This likely includes bug fixes, performance improvements, or compatibility updates in the native module.
2025-10-30 21:44:43 +08:00
Mlikiowa
b35283f970 release: v4.9.4 2025-10-30 13:40:21 +00:00
手瓜一十雪
54ac072bfb Replace console.error with console.log in error handler
Changed error logging in FFmpegAddonAdapter from console.error to console.log when addon loading fails.
2025-10-30 21:39:17 +08:00
pk5ls20
58cefb9cdc fix: napi2native linux offset 2025-10-30 21:00:35 +08:00
手瓜一十雪
be4344634d Add mappings for 3.2.20 versions in napi2native.json
Added send and recv address mappings for 3.2.20-x64 and 3.2.20-arm64 builds to support additional versions in napi2native.json.
2025-10-30 12:35:15 +08:00
手瓜一十雪
2da5d242f7 Refactor addon path resolution and rename Windows addon
Simplifies the logic for resolving the ffmpeg addon path by dynamically constructing the filename from process.platform and process.arch. Also renames the Windows x64 addon file to ffmpegAddon.win32.x64.node for consistency.
2025-10-30 11:29:54 +08:00
手瓜一十雪
fbd00b2576 feat: 9.9.22-40824 & 9.9.22-40768 2025-10-30 11:07:51 +08:00
手瓜一十雪
72548c9575 feat: packet能力增强 2025-10-30 11:05:19 +08:00
手瓜一十雪
003f3e946d refactor: 规范化 2025-10-30 11:01:45 +08:00
手瓜一十雪
dc51d01351 feat: raw包能力增强完成 2025-10-30 10:58:02 +08:00
手瓜一十雪
c5db525f4a refactor: 重构目录删除旧支持 2025-10-30 10:08:32 +08:00
手瓜一十雪
c1377e6de7 Remove 'bmp24' argument from getVideoInfo call
Updated the extractThumbnail method to call addon.getVideoInfo without the 'bmp24' argument, aligning with the updated addon API.
2025-10-30 09:23:25 +08:00
手瓜一十雪
d2c4f425c7 Remove baseClient.ts from packet client module
Deleted the src/core/packet/client/baseClient.ts file, which contained the PacketClient class and related interfaces. This may be part of a refactor or cleanup to remove unused or redundant code.
2025-10-30 09:11:58 +08:00
手瓜一十雪
803b1a6c77 feat: ffmpeg enhance for native node addon 2025-10-30 09:06:48 +08:00
手瓜一十雪
9a35ee9cd1 refactor: 大幅度调整send 2025-10-29 21:42:19 +08:00
手瓜一十雪
458d22223c fix: 简化代码 2025-10-29 21:37:55 +08:00
手瓜一十雪
4ed61136b2 fix: getQQBuildStr 2025-10-29 21:35:00 +08:00
手瓜一十雪
1445a29e15 Remove debug log for process PID in napcat.ts
Eliminated an unnecessary console.log statement that printed the process PID. This cleans up the output and removes leftover debugging code.
2025-10-29 21:25:24 +08:00
手瓜一十雪
55ef040852 Remove baseClient and simplify packet client selection
Deleted baseClient.ts and moved its logic into nativeClient.ts, making NativePacketClient a standalone class. Refactored PacketClientContext to always use NativePacketClient, removing support for multiple packet backends and related selection logic. Updated binary for napi2native.win32.x64.node.
2025-10-29 21:24:28 +08:00
手瓜一十雪
0c88319248 feat: Add FFmpeg native addon and TypeScript definitions
Introduced FFmpeg Node.js native addon binaries for multiple platforms (darwin-arm64, linux-arm64, linux-x64, win-x64) and added TypeScript type definitions for the addon interface, including video info extraction, duration detection, audio conversion, and PCM decoding.
2025-10-29 21:14:16 +08:00
手瓜一十雪
6778bd69de feat: new napcat-4.9.0-beta 2025-10-29 20:35:58 +08:00
手瓜一十雪
f9c9b3a852 Add native module loading and improve logging
Loaded a native module in NTQQGroupApi and added test calls to sendSsoCmdReqByContend with different parameter types. Changed fileLog default to true in config. Enhanced NativePacketClient with detailed send/receive logging. Updated NodeIKernelMsgService to accept unknown type for sendSsoCmdReqByContend param. Added process PID logging in napcat shell.
2025-10-26 20:05:21 +08:00
手瓜一十雪
9ce9e46c57 Merge branch 'main' into pr/1303 2025-10-25 16:17:51 +08:00
Mlikiowa
656bde25c8 release: v4.8.124 2025-10-22 13:12:25 +00:00
手瓜一十雪
791a360f28 Add 40990 version entries to appid and offset configs
Added new entries for version 40990 for multiple platforms in appid.json and offset.json, including appid, qua, and send/recv offsets. This update supports the latest client versions.
2025-10-22 21:09:37 +08:00
Mlikiowa
376245b749 release: v4.8.123 2025-10-21 15:06:10 +00:00
时瑾
d3e3527c2b fix: go-cqhttp 上传接口返回 file_id (UploadGroupFile, UploadPrivateFile) 2025-10-21 22:49:45 +08:00
Mlikiowa
5b78dfbd5a release: v4.8.122 2025-10-16 13:28:29 +00:00
手瓜一十雪
1e461aae3c Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-10-16 21:13:54 +08:00
手瓜一十雪
42abc2b9cb fix: #1286 2025-10-16 21:13:50 +08:00
Mlikiowa
4f8c320658 release: v4.8.121 2025-10-15 14:31:35 +00:00
手瓜一十雪
cbacc89907 Add appid and offset entries for version 40824
Added new entries for versions 3.2.20-40824, 9.9.22-40824, and 6.9.82-40824 in appid.json and corresponding offset values for x64 and arm64 architectures in offset.json.
2025-10-15 22:31:08 +08:00
Mlikiowa
8e6da5e2d0 release: v4.8.120 2025-10-14 12:33:43 +00:00
手瓜一十雪
02980c4d1a feat: Update QQ version data and add macOS ARM64 native module
Updated qqnt.json, appid.json, and offset.json to support QQ version 9.9.22-40768 and related Linux/macOS versions. Modified calcQQLevel in helper.ts to remove penguinNum from calculation. Added MoeHoo.darwin.arm64.new.node for macOS ARM64 support and updated LiteLoaderWrapper.zip binary.
2025-10-14 20:32:41 +08:00
时瑾
0129188739 fix: #1315 2025-10-14 09:38:18 +08:00
时瑾
98ef642cd1 feat: 取消群精华接口支持传递原始参数
- 1. onebot v11标准: 传递message_id
- 2. 通过官方http接口获取到的group_id、msg_random、msg_seq

二者任选其一
2025-10-12 21:13:56 +08:00
手瓜一十雪
32e886e53b Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-10-12 20:38:38 +08:00
风小七
315d847f06 Update helper.ts (#1311)
修复256级以后等级清零的问题。
2025-10-12 20:36:31 +08:00
手瓜一十雪
381d320967 feat: Add image and record stream download actions
Introduces BaseDownloadStream as a shared base class for streaming file downloads. Adds DownloadFileImageStream and DownloadFileRecordStream for image and audio file streaming with support for format conversion. Refactors DownloadFileStream to use the new base class, and updates action registration and router to include the new actions.
2025-10-12 15:50:34 +08:00
Clansty
2f5b62decb fix: get_group_info ownerUin is "0" 2025-10-09 02:30:08 +08:00
Mlikiowa
2afdb2a0da release: v4.8.119 2025-10-03 04:36:00 +00:00
手瓜一十雪
5bfbf92c21 Update key for 9.9.22-40362 offsets in offset.json
Changed the key from '9.9.22-40362' to '9.9.22-40362-x64' to clarify architecture specificity in the offsets mapping.
2025-10-03 12:35:28 +08:00
Mlikiowa
a775a0dde9 release: v4.8.118 2025-10-03 04:34:56 +00:00
手瓜一十雪
d7f00c0594 Fix batch variable quoting and case consistency
Updated batch scripts to use proper variable quoting and consistent casing for 'QQPath'. This improves reliability when handling paths with spaces and ensures environment variable names are used consistently.
2025-10-03 12:34:29 +08:00
Mlikiowa
77c8f874b6 release: v4.8.117 2025-10-03 04:21:22 +00:00
手瓜一十雪
fb0a20919b Add support for version 9.9.22-40362
Updated appid.json and offset.json to include entries for version 9.9.22-40362, specifying the new appid, qua, and offset values for send and recv.
2025-10-03 12:20:21 +08:00
手瓜一十雪
0300ba4648 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-10-03 12:20:15 +08:00
时瑾
d472eee777 fix: Reset pagination when navigating between directories in file manager
Fix: Reset pagination when navigating between directories in file manager
2025-10-02 09:40:37 +08:00
copilot-swe-agent[bot]
41bd06e50a Fix: Reset pagination to page 1 when navigating directories
Co-authored-by: sj817 <74231782+sj817@users.noreply.github.com>
2025-10-02 01:16:14 +00:00
copilot-swe-agent[bot]
97334dfbf5 Initial plan 2025-10-02 01:10:22 +00:00
手瓜一十雪
e3d8c8e940 fix: #1260 2025-09-29 16:39:37 +08:00
手瓜一十雪
f2c62db76e Update README with new features in v4.8.115+
Added a section describing new features in version v4.8.115+, including Stream API support and recommendations to use string types for message_id, user_id, and group_id. Also explained the benefits of these changes for Docker, cross-device, large file transfers, and better compatibility with languages lacking large integer support.
2025-09-21 13:29:35 +08:00
手瓜一十雪
b1b051c4ce Update DeepWiki badge formatting in README
Reformatted the DeepWiki badge section in the README to match the table style used for other community links.
2025-09-20 16:45:27 +08:00
手瓜一十雪
a754b2ecc7 Add DeepWiki badge to README
Added a DeepWiki badge with a link to the project's DeepWiki page for increased visibility and resource access.
2025-09-20 16:44:57 +08:00
Mlikiowa
e0eb625b75 release: v4.8.116 2025-09-20 08:20:05 +00:00
手瓜一十雪
937be7678e Add file_retention parameter to upload test
Introduces the 'file_retention' field with a value of 30,000 to the upload test payload in OneBotUploadTester. This may be used to specify file retention duration in milliseconds.
2025-09-20 16:19:39 +08:00
手瓜一十雪
9b88946209 Add file retention option to UploadFileStream
Introduces a 'file_retention' parameter to control how long uploaded files are retained before automatic deletion. If set, files are deleted after the specified duration; otherwise, they are not automatically removed. This helps manage temporary file storage and cleanup.
2025-09-20 16:13:25 +08:00
Mlikiowa
74de3d9100 release: v4.8.115 2025-09-20 07:57:47 +00:00
手瓜一十雪
42d50014a1 Refactor event handling to use async/await across adapters
Updated all network adapters' onEvent methods to be asynchronous and return Promises, ensuring consistent async event emission and handling. Adjusted related methods and event emission logic to properly await asynchronous operations, improving reliability for streaming, plugin, HTTP, and WebSocket event flows. Also improved error handling and messaging in stream and WebSocket actions.
2025-09-20 15:55:37 +08:00
手瓜一十雪
a36ae315b0 Fix useStream variable scope in HTTP server adapter
Moved the declaration of useStream inside the action check to prevent referencing it when action is undefined.
2025-09-20 15:23:37 +08:00
手瓜一十雪
2161ec5fa7 feat: 标准化 2025-09-20 15:20:43 +08:00
手瓜一十雪
32bba007cd Add Readme for stream API actions
Introduces a Readme.txt file in the stream action directory, providing an overview and usage notes for stream-related API functions such as file upload, download, and cleanup.
2025-09-16 23:33:48 +08:00
Mlikiowa
84d3dc9f40 release: v4.8.114 2025-09-16 15:24:25 +00:00
手瓜一十雪
890d032794 Add streaming file upload and download actions
Introduces new OneBot actions for streaming file upload and download, including chunked file transfer with memory/disk management and SHA256 verification. Adds CleanStreamTempFile, DownloadFileStream, UploadFileStream, and TestStreamDownload actions, updates action routing and network adapters to support streaming via HTTP and WebSocket, and provides Python test scripts for concurrent upload testing.
2025-09-16 23:24:00 +08:00
837951602
66f30e1ebf 找不到类型时显式报错 (#1256) 2025-09-16 17:19:59 +08:00
MliKiowa
ada614d007 Fix link for QQ Group#2 in README
Updated the link for QQ Group#2 in the README.
2025-09-13 17:23:21 +08:00
MliKiowa
ea3ab7f13f Fix QQ Group links in README.md 2025-09-13 17:22:53 +08:00
MliKiowa
a5e4c24de3 Revise README for clarity and additional resources
Updated README with links to documentation and community guidelines.
2025-09-13 17:20:07 +08:00
Mlikiowa
bcc7d25b64 release: v4.8.113 2025-09-13 05:51:02 +00:00
手瓜一十雪
aae676fdc7 Update default host and token length in config
Changed the default host to '0.0.0.0' and increased the default token length from 8 to 12 characters in WebUiConfigSchema. Also removed unused getDefaultHost import and made minor formatting adjustments.
2025-09-13 13:49:18 +08:00
Mlikiowa
0e9aa43476 release: v4.8.112 2025-09-12 14:15:08 +00:00
时瑾
b2ff556aa6 fix: 更新默认主机地址获取逻辑,支持Docker环境 2025-09-12 21:56:12 +08:00
Mlikiowa
69c5b78678 release: v4.8.111 2025-09-12 11:19:26 +00:00
时瑾
8be7f74e9f fix: 移除 defaultToken 字段,彻底移除硬编码的默认密码,采用全随机密码 2025-09-12 18:50:21 +08:00
时瑾
a05150ebe1 fix(dos): 修复红红的ci 2025-09-12 15:36:30 +08:00
Mlikiowa
5e6b607ded release: v4.8.110 2025-09-11 05:15:29 +00:00
时瑾
df2dabfe76 refactor: 将默认密码相关逻辑重构为后端处理 (#1247)
* refactor: 将默认密码相关逻辑重构为后端处理

* refactor: 日志路由进行脱敏,生成随机密码使用node:crypto.randomBytes

* feat: 更新密码功能增强,添加新密码强度验证和旧密码检查

* feat: 给文件管理添加WebUI配置文件的脱敏处理和验证逻辑

* refactor: 优化网络显示卡片按钮样式和行为,调整按钮属性以提升用户体验

* feat: 增强路径处理逻辑,添加安全验证以防止路径遍历攻击

* feat: 增强文件路径处理逻辑,添加安全验证以防止路径遍历攻击,并优化查询参数提取

* feat: CodeQL不认可 受不了
2025-09-11 13:13:00 +08:00
手瓜一十雪
5e032fcc6a upate: package 2025-09-08 16:16:38 +08:00
Mlikiowa
44200a2208 release: v4.8.109 2025-09-08 08:11:05 +00:00
手瓜一十雪
e39bb05f01 fix: 多层解析 2025-09-08 16:05:10 +08:00
手瓜一十雪
677731dd70 Add qq-chat-exporter to recommended tools
Included qq-chat-exporter, a NapCat-based message export tool, in the list of recommended related projects in the README.
2025-09-07 15:20:17 +08:00
Mlikiowa
fa8e6f2c59 release: v4.8.108 2025-09-07 05:57:27 +00:00
手瓜一十雪
509b23ff04 Update Telegram link in About page
Changed the Telegram href in the About page from MelodicMoonlight to napcatqq to reflect the correct contact or channel.
2025-09-07 13:54:44 +08:00
Mlikiowa
cf1765f5a4 release: v4.8.107 2025-09-07 05:48:15 +00:00
手瓜一十雪
c541c7e257 Update Telegram link in README
Changed the Telegram badge and link from MelodicMoonlight to napcatqq for accuracy.
2025-09-07 13:47:23 +08:00
手瓜一十雪
298b8b71c8 Log WebUI panel URL on server start
Adds logging of the WebUI user panel URL with localhost address when the server starts. Also adjusts QQ login callback invocation in OneBot adapter for improved login flow.
2025-09-07 13:29:37 +08:00
手瓜一十雪
5c120a8231 Add WebUI token update and callback handling
Introduces callback mechanisms for WebUI token changes and QQ login status updates. The WebUI token is now updated and communicated via a callback after login, and related runtime and type definitions are extended to support these features. Also sets a static default token value in the config schema.
2025-09-06 18:15:35 +08:00
手瓜一十雪
88ee8f89fe Comment out documentation links in README
The section containing links to documentation and Telegram has been commented out in the README.md. This may be for temporary removal or future revision.
2025-09-06 13:13:14 +08:00
Mlikiowa
12b8130372 release: v4.8.106 2025-09-06 03:53:33 +00:00
手瓜一十雪
58332dad24 feat: 支持禁用webui 速率配置 禁用外网访问 禁用webui 2025-09-06 11:53:04 +08:00
手瓜一十雪
e97f3e1283 feat: 进一步提高密码安全性 2025-09-06 11:42:12 +08:00
手瓜一十雪
e406dca7ae feat: 禁止默认密码 2025-09-06 11:41:06 +08:00
手瓜一十雪
e4c1807f76 feat: 安全性提升 2025-09-06 11:32:09 +08:00
手瓜一十雪
f4412bb086 feat: 安全性提升 2025-09-06 11:23:09 +08:00
手瓜一十雪
27af8e52ac feat: 安全性提升 2025-09-06 10:49:29 +08:00
手瓜一十雪
4c9a220300 Add README for example plugin
Introduces installation instructions for the example plugin, detailing how to place the build output in the appropriate plugins directory.
2025-09-02 22:54:44 +08:00
手瓜一十雪
1fe822cd20 Update import paths and remove plugin tsconfig
Changed import paths in example-plugin to use alias '@' instead of relative paths. Deleted the example-plugin/tsconfig.json file, likely consolidating TypeScript configuration or relying on a root config.
2025-09-02 22:48:01 +08:00
手瓜一十雪
0ab8d025bf Declare config property in OB11PluginMangerAdapter
Adds an explicit declaration for the 'config' property in the OB11PluginMangerAdapter class to improve type safety and clarity.
2025-09-02 22:42:09 +08:00
Mlikiowa
a0f3d66607 release: v4.8.105 2025-09-02 14:33:33 +00:00
手瓜一十雪
06e7c3363a Add quick_reply support to message parsing
Introduces the quick_reply boolean option to message history schema and message parsing logic. Updates relevant functions to handle quick_reply, allowing for conditional behavior during message reply and segment parsing.
2025-09-02 22:33:05 +08:00
手瓜一十雪
4d200de6b7 Refactor packet client and update message history actions
Replaced LRUCache with Map for callback and event management in packet clients, and standardized callback hash usage. Updated GetFriendMsgHistory and GetGroupMsgHistory actions to use snake_case for payload keys. Modified OneBotMsgApi to support disabling URL retrieval for ptt elements via a new parameter.
2025-09-02 22:24:53 +08:00
手瓜一十雪
6200097f7c Add resource health management and enhance message parsing
Introduces a ResourceManager for health checking and retry logic in src/common/health.ts. Updates OneBot message parsing to support disabling URL fetching and multi-message parsing via new payload options. File, image, video, and ptt URL retrievals now use resource health management for improved reliability. Also refactors packet API to allow configurable timeout for FetchRkey.
#1220
2025-09-02 21:19:49 +08:00
手瓜一十雪
c7af0384fb Add plugin manager and example plugin system
Introduces a plugin manager (OB11PluginMangerAdapter) for dynamic plugin loading, initialization, and event handling. Adds an example plugin with configuration files and updates related code to support plugin directory detection and loading. Refactors plugin adapter logic for extensibility and modularity.
2025-09-02 20:42:54 +08:00
Mlikiowa
dc87615bd6 release: v4.8.104 2025-09-02 08:32:22 +00:00
手瓜一十雪
ff2cfcee97 Add appid and offset entries for new versions
Added appid and qua mappings for versions 3.2.19-39038 and 9.9.21-39038 in appid.json. Updated offset.json with send/recv offsets for these new versions and architectures (x64, arm64).
2025-09-02 16:31:24 +08:00
手瓜一十雪
f3c07ed8fc Add timeout parameter to file and packet API methods
Introduces an optional timeout parameter (defaulting to 20000ms) to various file and packet API methods for improved control over request duration. Updates all relevant method calls and internal usages to support the new timeout argument, including OneBot message API calls with a shorter timeout for file, video, and ptt URL retrieval.
2025-09-02 09:54:42 +08:00
手瓜一十雪
7ab44dcb34 Refactor video thumbnail generation logic
Moved video thumbnail generation to occur before custom thumbnail copying, ensuring fallback to default thumbnail only if video info retrieval fails. Also reordered rkeyManager URLs for consistency.
2025-09-02 09:35:56 +08:00
Mlikiowa
aa6699d06e release: v4.8.103 2025-08-31 13:32:47 +00:00
手瓜一十雪
3cb51a17a6 Add new appid and offset entries for version 38960
Updated appid.json and offset.json to include new entries for versions 3.2.19-38960 and 9.9.21-38960, supporting both x64 and arm64 architectures.
2025-08-31 21:32:15 +08:00
Mlikiowa
994e8ced3e release: v4.8.102 2025-08-26 06:59:25 +00:00
手瓜一十雪
75d26465f1 Add group album media actions and API integration
Introduces new OneBot actions for group album media: listing, commenting, liking, and deleting. Adds supporting API methods and data structures for album media operations in NTQQWebApi and NodeIKernelAlbumService. Updates action router and index to register new actions.
2025-08-26 14:58:11 +08:00
LgCookie
f5052935bd fix: special char of token in webui url should be url encoded (#1209) 2025-08-26 08:37:39 +08:00
Mlikiowa
84b89de2a6 release: v4.8.101 2025-08-25 11:05:45 +00:00
手瓜一十雪
c4f9c4f630 fix 2025-08-25 19:05:13 +08:00
手瓜一十雪
c213cd6c3a Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-08-25 19:04:31 +08:00
手瓜一十雪
9d22d6e3a0 Add NTQQ album list API and update related logic
Introduces getAlbumListByNTQQ to NTQQWebApi for retrieving group album lists via NTQQ. Updates NodeIKernelAlbumService interface for typed getAlbumList parameters and response. Refactors GetQunAlbumList action to use the new NTQQ API and return the correct album list format. Also fixes cookie and bkn usage in album-related methods for consistency.
2025-08-25 19:04:22 +08:00
Mlikiowa
a38419e3cb release: v4.8.100 2025-08-25 07:51:18 +00:00
手瓜一十雪
a64779684e fix #1171 && Improve message recall handling and cleanup
Changed recallMsg to return the result of the event call. Added a 5-second cache cleanup for recall events in DeleteMsg. Removed an unnecessary blank line in plugin.ts.
2025-08-25 15:50:05 +08:00
手瓜一十雪
ecd7012eee Remove redundant upload success log
Eliminated a logger statement that logged successful uploads in the NTQQWebApi class. This reduces unnecessary log output during chunked uploads.
2025-08-25 15:24:30 +08:00
手瓜一十雪
74a1011fcc Refactor Qun album image upload logic
Reworked the group album image upload process to use a new slice-based upload method, replacing the previous chunked upload implementation. Updated related interfaces and removed unused chunk upload code for improved maintainability and clarity.
2025-08-25 14:45:52 +08:00
手瓜一十雪
d4b0a4acca Simplify recall check for self-operated messages 2025-08-25 13:04:22 +08:00
手瓜一十雪
ac6e593315 Add cookie header to getAlbumList API request
The getAlbumList method in NTQQWebApi now includes a 'Cookie' header with skey, pskey, and uin for authentication. Updated GetQunAlbumList action to use the correct return type from NTQQWebApi.getAlbumList.
2025-08-24 20:39:07 +08:00
手瓜一十雪
b1e77b1658 feat: Add group album upload utilities and refactor API && close #1116
Introduces src/core/data/webapi.ts with utilities for chunked group album uploads, including session creation and chunk management. Refactors NTQQWebApi in webapi.ts to use these utilities, adds getAlbumList and uploadImageToQunAlbum methods, and improves upload logic for efficiency and maintainability.
2025-08-24 20:12:35 +08:00
手瓜一十雪
722c3554e9 feat: #1121 & Add cache cleaning for specific directories
Extended the CleanCache action to remove files from Pic, Ptt, Video, File, and log directories under the nt_data path. This improves cache management by ensuring these directories are also cleaned, with logging for successful and failed deletions. 增强缓存清理
2025-08-24 16:02:05 +08:00
手瓜一十雪
1d08966571 feat: #1179 2025-08-24 15:50:08 +08:00
手瓜一十雪
fb50ae7544 chore: Update LiteLoaderWrapper.zip binary && close #1169
Replaces the existing LiteLoaderWrapper.zip file with a new version. Details of the changes within the binary are not shown in this commit.
2025-08-24 15:18:49 +08:00
手瓜一十雪
ea695fc9e9 fix: #1171 2025-08-24 14:53:58 +08:00
Mlikiowa
5c6d1e6a14 release: v4.8.99 2025-08-24 06:02:30 +00:00
手瓜一十雪
b030c40853 feat: 38711 2025-08-24 14:02:03 +08:00
子寻
d6782c35e2 fix: /get_msg interface returns group type message with group_name. (#1205) 2025-08-23 11:38:24 +08:00
Mlikiowa
120e6db119 release: v4.8.98 2025-08-17 15:35:05 +00:00
手瓜一十雪
fa10f8ce19 Fix typo in getFullQQVersion method call
Corrects the method name from getFullQQVesion to getFullQQVersion in the WebUiDataRuntime.setQQVersion call to ensure proper retrieval of QQ version information.
2025-08-17 23:34:41 +08:00
Mlikiowa
31494b4687 release: v4.8.97 2025-08-17 14:59:12 +00:00
手瓜一十雪
857ed0f343 fix: error 2025-08-17 22:58:30 +08:00
837951602
8133ff08a7 fix: offset.json (#1193)
fix win add lin x64
2025-08-17 17:31:24 +08:00
Mlikiowa
2d315c4d8e release: v4.8.96 2025-08-15 11:17:15 +00:00
手瓜一十雪
505f7b6ac9 Update group.ts 2025-08-15 19:12:13 +08:00
手瓜一十雪
2735eb14bd fix: #1191 2025-08-15 19:12:04 +08:00
dependabot[bot]
7afbc95eda build(deps-dev): bump vite from 6.3.5 to 7.1.1 (#1183)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 7.1.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 19:09:03 +08:00
dependabot[bot]
91bb83d8c1 build(deps-dev): bump @typescript-eslint/parser from 8.38.0 to 8.39.0 (#1182)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.38.0 to 8.39.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 19:08:57 +08:00
dependabot[bot]
55550790e4 build(deps-dev): bump @eslint/js from 9.32.0 to 9.33.0 (#1185)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.32.0 to 9.33.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.33.0/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.33.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 19:08:40 +08:00
dependabot[bot]
40221926a9 build(deps-dev): bump @sinclair/typebox from 0.34.35 to 0.34.38 (#1187)
Bumps [@sinclair/typebox](https://github.com/sinclairzx81/typebox) from 0.34.35 to 0.34.38.
- [Commits](https://github.com/sinclairzx81/typebox/compare/0.34.35...0.34.38)

---
updated-dependencies:
- dependency-name: "@sinclair/typebox"
  dependency-version: 0.34.38
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 19:08:30 +08:00
手瓜一十雪
d069374a95 Add explicit type annotations to AST traversal paths
Updated the performance monitor Vite plugin to add explicit type annotations to AST traversal callback parameters, improving type safety and clarity. Also removed a duplicate import in src/plugin/index.ts.
2025-08-15 19:02:53 +08:00
手瓜一十雪
1183fe2057 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-08-15 19:00:57 +08:00
手瓜一十雪
f4605d4f74 Add appid and offset entries for new versions
Added appid and qua for versions 9.9.21-38503 and 3.2.19-38503 in appid.json. Added send and recv offsets for 9.9.21-38503 in offset.json to support new application versions.
2025-08-15 18:57:43 +08:00
dependabot[bot]
3ce3fb685b build(deps-dev): bump @eslint/js from 9.31.0 to 9.32.0 (#1160)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.31.0 to 9.32.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.32.0/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.32.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 20:39:28 +08:00
dependabot[bot]
07c2f7371f build(deps-dev): bump esbuild from 0.25.5 to 0.25.8 (#1159)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.5 to 0.25.8.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.5...v0.25.8)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 20:39:14 +08:00
囧囧JOJO
114aae98a9 feat: 添加可选参数 'count' 到 SetGroupAddRequest 以支持动态获取通知数量(#1113 补充) (#1146)
* feat: 添加可选参数 'count' 到 SetGroupAddRequest 以支持动态获取通知数量(#1113 补充)

* feat: 设置 SetGroupAddRequest 中 'count' 的默认值为 100

* Update src/onebot/action/group/SetGroupAddRequest.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-06 20:38:57 +08:00
dependabot[bot]
0bd6548f45 build(deps): bump ws from 8.18.2 to 8.18.3 (#1157)
Bumps [ws](https://github.com/websockets/ws) from 8.18.2 to 8.18.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.18.2...8.18.3)

---
updated-dependencies:
- dependency-name: ws
  dependency-version: 8.18.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 20:38:41 +08:00
dependabot[bot]
06c5b7807b build(deps-dev): bump @typescript-eslint/parser from 8.37.0 to 8.38.0 (#1158)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.37.0 to 8.38.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.38.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.38.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 20:38:15 +08:00
手瓜一十雪
e8ef08cae2 Pr enhance (#1176)
* fix

* Refactor PacketApi status checks and fix typos

Replaced all usages of PacketApi.available with PacketApi.packetStatus for more accurate status checking. Fixed a typo in getFullQQVesion to getFullQQVersion. Updated plugin_onmessage to fetch and parse messages for a user. These changes improve reliability and consistency in API status handling.

* Fix typo in getFullQQVersion method name

Corrected the method name from getFullQQVesion to getFullQQVersion in multiple locations to ensure consistency and prevent potential runtime errors.

* Remove performance CLI and demo, fix typos, update proto

Deleted the performance CLI and demo files. Fixed a typo in getFullQQVesion to getFullQQVersion across multiple files. Changed the 'time' field type from UINT32 to UINT64 in Oidb.0x9067_202 proto. Commented out performanceMonitorPlugin in vite.config.ts. Removed an unimplemented log statement in NodeIKernelBuddyListener.

* Comment out default plugin adapter registration

The default registration of OB11PluginAdapter in NapCatOneBot11Adapter was commented out, likely to prevent automatic plugin loading or to allow for more flexible plugin management. Also, removed an unnecessary blank line in the plugin_onmessage function.

* fix

* Add shell-analysis mode with performance monitoring

Introduces a new .env.shell-analysis file and a dev:shell-analysis npm script for building in shell-analysis mode. Updates vite.config.ts to support the new mode, enabling the performance monitor plugin with an updated exclude list. Also extends the plugin's exclude patterns to filter out 'packet' files.

* Delete performance-api.ts

* Add commented export for performance-monitor

Added a commented-out export statement for '@/common/performance-monitor' in napcat.ts, possibly for future use or reference. No functional changes to the file.
2025-08-06 20:37:45 +08:00
手瓜一十雪
0d251a9343 Remove performance CLI and demo, fix typos, update proto
Deleted the performance CLI and demo files. Fixed a typo in getFullQQVesion to getFullQQVersion across multiple files. Changed the 'time' field type from UINT32 to UINT64 in Oidb.0x9067_202 proto. Commented out performanceMonitorPlugin in vite.config.ts. Removed an unimplemented log statement in NodeIKernelBuddyListener.
2025-08-06 19:57:36 +08:00
手瓜一十雪
69a19b0e32 Fix typo in getFullQQVersion method name
Corrected the method name from getFullQQVesion to getFullQQVersion in multiple locations to ensure consistency and prevent potential runtime errors.
2025-08-06 19:32:14 +08:00
手瓜一十雪
1b0b5f3494 Refactor PacketApi status checks and fix typos
Replaced all usages of PacketApi.available with PacketApi.packetStatus for more accurate status checking. Fixed a typo in getFullQQVesion to getFullQQVersion. Updated plugin_onmessage to fetch and parse messages for a user. These changes improve reliability and consistency in API status handling.
2025-08-06 19:31:24 +08:00
手瓜一十雪
5abdc8c538 fix 2025-08-06 18:43:44 +08:00
Mlikiowa
9f0ba6d385 release: v4.8.95 2025-07-26 12:19:32 +00:00
手瓜一十雪
7863a0f45f Add new appid and offset entries for version 37625
Updated appid.json and offset.json to include new entries for versions 9.9.20-37625 and 3.2.18-37625, supporting both x64 and arm64 architectures.
2025-07-26 20:19:04 +08:00
Mlikiowa
ef4c2a935c release: v4.8.94 2025-07-21 11:49:43 +00:00
手瓜一十雪
40d2e948e4 feat: 添加新的appid和offset配置以支持版本9.9.20-37475和3.2.18-37475 2025-07-21 19:48:59 +08:00
dependabot[bot]
3de4e905d3 build(deps-dev): bump eslint-plugin-import from 2.31.0 to 2.32.0 (#1123)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.31.0 to 2.32.0.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.31.0...v2.32.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-version: 2.32.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 手瓜一十雪 <nanaeonn@outlook.com>
2025-07-21 19:19:51 +08:00
dependabot[bot]
655f4e199c build(deps-dev): bump eslint-import-resolver-typescript (#1124)
Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 4.4.3 to 4.4.4.
- [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases)
- [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v4.4.3...v4.4.4)

---
updated-dependencies:
- dependency-name: eslint-import-resolver-typescript
  dependency-version: 4.4.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 19:18:22 +08:00
dependabot[bot]
13c3d3a2fb build(deps-dev): bump @eslint/js from 9.30.1 to 9.31.0 (#1125)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.30.1 to 9.31.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.31.0/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.31.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 19:18:10 +08:00
dependabot[bot]
db2dca45f6 build(deps-dev): bump @typescript-eslint/parser from 8.35.1 to 8.37.0 (#1140)
---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.37.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 19:17:45 +08:00
手瓜一十雪
7330a05c78 fix 2025-07-11 19:28:01 +08:00
Mlikiowa
a39c932868 release: v4.8.93 2025-07-09 11:21:08 +00:00
dependabot[bot]
a2c24c9197 build(deps-dev): bump @eslint/js from 9.28.0 to 9.30.1 (#1110)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.28.0 to 9.30.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.30.1/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.30.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:11:09 +08:00
dependabot[bot]
5c3efc681f build(deps-dev): bump @eslint/compat from 1.3.0 to 1.3.1 (#1099)
Bumps [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v1.3.1/packages/compat)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 1.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:10:54 +08:00
dependabot[bot]
e70d2bd708 build(deps-dev): bump typescript-eslint from 8.34.0 to 8.35.1 (#1112)
---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.35.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:10:14 +08:00
dependabot[bot]
cf75a961fb build(deps-dev): bump @rollup/plugin-typescript from 12.1.2 to 12.1.4 (#1098)
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/HEAD/packages/typescript) from 12.1.2 to 12.1.4.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/typescript/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/typescript-v12.1.4/packages/typescript)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-typescript"
  dependency-version: 12.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:10:02 +08:00
手瓜一十雪
159f317071 Add support for 3.2.18-37051 and 9.9.20-37051 versions
Updated appid.json and offset.json to include new entries for versions 3.2.18-37051 and 9.9.20-37051, including their respective app IDs, QUA values, and offset mappings for x64 and arm64 architectures.
2025-07-09 19:06:47 +08:00
Mlikiowa
713eef592a release: v4.8.92 2025-07-07 12:47:57 +00:00
囧囧JOJO
cf03ad8fd9 feat: 向 /get_system_msg 添加可选参数 'count' (#1113)
* feat: Add the optional parameter "count" to /get_system_msg

* Refactor GetGroupSystemMsg to use TypeBox schema

Introduced TypeBox for payload validation in GetGroupSystemMsg, replacing manual count handling with a schema-based approach. Updated the handler to use the new payload type and schema, improving type safety and input validation.

---------

Co-authored-by: 手瓜一十雪 <nanaeonn@outlook.com>
2025-07-07 20:46:26 +08:00
Mlikiowa
0c0b27901a release: v4.8.91 2025-07-07 12:41:15 +00:00
手瓜一十雪
137fe3c8f2 feat: 37012 2025-07-07 20:40:36 +08:00
Mlikiowa
d96174076a release: v4.8.90 2025-06-29 02:01:31 +00:00
手瓜一十雪
6d5662d96e feat: Add new Linux native modules for arm64 and x64
Added MoeHoo.linux.arm64.new.node and MoeHoo.linux.x64.new.node binaries to support native packet functionality on both ARM64 and x64 Linux platforms.
2025-06-29 09:58:32 +08:00
Mlikiowa
57abd47d99 release: v4.7.85 2025-06-26 10:35:59 +00:00
手瓜一十雪
5092b3d791 fix: package 2025-06-26 18:35:12 +08:00
Mlikiowa
649409d1be release: v4.7.81 2025-06-26 10:32:56 +00:00
手瓜一十雪
8f549d896a feat: package 2025-06-26 18:32:31 +08:00
Mlikiowa
a1359ddbb5 release: v4.7.80 2025-06-26 10:30:32 +00:00
手瓜一十雪
304a0dda3e feat: 初步验证win 36580 2025-06-26 18:30:06 +08:00
手瓜一十雪
fff9c4a4d8 feat: 36580 2025-06-26 17:06:37 +08:00
Wang Zeng
2c76102fc4 ci: dispatch docker build workflow after release (#1078) 2025-06-15 23:26:17 +08:00
手瓜一十雪
f576cd9417 fix: type 2025-06-13 16:58:05 +08:00
时瑾
9cfd224b74 fix: 优化get_group_ignored_notifies接口返回值 2025-06-12 20:14:11 +08:00
时瑾
c12f8de8b4 feat: get_collection_list 2025-06-12 13:28:31 +08:00
时瑾
ed9a7c52e2 feat: get_group_ignore_add_request 2025-06-12 13:23:22 +08:00
Mlikiowa
38fcaaa28b release: v4.7.78 2025-06-12 04:30:05 +00:00
手瓜一十雪
5317a1c1a9 fix: 35951 2025-06-12 12:29:14 +08:00
时瑾
4bc5933ea2 fix: 修正部分接口的参数、返回值,提高兼容性 (#1072)
* fix: 修正`get_group_system_msg` `get_group_honor_info`接口返回值 提升兼容性

* fix: `create_group_file_folder` 接口兼容性提升
2025-06-11 12:37:29 +08:00
Mlikiowa
6a6bd33fe5 release: v4.7.77 2025-06-10 06:28:20 +00:00
手瓜一十雪
8256942a3d fix: #1051 2025-06-10 14:27:50 +08:00
手瓜一十雪
697632eee8 feat: 35951 2025-06-10 13:19:19 +08:00
手瓜一十雪
6bbf5b254d fix: #1049 2025-06-10 13:05:36 +08:00
手瓜一十雪
5831898c4a fix: #1058 2025-06-10 12:54:29 +08:00
dependabot[bot]
2cc413bec1 build(deps-dev): bump multer from 1.4.5-lts.2 to 2.0.1 (#1070)
Bumps [multer](https://github.com/expressjs/multer) from 1.4.5-lts.2 to 2.0.1.
- [Release notes](https://github.com/expressjs/multer/releases)
- [Changelog](https://github.com/expressjs/multer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/expressjs/multer/compare/v1.4.5-lts.2...v2.0.1)

---
updated-dependencies:
- dependency-name: multer
  dependency-version: 2.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-10 12:48:39 +08:00
时瑾
0af36e89d9 fix: 转发消息接口返回值兼容gocq (#1066) 2025-06-09 10:02:27 +08:00
837951602
b2c0f5d2e5 /get_group_system_msg description (#1064) 2025-06-08 10:38:32 +08:00
手瓜一十雪
80b74c7da9 Merge pull request #1054 from NapNeko/dependabot/npm_and_yarn/file-type-21.0.0
build(deps-dev): bump file-type from 20.5.0 to 21.0.0
2025-06-06 11:53:31 +08:00
手瓜一十雪
f14f13b158 build(deps-dev): bump esbuild from 0.25.4 to 0.25.5 (#1056)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.4 to 0.25.5.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.4...v0.25.5)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-06 11:53:06 +08:00
lzw
9dda00b6fa chore: add host in listen log
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-06-05 12:59:01 +08:00
Lan Zongwei
a29debb738 fix: fix missing host in onebot http-server listen 2025-06-05 12:59:01 +08:00
dependabot[bot]
b990fc43df build(deps-dev): bump esbuild from 0.25.4 to 0.25.5
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.4 to 0.25.5.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.4...v0.25.5)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 09:14:28 +00:00
dependabot[bot]
915e9552ee build(deps-dev): bump file-type from 20.5.0 to 21.0.0
Bumps [file-type](https://github.com/sindresorhus/file-type) from 20.5.0 to 21.0.0.
- [Release notes](https://github.com/sindresorhus/file-type/releases)
- [Commits](https://github.com/sindresorhus/file-type/compare/v20.5.0...v21.0.0)

---
updated-dependencies:
- dependency-name: file-type
  dependency-version: 21.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 09:09:48 +00:00
Mlikiowa
c522e0a386 release: v4.7.76 2025-05-29 13:58:02 +00:00
手瓜一十雪
c9cc08a9ba fix: #1048 2025-05-29 21:15:07 +08:00
手瓜一十雪
66e1b1662f fix: 支持registerCallback 2025-05-29 20:45:35 +08:00
手瓜一十雪
9372e83bd8 feat: nativeLoader功能预备 2025-05-29 14:39:09 +08:00
Mlikiowa
b38a240dbb release: v4.7.75 2025-05-26 12:19:44 +00:00
手瓜一十雪
76b9506395 fix: #1043 2025-05-26 19:58:50 +08:00
手瓜一十雪
f1cf636aa2 Merge pull request #1041 from Neboer/main
允许使用环境变量指定napcat工作路径。
2025-05-26 14:41:01 +08:00
Mlikiowa
312dcd0e13 release: v4.7.74 2025-05-26 05:57:44 +00:00
手瓜一十雪
42c2419613 Revert "fix: #1038"
This reverts commit 4e7c96634c.
2025-05-26 13:56:48 +08:00
手瓜一十雪
8f7f748e82 Revert "fix: #1039"
This reverts commit 1eda3f2e33.
2025-05-26 13:56:14 +08:00
Neboer
7ad3bad1be 修改环境变量名字NAPCAT_WRITEPATH为NAPCAT_WORKDIR 2025-05-26 05:36:07 +00:00
Neboer
5cd682e69f 允许使用NAPCAT_WRITEPATH环境变量指定napcat工作路径。 2025-05-26 04:59:20 +00:00
Mlikiowa
5d57780e84 release: v4.7.73 2025-05-26 03:52:01 +00:00
手瓜一十雪
f399955204 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-05-26 11:51:35 +08:00
手瓜一十雪
770652fe6b fix: remove debug 2025-05-26 11:51:25 +08:00
Mlikiowa
9ed5fa8c67 release: v4.7.72 2025-05-26 03:51:12 +00:00
手瓜一十雪
5a4ad29727 fix: #1040 2025-05-26 11:50:45 +08:00
手瓜一十雪
1eda3f2e33 fix: #1039 2025-05-26 10:58:01 +08:00
Mlikiowa
95cb95ef96 release: v4.7.70 2025-05-25 08:56:20 +00:00
手瓜一十雪
4e7c96634c fix: #1038 2025-05-25 16:55:32 +08:00
手瓜一十雪
58587b8aea fix 2025-05-25 16:30:15 +08:00
手瓜一十雪
3fbf6239db fix: #1031 2025-05-25 16:18:50 +08:00
手瓜一十雪
faec53d497 feat: #1031 2025-05-25 16:09:06 +08:00
手瓜一十雪
482dcc534e feat: kill-update 2025-05-24 10:33:14 +08:00
手瓜一十雪
854f61dda6 feat: createGrayTip 2025-05-23 17:22:07 +08:00
Mlikiowa
fca38713a1 release: v4.7.68 2025-05-22 03:48:00 +00:00
手瓜一十雪
5dd3bade53 fix: #1029 2025-05-22 11:47:31 +08:00
手瓜一十雪
665360f48d fix: #1027 2025-05-22 11:33:23 +08:00
Mlikiowa
65719cb56a release: v4.7.67 2025-05-21 04:59:16 +00:00
手瓜一十雪
bdb76d4639 fix: make ts happy 2025-05-21 12:58:53 +08:00
Mlikiowa
15634412ef release: v4.7.66 2025-05-21 04:53:55 +00:00
手瓜一十雪
bbcf9649fa feat: 35341 2025-05-21 12:53:21 +08:00
Mlikiowa
e845d7314e release: v4.7.65 2025-05-18 14:32:18 +00:00
手瓜一十雪
6927b1c94f Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-05-18 20:59:17 +08:00
手瓜一十雪
a09c6acd0d fix 2025-05-18 20:59:11 +08:00
Mlikiowa
0963650ccb release: v4.6.65 2025-05-18 12:58:07 +00:00
手瓜一十雪
380688b353 fix 2025-05-18 20:57:41 +08:00
手瓜一十雪
ad5466bff8 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-05-18 20:55:13 +08:00
手瓜一十雪
a83652bf3f feat: 更优美的代码 2025-05-18 20:55:11 +08:00
Mlikiowa
c632de314d release: v4.7.64 2025-05-18 12:49:37 +00:00
手瓜一十雪
259c9610d5 Merge pull request #1022 from NapNeko/poke_enhance
fix: #1018
2025-05-18 20:45:08 +08:00
手瓜一十雪
e9936c5524 fix 2025-05-18 20:42:03 +08:00
手瓜一十雪
3f60440e72 fix 2025-05-18 20:24:49 +08:00
手瓜一十雪
71a15f92fb fix 2025-05-18 20:19:53 +08:00
手瓜一十雪
32bc0dd820 fix 2025-05-18 20:16:55 +08:00
手瓜一十雪
20d1ac9d01 fix: #1018 2025-05-18 20:15:38 +08:00
手瓜一十雪
18baf89e0e Merge pull request #1021 from NapNeko/feat-new-context
feat: 隔离context传递 避免高并发干扰一个实例
2025-05-18 19:27:23 +08:00
手瓜一十雪
3a1d1f2e59 feat: 隔离context传递 避免高并发干扰一个实例 2025-05-18 19:21:14 +08:00
手瓜一十雪
e9a048721d fix: readonly 2025-05-18 19:02:31 +08:00
手瓜一十雪
68f0c7ff1a fix: readonly 2025-05-18 19:01:57 +08:00
手瓜一十雪
2875fe94ea Merge pull request #1020 from pohgxz/main
增加抽象类,修改继承关系
2025-05-18 18:59:25 +08:00
手瓜一十雪
1870427c0f fix: readonly 2025-05-18 18:58:27 +08:00
手瓜一十雪
636568fd30 fix: 字面量
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-18 18:56:59 +08:00
手瓜一十雪
bbc2391bf8 fix: 别名
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-18 18:55:56 +08:00
手瓜一十雪
401684542a fix: override
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-05-18 18:53:01 +08:00
手瓜一十雪
870edb2513 fix: override
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-05-18 18:52:07 +08:00
Nepenthe
7ad09169ea 增加抽象类,修改继承关系 2025-05-18 18:32:23 +08:00
手瓜一十雪
c1a0f8915b docs: mai 2025-05-17 17:54:53 +08:00
Mlikiowa
dcdab8e5a1 release: v4.7.63 2025-05-17 05:09:36 +00:00
手瓜一十雪
eb3278fdab feat: 35184 2025-05-17 11:16:39 +08:00
手瓜一十雪
34db3af48d fix: #1007 2025-05-15 21:10:21 +08:00
Mlikiowa
198da960dd release: v4.7.62 2025-05-12 12:37:01 +00:00
手瓜一十雪
cb83918fb3 fix 2025-05-12 20:36:39 +08:00
Mlikiowa
f59a48540b release: v4.7.61 2025-05-12 12:34:18 +00:00
手瓜一十雪
ccf9c1a5fb Merge pull request #1005 from NapNeko/fix-1001
refactor: remove image-size
2025-05-12 20:22:35 +08:00
手瓜一十雪
ba6a85142a fix 2025-05-12 19:29:40 +08:00
手瓜一十雪
440baccd2a fix 2025-05-12 19:27:46 +08:00
手瓜一十雪
690c073328 fix 2025-05-12 19:27:01 +08:00
手瓜一十雪
3f0730ed4f fix 2025-05-12 19:25:03 +08:00
手瓜一十雪
01d5663bc8 fix: image size 2025-05-12 19:18:33 +08:00
手瓜一十雪
49806cd00e Create index.ts 2025-05-12 19:17:17 +08:00
手瓜一十雪
935b0848e5 Merge pull request #1004 from NapNeko/dependabot/npm_and_yarn/esbuild-0.25.4
build(deps-dev): bump esbuild from 0.25.0 to 0.25.4
2025-05-12 18:45:23 +08:00
dependabot[bot]
5ca20a89a2 build(deps-dev): bump esbuild from 0.25.0 to 0.25.4
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.0 to 0.25.4.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.0...v0.25.4)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 08:37:11 +00:00
Mlikiowa
e89a2266ec release: v4.7.60 2025-05-11 03:32:29 +00:00
手瓜一十雪
6607533311 fix: #996 2025-05-11 11:31:52 +08:00
Mlikiowa
4057054220 release: v4.7.58 2025-05-11 03:24:02 +00:00
手瓜一十雪
055e43845e feat: ffmpeg下载来源更换 2025-05-11 11:23:43 +08:00
Mlikiowa
d67270f2f8 release: v4.7.57 2025-05-10 13:15:37 +00:00
手瓜一十雪
d061b6c190 feat: 34958 2025-05-10 21:15:07 +08:00
Mlikiowa
945f87d77f release: v4.7.56 2025-05-09 11:28:05 +00:00
手瓜一十雪
6c9be52d39 feat: 34740 2025-05-09 19:16:25 +08:00
手瓜一十雪
98e347f010 fix: 过滤掉已读 2025-05-09 18:51:00 +08:00
手瓜一十雪
607e367bb1 fix 2025-05-09 13:17:47 +08:00
Mlikiowa
7a25dc1ef1 release: v4.7.55 2025-05-08 10:11:36 +00:00
手瓜一十雪
e22ec4be09 Merge pull request #991 from NapNeko/fix-upload-foward-cahce-del
fix: upload-foward-cache-del
2025-05-08 18:10:45 +08:00
手瓜一十雪
51a06622f9 fix: typo
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-05-08 18:04:40 +08:00
手瓜一十雪
22faf5b831 fix 2025-05-08 17:58:12 +08:00
手瓜一十雪
e781c662b2 fix 2025-05-08 15:20:55 +08:00
手瓜一十雪
5744698d24 docs: 清空不好的影响&推荐一下 2025-05-08 15:20:06 +08:00
Mlikiowa
2c2ab3cd48 release: v4.7.51 2025-05-07 15:16:50 +00:00
手瓜一十雪
cfae4f5acd fix: 增强 2025-05-07 22:26:25 +08:00
Mlikiowa
de541e3249 release: v4.5.50 2025-05-07 14:10:15 +00:00
手瓜一十雪
f5187c5c01 Merge pull request #989 from NapNeko/file-url-feat
feat: 消息上报Url重构
2025-05-07 22:09:29 +08:00
手瓜一十雪
9936279443 fix 2025-05-07 22:03:00 +08:00
手瓜一十雪
2818773fd4 fix 2025-05-07 20:53:36 +08:00
手瓜一十雪
b9293cbcd0 fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:51:50 +08:00
手瓜一十雪
5b9e44ddfc fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:51:28 +08:00
手瓜一十雪
1791accab7 fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:51:12 +08:00
手瓜一十雪
08081360f3 fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:50:51 +08:00
手瓜一十雪
e933a95e97 fix 2025-05-07 18:10:58 +08:00
手瓜一十雪
4ef457fe6f feat: fileUrl Get 2025-05-07 18:10:49 +08:00
Mlikiowa
bd9cae8921 release: v4.7.49 2025-05-07 09:16:28 +00:00
手瓜一十雪
303a74f8fd feat: 背压问题 2025-05-07 17:14:57 +08:00
Mlikiowa
0b7f126ce1 release: v4.7.48 2025-05-07 08:21:34 +00:00
Clansty
308b5c027f fix: at 变成负数 2025-05-07 03:46:17 +08:00
手瓜一十雪
ed3abc4b43 feat 2025-05-04 21:11:34 +08:00
Mlikiowa
87ecb3b380 release: v4.7.47 2025-05-03 14:27:49 +00:00
手瓜一十雪
7e31763a25 fix 2025-05-03 22:26:41 +08:00
Mlikiowa
c9df57d16a release: v4.7.46 2025-05-03 08:08:25 +00:00
手瓜一十雪
3d0f8ee657 fix 2025-05-03 16:06:51 +08:00
手瓜一十雪
6421bb4f5c feat: normalize 2025-05-02 15:10:31 +08:00
Mlikiowa
3919743885 release: v4.7.45 2025-04-30 13:43:59 +00:00
pk5ls20
a5a57b9e20 fix: fxxking fake forward element
- close #972, #977, #666
2025-04-30 20:31:03 +08:00
手瓜一十雪
e31d2810ad fix 2025-04-29 22:06:01 +08:00
Mlikiowa
140e62fdcd release: v4.7.44 2025-04-28 14:04:40 +00:00
手瓜一十雪
014b4deb87 feat: 34740 2025-04-28 22:04:20 +08:00
Mlikiowa
956b6cd172 release: v4.7.43 2025-04-26 11:10:37 +00:00
手瓜一十雪
bbaca3f044 fix 2025-04-26 19:10:00 +08:00
Mlikiowa
bb8a44b918 release: v4.7.42 2025-04-26 11:02:25 +00:00
手瓜一十雪
b5574d5999 fix: #976 2025-04-26 19:00:31 +08:00
手瓜一十雪
06dde072da Merge pull request #975 from pohgxz/main
接口 _get_model_show 的 model 设置为可选属性
2025-04-26 18:31:10 +08:00
Nepenthe
8e92a81bb9 接口 _get_model_show 的 model 设置为可选属性 2025-04-26 14:48:35 +08:00
Nepenthe
2c7345ae88 Merge branch 'NapNeko:main' into main 2025-04-26 14:40:38 +08:00
Mlikiowa
33d4696155 release: v4.7.41 2025-04-24 09:43:32 +00:00
手瓜一十雪
7d2dcc10e5 fix 2025-04-24 17:43:13 +08:00
Mlikiowa
e82687454c release: v4.7.40 2025-04-24 07:57:16 +00:00
手瓜一十雪
84382caebc fix 2025-04-24 15:56:55 +08:00
Mlikiowa
662530e507 release: v4.7.36 2025-04-24 07:53:59 +00:00
手瓜一十雪
edf81d0a2e feat: 34606 2025-04-24 15:37:44 +08:00
手瓜一十雪
7cbae86941 Revert "fix: 私聊撤回"
This reverts commit 8ff7420a5e.
2025-04-24 11:34:07 +08:00
手瓜一十雪
8ff7420a5e fix: 私聊撤回 2025-04-24 11:33:11 +08:00
手瓜一十雪
7ae59b1419 Merge pull request #971 from Sn0wo2/main
fix: temp_source
2025-04-24 09:54:29 +08:00
手瓜一十雪
41036f8ee8 fix: 969 2025-04-24 09:50:26 +08:00
Me0wo
380777ca04 fix: #970 2025-04-24 04:11:31 +08:00
Mlikiowa
c658cd1096 release: v4.7.35 2025-04-23 08:52:43 +00:00
手瓜一十雪
c7b9946d2f feat: doubt friends支持 2025-04-23 16:46:09 +08:00
手瓜一十雪
0caca473d6 feat: 34566 2025-04-23 16:18:48 +08:00
手瓜一十雪
3e5d35957d fix 2025-04-23 16:12:56 +08:00
手瓜一十雪
6b8b14aba2 fix: #963 2025-04-23 11:47:58 +08:00
手瓜一十雪
5db7a90a24 feat: 301 302自动跟随下载 2025-04-21 18:43:44 +08:00
Mlikiowa
88b86611a3 release: v4.7.34 2025-04-20 14:12:47 +00:00
手瓜一十雪
886fe2052e feat: 避免危险信息 2025-04-20 22:12:12 +08:00
手瓜一十雪
e4dd194d4a fix: #960
神经设计
2025-04-20 22:10:24 +08:00
手瓜一十雪
a47af60f58 feat: disband 2025-04-20 19:28:35 +08:00
Mlikiowa
35f24eb806 release: v4.7.33 2025-04-19 12:17:18 +00:00
手瓜一十雪
36e3119d34 feat: 支持https 面板 2025-04-19 20:16:24 +08:00
手瓜一十雪
8ff3ad824e feat: 支持环境变量禁用ffmpeg下载支持 2025-04-19 20:03:00 +08:00
手瓜一十雪
556000c002 feat: 优雅的回车登录 2025-04-19 19:59:11 +08:00
手瓜一十雪
fda050d3fe feat: 加强安全性 传输过程使用salt sha256 2025-04-19 19:50:52 +08:00
手瓜一十雪
b1047309c9 feat: 消息context增强识别 2025-04-19 11:36:27 +08:00
Mlikiowa
d766c4945e release: v4.7.32 2025-04-19 03:17:47 +00:00
手瓜一十雪
43c98c45b9 fix 2025-04-19 11:13:02 +08:00
手瓜一十雪
f7556b5af3 fix 2025-04-19 11:10:04 +08:00
手瓜一十雪
cd781c4cf6 feat: 回归ajv 2025-04-19 11:07:01 +08:00
手瓜一十雪
cd8698b157 fix 2025-04-19 11:03:03 +08:00
手瓜一十雪
d921dcddf1 Revert "package->dev"
This reverts commit 45d6ebf084.
2025-04-19 11:01:45 +08:00
手瓜一十雪
9f318ddaef Revert "feat: 区分resId和普通消息Id"
This reverts commit 7ecdd63bef.
2025-04-19 11:01:12 +08:00
手瓜一十雪
5c35ea11c3 Revert "fix: 修掉漏掉的"
This reverts commit 7a42f8c26f.
2025-04-19 11:01:06 +08:00
手瓜一十雪
3b16effff0 Revert "fix"
This reverts commit 40b06daf1e.
2025-04-19 10:59:04 +08:00
手瓜一十雪
d3a27ad701 Revert "fix:coerce"
This reverts commit dd895d7c17.
2025-04-19 10:58:56 +08:00
手瓜一十雪
2a4589e268 Revert "fix: checker"
This reverts commit 941978b578.
2025-04-19 10:58:46 +08:00
手瓜一十雪
80a34c82b9 Revert "fix: zod boolean强制转换"
This reverts commit 17ef3231df.
2025-04-19 10:58:39 +08:00
手瓜一十雪
fca7a65ee0 Revert "fix"
This reverts commit 3f6249f39c.
2025-04-19 10:57:36 +08:00
手瓜一十雪
30a75bc581 Revert "fix"
This reverts commit 54e6d5c3f2.
2025-04-19 10:57:32 +08:00
手瓜一十雪
7b365367f7 Revert "fix"
This reverts commit 41dccd98a9.
2025-04-19 10:57:28 +08:00
手瓜一十雪
3ed5f543e2 Revert "fix"
This reverts commit bd3e06520f.
2025-04-19 10:57:25 +08:00
手瓜一十雪
ceea50b116 Revert "fix: coerce"
This reverts commit fb20b2e16c.
2025-04-19 10:53:29 +08:00
手瓜一十雪
a5455e27d1 feat: 34467 2025-04-19 09:44:00 +08:00
手瓜一十雪
6f83d01321 feat: 34362 2025-04-18 18:19:12 +08:00
手瓜一十雪
c453b82e9f feat: #954 2025-04-18 12:12:18 +08:00
Mlikiowa
b7da316447 release: v4.7.31 2025-04-17 14:17:57 +00:00
手瓜一十雪
fb20b2e16c fix: coerce 2025-04-17 22:17:35 +08:00
Mlikiowa
9df7c341a9 release: v4.7.30 2025-04-17 10:07:28 +00:00
手瓜一十雪
7c113d6e04 fix: 一些问题 2025-04-17 18:07:07 +08:00
Mlikiowa
a6f22167ff release: v4.7.29 2025-04-17 09:59:29 +00:00
手瓜一十雪
d49e69735a fix: 自动化验证环境变量的ffmpeg 2025-04-17 17:59:06 +08:00
Mlikiowa
eca73eae18 release: v4.7.28 2025-04-17 09:49:05 +00:00
手瓜一十雪
d3a34dfdf9 feat: 增强异常处理 2025-04-17 17:48:13 +08:00
手瓜一十雪
623188d884 feat: 34362 2025-04-17 17:07:09 +08:00
Mlikiowa
f093f52792 release: v4.7.27 2025-04-17 06:39:48 +00:00
手瓜一十雪
d53607a118 fix 2025-04-17 14:39:30 +08:00
Mlikiowa
5f637e064a release: v4.7.26 2025-04-17 06:29:11 +00:00
手瓜一十雪
e4b21e94f5 feat: ffmpeg download auto 2025-04-17 14:28:51 +08:00
手瓜一十雪
fc37288827 fix: ffmpeg 2025-04-17 13:55:31 +08:00
Mlikiowa
dad7245a3a release: v4.7.25 2025-04-17 05:28:19 +00:00
手瓜一十雪
4190831081 fix: 扬了ffmpeg.wasm 2025-04-17 13:26:24 +08:00
Mlikiowa
c509a01d7d release: v4.7.24 2025-04-17 01:57:25 +00:00
手瓜一十雪
6d259593fd Merge pull request #953 from NapNeko/fix-zod-boolean
fix: zod boolean强制转换
2025-04-17 09:56:48 +08:00
手瓜一十雪
bd3e06520f fix 2025-04-17 09:56:12 +08:00
手瓜一十雪
41dccd98a9 fix 2025-04-17 09:54:12 +08:00
手瓜一十雪
54e6d5c3f2 fix 2025-04-17 09:52:03 +08:00
手瓜一十雪
3f6249f39c fix 2025-04-17 09:48:59 +08:00
手瓜一十雪
a888714629 fix: napcat log 2025-04-17 09:47:39 +08:00
手瓜一十雪
17ef3231df fix: zod boolean强制转换 2025-04-17 09:38:38 +08:00
Mlikiowa
cc30b51d58 release: v4.7.23 2025-04-15 10:38:47 +00:00
手瓜一十雪
9d40eacc15 fix: 34231 linux arm64 docker问题 2025-04-15 18:38:24 +08:00
Mlikiowa
a0415c5f4e release: v4.7.22 2025-04-15 04:35:48 +00:00
手瓜一十雪
941978b578 fix: checker 2025-04-15 12:34:33 +08:00
Mlikiowa
06538b9122 release: v4.7.21 2025-04-15 04:25:59 +00:00
手瓜一十雪
dd895d7c17 fix:coerce 2025-04-15 12:25:37 +08:00
Mlikiowa
9257a6cfde release: v4.7.20 2025-04-14 14:37:43 +00:00
手瓜一十雪
f8260067ab Merge pull request #944 from clansty/fix/isReverseOrder
fix: isReverseOrder
2025-04-14 21:44:42 +08:00
手瓜一十雪
40b06daf1e fix 2025-04-14 21:44:25 +08:00
手瓜一十雪
e30915a06b Merge pull request #946 from NapNeko/fix-923
feat: 区分resId和普通消息Id
2025-04-14 19:05:01 +08:00
手瓜一十雪
2ab3898d28 readme: new 2025-04-14 13:25:00 +08:00
Clansty
6e38e748b8 fix: isReverseOrder 2025-04-14 05:40:11 +08:00
手瓜一十雪
7ecdd63bef feat: 区分resId和普通消息Id 2025-04-13 20:33:25 +08:00
手瓜一十雪
8056962203 Merge pull request #943 from NapNeko/zod-refactor
fix: 修掉漏掉的
2025-04-13 20:17:08 +08:00
手瓜一十雪
7a42f8c26f fix: 修掉漏掉的 2025-04-13 20:14:35 +08:00
手瓜一十雪
6c510e42e8 Merge pull request #942 from NapNeko/zod-refactor
迁移类型校验到zod
2025-04-13 20:10:43 +08:00
手瓜一十雪
45d6ebf084 package->dev 2025-04-13 20:06:30 +08:00
手瓜一十雪
2147c4ffee 迁移类型校验到zod 2025-04-13 20:05:11 +08:00
Mlikiowa
d4ab191f34 release: v4.7.19 2025-04-12 04:25:39 +00:00
手瓜一十雪
a5e53a713b feat: 增强win 输出可读性 2025-04-12 12:23:38 +08:00
Mlikiowa
e3f965a9d6 release: v4.7.18 2025-04-12 04:04:09 +00:00
手瓜一十雪
dd00d4c8a5 fix: 小问题 2025-04-12 11:59:29 +08:00
手瓜一十雪
4d292a75fa fix 2025-04-12 11:36:46 +08:00
pk5ls20
50b6733f57 Merge pull request #938 from Fahaxikiii/patch-2
Update README.md
2025-04-12 00:11:23 +08:00
万里
19479b4b3c Update README.md
换成美国vps
2025-04-12 00:09:19 +08:00
pk5ls20
540f58d8ec Merge pull request #937 from Fahaxikiii/patch-1 2025-04-11 23:18:46 +08:00
万里
749f1dfcf9 Update README.md
服务器到期了呜呜呜
2025-04-11 22:03:28 +08:00
Mlikiowa
176691bb96 release: v4.7.17 2025-04-11 07:12:53 +00:00
手瓜一十雪
b9ec8ac9b1 feat: LL Framework适配 2025-04-11 15:12:32 +08:00
手瓜一十雪
28d973b9cb Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-04-11 14:40:31 +08:00
手瓜一十雪
a6be54937c feat: 删掉不必要的启动脚本 2025-04-11 14:40:22 +08:00
Mlikiowa
0664b9af84 release: v4.7.16 2025-04-11 06:21:19 +00:00
手瓜一十雪
407d8d1fd2 feat: 34231 2025-04-11 14:20:27 +08:00
手瓜一十雪
108897f6ad feat: shell 能力 launcher-user-34231 2025-04-11 14:18:12 +08:00
手瓜一十雪
3d2decb0ec feat: 34231 2025-04-11 12:47:25 +08:00
Mlikiowa
386b884f1b release: v4.7.15 2025-04-10 11:00:12 +00:00
手瓜一十雪
ace4da2297 fix: rkey server部署 2025-04-10 18:59:49 +08:00
Mlikiowa
a8fb48fb50 release: v4.7.14 2025-04-10 10:55:24 +00:00
手瓜一十雪
61f065c0c6 feat: rkey标准化&rkey server增强&简化rkey端部署 2025-04-10 18:54:18 +08:00
手瓜一十雪
d6cf6d120a feat: group_all_shut 2025-04-10 09:00:00 +08:00
手瓜一十雪
c20c19d8e0 feat: 更新类型 fetchUserDetailInfo 2025-04-08 10:12:18 +08:00
手瓜一十雪
bd8bbf76ab feat: moveGroupFile 2025-04-08 09:42:28 +08:00
手瓜一十雪
faccff1834 Merge pull request #927 from NapNeko/dependabot/npm_and_yarn/vite-plugin-cp-6.0.0
chore(deps-dev): bump vite-plugin-cp from 4.0.8 to 6.0.0
2025-04-08 09:19:58 +08:00
手瓜一十雪
99d3c5a117 Merge pull request #930 from clansty/feat/gfs
增加更多群文件相关功能
2025-04-08 09:19:41 +08:00
Clansty
31eb09edef feat: 重命名群文件 2025-04-08 05:14:53 +08:00
Clansty
4180c2d754 feat: 群文件转存永久 2025-04-08 04:40:34 +08:00
Clansty
68f5deedff feat: 移动群文件 2025-04-08 02:48:48 +08:00
dependabot[bot]
9f72196414 chore(deps-dev): bump vite-plugin-cp from 4.0.8 to 6.0.0
Bumps [vite-plugin-cp](https://github.com/fengxinming/vite-plugins/tree/HEAD/packages/vite-plugin-cp) from 4.0.8 to 6.0.0.
- [Commits](https://github.com/fengxinming/vite-plugins/commits/HEAD/packages/vite-plugin-cp)

---
updated-dependencies:
- dependency-name: vite-plugin-cp
  dependency-version: 6.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 09:33:04 +00:00
手瓜一十雪
b32efa9131 fix: 更新逻辑 2025-04-05 11:45:21 +08:00
pk5ls20
3cf502fea3 chore: improve log output for protocol fetch with multiple messages 2025-04-04 01:59:31 +08:00
手瓜一十雪
863a953ae1 style: lint 2025-04-03 15:06:34 +08:00
手瓜一十雪
a44104d8f7 Update OneBotAction.ts 2025-04-03 15:03:00 +08:00
手瓜一十雪
f602bbb0cf fix: 启用类型强制转换 2025-04-03 14:52:33 +08:00
手瓜一十雪
2807ff5927 fix: 刷新群头衔缓存 2025-04-03 14:46:56 +08:00
手瓜一十雪
4fb8e6a4da fix: typo 2025-04-03 14:44:59 +08:00
手瓜一十雪
7ec61f089d fix 2025-04-02 21:40:10 +08:00
手瓜一十雪
f7a500a8cf feat: GroupMemberTitle 2025-04-02 21:30:25 +08:00
Mlikiowa
2b319bd694 release: v4.7.13 2025-04-02 04:05:02 +00:00
手瓜一十雪
24cf7c01f8 fix: 清理文件 2025-04-02 12:04:05 +08:00
手瓜一十雪
aa50e73909 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-04-02 11:58:59 +08:00
手瓜一十雪
d9b33b5439 fix: file clean 2025-04-02 11:58:56 +08:00
Mlikiowa
da58c6bec0 release: v4.7.12 2025-04-02 03:54:37 +00:00
手瓜一十雪
c4cbac4331 fix: #918 2025-04-02 11:51:51 +08:00
手瓜一十雪
ac26a99143 fix: type 2025-04-02 10:49:48 +08:00
手瓜一十雪
640252d391 fix: 初始化后清理 2025-04-02 10:05:57 +08:00
Mlikiowa
258a1dda5e release: v4.7.11 2025-04-01 12:44:06 +00:00
手瓜一十雪
53a7ce2e46 feat: 优化webui快速登录&优化代码整体逻辑 2025-04-01 20:43:46 +08:00
手瓜一十雪
291e2fd8fd feat: 33800 2025-04-01 20:33:14 +08:00
手瓜一十雪
f180c7698f feat: 文件清理quene 2025-04-01 20:22:46 +08:00
手瓜一十雪
183d6f3011 feat: no_cache 2025-04-01 19:54:01 +08:00
bietiaop
ba71d7ad03 fix: #908 2025-03-29 21:26:34 +08:00
手瓜一十雪
26cfaac3bd fix: 增强异常处理 2025-03-29 10:50:27 +08:00
手瓜一十雪
556c8b24c0 fix 2025-03-28 16:55:19 +08:00
Mlikiowa
31f0f527b7 release: v4.7.10 2025-03-27 04:36:54 +00:00
手瓜一十雪
b2e0cab702 feat: 好友等级 2025-03-27 12:35:05 +08:00
Mlikiowa
3e3609e0f2 release: v4.7.9 2025-03-27 04:16:25 +00:00
手瓜一十雪
83e73d9842 fix: 修复一些数据问题 2025-03-27 12:15:53 +08:00
手瓜一十雪
673a175ddf fix: 烘焙raw 2025-03-26 21:51:51 +08:00
手瓜一十雪
12eacd3530 feat: 移除ws 2025-03-21 20:54:59 +08:00
手瓜一十雪
030f0551fd feat: 移除部分淘汰代码 2025-03-21 20:47:38 +08:00
Mlikiowa
47f5947410 release: v4.7.8 2025-03-21 12:43:58 +00:00
手瓜一十雪
aaefa2e83c fix: error 2025-03-21 20:43:41 +08:00
Mlikiowa
f8e92f7c8d release: v4.7.7 2025-03-21 11:48:29 +00:00
手瓜一十雪
b6430e6eb6 fix: 优化 2025-03-21 19:44:31 +08:00
手瓜一十雪
e60605c7bb feat: 简化代码逻辑 2025-03-21 19:40:47 +08:00
手瓜一十雪
58d2bd3c81 Revert "fix: #883"
This reverts commit 79aa1dc67f.
2025-03-20 10:57:57 +08:00
手瓜一十雪
6534d05b76 Revert "fix"
This reverts commit 2d7de174c5.
2025-03-20 10:57:54 +08:00
手瓜一十雪
2d7de174c5 fix 2025-03-20 10:32:30 +08:00
手瓜一十雪
79aa1dc67f fix: #883 2025-03-20 10:32:13 +08:00
Mlikiowa
7792ad9ea0 release: v4.7.6 2025-03-19 07:35:12 +00:00
手瓜一十雪
be6671923b feat: 33139 2025-03-19 15:34:33 +08:00
手瓜一十雪
0fa1b3f044 feat: 33139 2025-03-19 15:26:55 +08:00
手瓜一十雪
4ab751696b Revert "fix: image size"
This reverts commit 2759a34d96.
2025-03-19 11:58:19 +08:00
手瓜一十雪
dce4eedf7d Revert "fix: moduleResolution"
This reverts commit 9ab776d53a.
2025-03-19 11:58:16 +08:00
手瓜一十雪
129b67b751 Revert "chore(deps-dev): bump image-size from 1.2.0 to 2.0.1"
This reverts commit e3feb6a73c.
2025-03-19 11:57:55 +08:00
手瓜一十雪
9ab776d53a fix: moduleResolution 2025-03-19 11:54:29 +08:00
手瓜一十雪
2759a34d96 fix: image size 2025-03-19 11:09:57 +08:00
手瓜一十雪
2f9f42750e Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-03-19 10:45:59 +08:00
手瓜一十雪
30abd1f904 fix: #884 2025-03-19 10:45:56 +08:00
手瓜一十雪
008075466e Merge pull request #887 from NapNeko/dependabot/npm_and_yarn/eslint-import-resolver-typescript-4.0.0
chore(deps-dev): bump eslint-import-resolver-typescript from 3.9.1 to 4.0.0
2025-03-19 10:39:33 +08:00
手瓜一十雪
5b4035c320 Merge pull request #888 from NapNeko/dependabot/npm_and_yarn/image-size-2.0.1
chore(deps-dev): bump image-size from 1.2.0 to 2.0.1
2025-03-19 10:39:21 +08:00
dependabot[bot]
e3feb6a73c chore(deps-dev): bump image-size from 1.2.0 to 2.0.1
Bumps [image-size](https://github.com/image-size/image-size) from 1.2.0 to 2.0.1.
- [Release notes](https://github.com/image-size/image-size/releases)
- [Commits](https://github.com/image-size/image-size/compare/v1.2.0...v2.0.1)

---
updated-dependencies:
- dependency-name: image-size
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 09:23:32 +00:00
dependabot[bot]
40fe73317d chore(deps-dev): bump eslint-import-resolver-typescript
Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 3.9.1 to 4.0.0.
- [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases)
- [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v3.9.1...v4.0.0)

---
updated-dependencies:
- dependency-name: eslint-import-resolver-typescript
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 09:21:48 +00:00
pk5ls20
073745030c fix: #843
- maybe a temporary solution
2025-03-16 20:07:47 +08:00
Mlikiowa
c523437506 release: v4.7.5 2025-03-16 08:08:15 +00:00
手瓜一十雪
9eef570d37 fix: network prepare 2025-03-16 16:07:51 +08:00
Mlikiowa
be37b8cbbd release: v4.7.4 2025-03-16 07:57:45 +00:00
手瓜一十雪
c635496677 fix: msf Status 2025-03-16 15:57:27 +08:00
Mlikiowa
8753ecfd92 release: v4.7.3 2025-03-16 03:57:48 +00:00
手瓜一十雪
5eda1f2870 fix: quick login 2025-03-16 11:57:28 +08:00
Mlikiowa
d5a60074f7 release: v4.7.2 2025-03-16 03:55:17 +00:00
手瓜一十雪
91df57d932 fix: async error 2025-03-16 11:55:00 +08:00
Mlikiowa
e27d4c4302 release: v4.7.1 2025-03-16 03:40:48 +00:00
手瓜一十雪
55847f6e10 fix: quick login 延迟问题 2025-03-16 11:39:38 +08:00
手瓜一十雪
b39d8bae27 fix: login timer / add:不规范的promise 2025-03-16 10:42:15 +08:00
手瓜一十雪
b0cf23f775 doc: security 2025-03-16 09:36:27 +08:00
手瓜一十雪
c641246056 doc: code of conduct 2025-03-16 09:33:06 +08:00
Mlikiowa
1e5bc9bbea release: v4.7.0 2025-03-16 01:13:17 +00:00
手瓜一十雪
99b504b5f6 fix: #880 2025-03-16 09:12:52 +08:00
Mlikiowa
1146454fec release: v4.6.9 2025-03-15 10:58:09 +00:00
手瓜一十雪
805e014a75 fix: #877 2025-03-15 18:54:51 +08:00
Mlikiowa
d3acd1efc1 release: v4.6.8 2025-03-14 10:13:29 +00:00
手瓜一十雪
9fcd218a5a fix: #873 2025-03-14 18:12:58 +08:00
手瓜一十雪
d6a0830cfe fix: #875 2025-03-14 18:07:03 +08:00
手瓜一十雪
40a63b9c66 fix: #870 2025-03-14 17:53:03 +08:00
手瓜一十雪
eeb19a04cc fix: packet异常 2025-03-14 17:39:37 +08:00
Mlikiowa
91e457eb03 release: v4.6.7 2025-03-09 08:31:07 +00:00
手瓜一十雪
78d1919d7f feat: 32896 2025-03-09 16:30:43 +08:00
手瓜一十雪
8393acf173 Merge pull request #856 from HDTianRu/main
feat: 额外返回原msgSeq条目
2025-03-09 10:09:41 +08:00
手瓜一十雪
bca152a047 feat: readme 翻新 2025-03-09 10:08:49 +08:00
HDTianRu
6a15908a93 feat: 额外返回原msgSeq条目 2025-03-08 16:36:17 +08:00
bietiaop
c626bbab74 fix: #854 2025-03-07 10:25:38 +08:00
Mlikiowa
c5c7dcc6f2 release: v4.6.6 2025-03-06 10:51:30 +00:00
手瓜一十雪
03dafe727e fix: win 2025-03-06 18:51:05 +08:00
Mlikiowa
744921c45e release: v4.6.5 2025-03-06 10:09:45 +00:00
手瓜一十雪
abc4a4dcba feat: 32793 2025-03-06 18:09:14 +08:00
Mlikiowa
7e0da2f929 release: v4.6.4 2025-03-05 13:15:11 +00:00
手瓜一十雪
a3b70d0f1f fix 2025-03-05 21:14:52 +08:00
Mlikiowa
d291724f06 release: v4.6.3 2025-03-03 09:17:03 +00:00
手瓜一十雪
122a9ca2cc feat: o3拦截 2025-03-03 17:16:36 +08:00
手瓜一十雪
48aaddd32b feat:rkey 2025-03-03 12:28:55 +08:00
手瓜一十雪
47401af856 feat: searchMsgWithKeywords 2025-03-02 16:07:27 +08:00
Mlikiowa
709adfd812 release: v4.6.2 2025-03-02 07:11:16 +00:00
手瓜一十雪
038d0c5412 fix: #785 2025-03-02 14:55:47 +08:00
手瓜一十雪
6bb4362ed4 feat: 32721 2025-03-02 14:36:11 +08:00
手瓜一十雪
e617f9452d fix: #841 2025-03-02 14:32:21 +08:00
手瓜一十雪
6d8bb49a37 fix: #837 2025-03-02 14:27:09 +08:00
手瓜一十雪
4f6073ee86 fix: 837 2025-03-02 14:26:28 +08:00
手瓜一十雪
2e7176304b fix: #843 2025-03-02 14:24:51 +08:00
Mlikiowa
e36cf11004 release: v4.6.1 2025-02-27 08:35:02 +00:00
手瓜一十雪
0e49e17f68 feat: 32690 2025-02-27 16:34:09 +08:00
手瓜一十雪
524de45f6b Merge pull request #827 from NapNeko/dependabot/npm_and_yarn/globals-16.0.0
chore(deps-dev): bump globals from 15.15.0 to 16.0.0
2025-02-27 16:18:17 +08:00
手瓜一十雪
85741a4b60 feat: 32690 2025-02-27 16:14:39 +08:00
dependabot[bot]
f9ccb8c978 chore(deps-dev): bump globals from 15.15.0 to 16.0.0
Bumps [globals](https://github.com/sindresorhus/globals) from 15.15.0 to 16.0.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v15.15.0...v16.0.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-24 09:06:38 +00:00
手瓜一十雪
ea3d069e49 feat: vsc build dev体验增强 2025-02-23 17:54:19 +08:00
Mlikiowa
3e6024f183 release: v4.6.0 2025-02-23 09:31:55 +00:00
Mlikiowa
337871693a release: v4.5.24 2025-02-23 09:31:19 +00:00
手瓜一十雪
2d921c4577 feat: sisi的妙妙rkey 2025-02-23 17:30:01 +08:00
手瓜一十雪
9accff7323 fix: ts warning 2025-02-23 17:28:30 +08:00
手瓜一十雪
88b1ee8c31 docs: todo #819 2025-02-23 17:17:52 +08:00
手瓜一十雪
3ac618bb4e fix: #822 2025-02-23 17:01:00 +08:00
手瓜一十雪
0051df3741 fix: #824 2025-02-23 16:57:55 +08:00
手瓜一十雪
7eb4e010b0 Merge pull request #823 from NapNeko/refactor-worker
refactor: 即刻起逐出piscina
2025-02-23 14:31:33 +08:00
手瓜一十雪
33cc23ada3 refactor: 即刻起逐出piscina 2025-02-23 14:29:26 +08:00
手瓜一十雪
e5aee372e3 fix: 调整依赖 2025-02-23 13:40:47 +08:00
手瓜一十雪
6b6ce4a761 fix: 依赖迁移到dev 2025-02-22 12:59:37 +08:00
手瓜一十雪
8c4ea7f8f2 fix: 异常代码 2025-02-22 11:57:48 +08:00
手瓜一十雪
c8b268b806 fix: #791 2025-02-22 11:50:54 +08:00
Nepenthe
faf390bb18 Merge branch 'NapNeko:main' into main 2025-02-21 21:19:02 +08:00
手瓜一十雪
cf5e0e0f14 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-02-18 17:08:26 +08:00
手瓜一十雪
7b79f9cc17 fix: 日志显示 2025-02-18 17:08:24 +08:00
Mlikiowa
708d599966 release: v4.5.23 2025-02-18 08:56:25 +00:00
手瓜一十雪
1ecd5b78e6 feat: 文件移除path字段增强部分能力 2025-02-18 16:55:43 +08:00
手瓜一十雪
fca2e3c51a style: remove debug 2025-02-18 16:52:30 +08:00
手瓜一十雪
95ea761b2d feat: get_private_file_url 2025-02-18 16:51:51 +08:00
手瓜一十雪
6b3bfa1ee9 fix #810 2025-02-18 13:24:37 +08:00
bietiaop
df3e302a9d fix: #802 2025-02-14 21:26:16 +08:00
pk5ls20
c88a68c9a8 fix: typo x2 2025-02-14 20:52:31 +08:00
Mlikiowa
92d01b9cdd release: v4.5.22 2025-02-14 10:36:03 +00:00
手瓜一十雪
fe04fa5986 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-02-14 17:41:40 +08:00
手瓜一十雪
c382f541b4 fix: 优化文件处理错误信息并简化下载逻辑 2025-02-14 17:41:25 +08:00
手瓜一十雪
f420527207 Update msg.ts 2025-02-14 17:41:03 +08:00
手瓜一十雪
e0c83ebf79 fix: #793 2025-02-14 17:15:19 +08:00
手瓜一十雪
c7fb18fc08 feat: 补全一些type 2025-02-14 15:39:06 +08:00
手瓜一十雪
2db8ab937d feat: GetUnidirectionalFriendList router 2025-02-14 15:06:36 +08:00
手瓜一十雪
819f5dd8e5 fix: #785 2025-02-14 14:50:00 +08:00
手瓜一十雪
d4a8ed735e fix: #789 2025-02-14 14:48:36 +08:00
手瓜一十雪
f07e3bb4d5 fix: type 2025-02-14 14:44:10 +08:00
手瓜一十雪
fa5ef0c221 fix: #797 2025-02-14 14:41:16 +08:00
手瓜一十雪
da7499ec0b Merge pull request #790 from NapNeko/dependabot/npm_and_yarn/esbuild-0.25.0
chore(deps-dev): bump esbuild from 0.24.0 to 0.25.0
2025-02-14 13:51:47 +08:00
Mlikiowa
d2f4327e44 release: v4.5.21 2025-02-12 18:57:14 +00:00
pk5ls20
2eba640180 fix: typo 2025-02-13 02:56:07 +08:00
dependabot[bot]
29ae55f340 chore(deps-dev): bump esbuild from 0.24.0 to 0.25.0
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.24.0 to 0.25.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 08:16:17 +00:00
Mlikiowa
3d2bca3f9f release: v4.5.20 2025-02-09 05:05:52 +00:00
手瓜一十雪
7fd8c0c822 style:lint 2025-02-09 13:00:54 +08:00
手瓜一十雪
a9e9c81505 refactor: data recv 2025-02-09 13:00:17 +08:00
手瓜一十雪
e8cc68bdea style:lint 2025-02-09 12:53:42 +08:00
手瓜一十雪
9e51a661a4 fix: #761 2025-02-09 12:53:10 +08:00
bietiaop
a167aaf55f style: 修改首页卡片色适配主题 2025-02-09 12:28:57 +08:00
bietiaop
a54ecbcaa0 style: 修改侧边栏标题色适配主题 2025-02-09 12:21:34 +08:00
bietiaop
788462cdfa fix: 修复heroui primary色 2025-02-09 12:13:43 +08:00
bietiaop
45c5965b99 style: 增加heroui主题色 2025-02-09 12:11:27 +08:00
bietiaop
ce7614de46 fix: 缺少default 2025-02-09 12:00:02 +08:00
bietiaop
9f78e1ce1e feat: 预定义主题 2025-02-09 11:58:46 +08:00
pk5ls20
2c7b0625e8 chore: format 2025-02-09 01:35:37 +08:00
pk5ls20
c3a5da9be1 feat: #768 2025-02-09 01:33:56 +08:00
bietiaop
ca796e1920 feat: 设置快速登录QQ & 自定义webui主题色
feat: 设置快速登录QQ & 自定义webui主题色
2025-02-09 00:54:27 +08:00
bietiaop
7ce04cf781 final 2025-02-09 00:47:00 +08:00
bietiaop
024a3eb760 fix 2025-02-09 00:18:14 +08:00
bietiaop
1702f429b4 fix 2025-02-09 00:17:49 +08:00
bietiaop
96d79cf495 fix 2025-02-08 23:45:33 +08:00
bietiaop
a6a11a7026 fix 2025-02-08 23:38:30 +08:00
bietiaop
970a49e2a5 fix: 猪咪 2025-02-08 23:05:48 +08:00
bietiaop
2e013ed4f5 fix 2025-02-08 22:43:53 +08:00
bietiaop
f8c396b1fe feat(webui): 快速登录config 2025-02-08 21:16:49 +08:00
手瓜一十雪
b54870cb60 fix 2025-02-08 21:03:59 +08:00
bietiaop
84318acb18 feat(webui): theme 2025-02-08 21:01:29 +08:00
手瓜一十雪
a11a042b93 docs: update 2025-02-08 20:22:51 +08:00
Mlikiowa
8a8aa8f62c release: v4.5.18 2025-02-08 09:43:06 +00:00
手瓜一十雪
93f78f4db5 feat: #780 2025-02-08 17:34:31 +08:00
手瓜一十雪
404bfdd5e6 fix: #783 2025-02-08 17:00:11 +08:00
Mlikiowa
e4577dc2f1 release: v4.5.17 2025-02-07 12:40:47 +00:00
pk5ls20
5c932e5a27 fix: native rkey 2025-02-07 19:20:35 +08:00
Mlikiowa
4bd63c6267 release: v4.5.16 2025-02-07 10:02:35 +00:00
bietiaop
aabe24f903 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-02-07 18:00:31 +08:00
bietiaop
69cebd7fbc feat: 提示修改默认密码 2025-02-07 18:00:22 +08:00
Mlikiowa
8da371176a release: v4.5.15 2025-02-07 09:52:51 +00:00
手瓜一十雪
dd08adf1d1 fix 2025-02-07 17:43:08 +08:00
手瓜一十雪
2f67bef139 fix: #775 2025-02-07 17:25:48 +08:00
手瓜一十雪
8968c51cdc fix: 砍掉mac pty 沙盒权限不足 2025-02-07 17:11:10 +08:00
手瓜一十雪
f2fdcc9289 feat: webui体验优化 2025-02-07 13:56:48 +08:00
手瓜一十雪
aa3a575cbe feat: 优化初始化步骤 2025-02-07 13:26:48 +08:00
bietiaop
11816d038d fix: #776 2025-02-06 20:10:11 +08:00
Mlikiowa
6a990edb38 release: v4.5.14 2025-02-06 09:17:22 +00:00
手瓜一十雪
fa12865924 fix: error 2025-02-06 17:10:30 +08:00
Mlikiowa
ecdd717742 release: v4.5.12 2025-02-06 08:23:07 +00:00
bietiaop
6851334af9 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-02-06 15:29:04 +08:00
bietiaop
9051b29565 feat: 字体修改#771 2025-02-06 15:28:42 +08:00
手瓜一十雪
95c7d3dfbd fix: remove __dirname 2025-02-06 15:28:24 +08:00
手瓜一十雪
bc1148c00a fix: require_dlopen 2025-02-06 15:25:47 +08:00
Mlikiowa
d4556d9299 release: v4.5.11 2025-02-06 03:13:17 +00:00
pk5ls20
5d389a2359 fix: fake forwardMsg construct 2025-02-06 01:09:23 +08:00
Mlikiowa
305116874b release: v4.5.10 2025-02-05 11:49:14 +00:00
bietiaop
b08a29897f fix: #769 2025-02-05 19:45:30 +08:00
Mlikiowa
b59c1d9122 release: v4.5.9 2025-02-05 11:14:25 +00:00
手瓜一十雪
adb9cea701 Merge pull request #765 from NapNeko/fix/multi-forward-protocol-fetch
fix: #721
2025-02-05 19:08:08 +08:00
Mlikiowa
5e148d2e82 release: v4.5.8 2025-02-05 11:02:28 +00:00
手瓜一十雪
a0d780558e fix 2025-02-05 19:01:14 +08:00
Mlikiowa
ad56065a4e release: v4.5.7 2025-02-05 07:10:27 +00:00
手瓜一十雪
f5dee80b6e Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-02-05 15:09:27 +08:00
手瓜一十雪
9cc75881b8 fix: arm64 2025-02-05 14:51:12 +08:00
bietiaop
593fb13b61 style: 语义化样式 2025-02-05 10:38:12 +08:00
pk5ls20
fca90592d6 try fix: #755 2025-02-05 08:29:37 +08:00
pk5ls20
d6848e2855 fix: #721 2025-02-05 08:07:58 +08:00
bietiaop
7539a4129f fix: 获取歌单 2025-02-04 22:14:23 +08:00
bietiaop
5402574266 feat: AI更新总结 2025-02-04 22:03:37 +08:00
Mlikiowa
853175aa1a release: v4.5.6 2025-02-04 13:24:46 +00:00
手瓜一十雪
feb84809ec fix: #761 2025-02-04 21:22:36 +08:00
bietiaop
a812c568e4 fix: 文件预览 2025-02-04 21:12:13 +08:00
bietiaop
11db25e355 fix: 文件预览 2025-02-04 21:08:28 +08:00
手瓜一十雪
ecd2fba629 fix: #762 2025-02-04 20:42:13 +08:00
Mlikiowa
a6763cf5a1 release: v4.5.5 2025-02-04 11:50:37 +00:00
手瓜一十雪
c9e91a9b94 fix: defalut config 2025-02-04 19:49:56 +08:00
Mlikiowa
43fb62c5bd release: v4.5.4 2025-02-04 11:35:51 +00:00
手瓜一十雪
cb8727d487 fix: reload and parse msg 2025-02-04 19:34:51 +08:00
Mlikiowa
a94e03e2fd release: v4.5.3 2025-02-04 10:16:07 +00:00
手瓜一十雪
425c3c6432 fix: 避免重复reload 2025-02-04 18:14:13 +08:00
手瓜一十雪
89b9610016 fix: 避免read异常 2025-02-04 18:13:42 +08:00
手瓜一十雪
62fe88f868 Merge pull request #760 from NapNeko/config-refactor
refactor
2025-02-04 18:09:57 +08:00
手瓜一十雪
11a7f5fade refactor 2025-02-04 18:09:30 +08:00
bietiaop
fbde997f7c style: 调整样式 2025-02-04 17:58:38 +08:00
bietiaop
26734a35ef fix: 文件下载 2025-02-04 15:31:10 +08:00
Mlikiowa
715c4ac534 release: v4.5.2 2025-02-04 06:52:11 +00:00
bietiaop
bd4b0885a1 fix: 预览 2025-02-04 14:47:38 +08:00
手瓜一十雪
e3c7af3d91 fix: 解决nonebot可能卡死问题 2025-02-04 14:42:14 +08:00
手瓜一十雪
a7ee21bfd8 fix: #757 2025-02-04 14:34:55 +08:00
手瓜一十雪
d0f51d92ac feat: tailwind css 2025-02-04 13:52:53 +08:00
手瓜一十雪
e6dc148ea2 fix: diy status问题 2025-02-04 13:44:35 +08:00
手瓜一十雪
514ab6637f feat: 全局字体优化 2025-02-04 13:37:11 +08:00
bietiaop
377794abe8 style: font & terminal
style: font & terminal
2025-02-04 13:09:00 +08:00
bietiaop
0f3251f35b fix: 字体、终端样式 2025-02-04 12:59:51 +08:00
手瓜一十雪
8002dc5bc5 fix 2025-02-04 00:16:59 +08:00
手瓜一十雪
c75a13dcf4 fix 2025-02-04 00:14:15 +08:00
Mlikiowa
91d153bb9d release: v4.5.1 2025-02-03 12:48:00 +00:00
bietiaop
b32f9fa397 feat: 文件下载/上传 2025-02-03 19:56:33 +08:00
Mlikiowa
80593730ae release: v4.4.20 2025-02-03 08:36:59 +00:00
手瓜一十雪
090d54a78d fix: typo 2025-02-03 16:36:25 +08:00
Mlikiowa
b7d1fb181c release: v4.4.19 2025-02-03 08:34:24 +00:00
手瓜一十雪
6e56693ca7 feat: 支持set_diy_online_status 2025-02-03 16:33:31 +08:00
Mlikiowa
7403db9b20 release: v4.4.18 2025-02-03 07:05:56 +00:00
手瓜一十雪
9d167cd883 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-02-03 15:05:16 +08:00
手瓜一十雪
197eec40ad fix 2025-02-03 15:05:12 +08:00
Mlikiowa
07819a6618 release: v4.4.17 2025-02-03 06:50:58 +00:00
手瓜一十雪
b72156866d fix: broken pipe 2025-02-03 14:50:11 +08:00
手瓜一十雪
59a7d12a8c style: lint 2025-02-03 14:29:38 +08:00
手瓜一十雪
179351b23a Merge pull request #754 from NapNeko/qrcode-refactor
fix: thumb残留
2025-02-03 14:11:22 +08:00
手瓜一十雪
790809e8e5 fix: thumb残留 2025-02-03 14:09:51 +08:00
手瓜一十雪
1414a8a8c9 Merge pull request #753 from NapNeko/qrcode-refactor
refactor: qrcode to ts
2025-02-03 14:02:48 +08:00
bietiaop
9ab41734a5 fix: piscina src 2025-02-03 13:33:18 +08:00
手瓜一十雪
03cace2ea1 优化依赖 2025-02-03 13:07:05 +08:00
手瓜一十雪
c7371ab869 refactor: qrcode to ts 2025-02-03 12:51:50 +08:00
手瓜一十雪
b32d4b618c fix: 必须清理并回收 2025-02-03 11:40:58 +08:00
手瓜一十雪
3a27f37686 fix: pcm清理 2025-02-03 11:37:39 +08:00
手瓜一十雪
fe2d21979d Merge pull request #749 from NapNeko/type-force
style: Type force
2025-02-03 11:13:55 +08:00
手瓜一十雪
48b1f3d4f0 Merge branch 'main' into type-force 2025-02-03 11:13:46 +08:00
手瓜一十雪
93ed589ac7 Merge pull request #745 from NapNeko/dev-terminal
feat: 文件管理 & 系统终端
2025-02-03 11:13:14 +08:00
手瓜一十雪
96de9e2c16 style: 简化loader 避免全局error 2025-02-03 10:40:33 +08:00
手瓜一十雪
b25f9d3bec feat: 摇树生成&多平台统一改造 2025-02-03 10:33:10 +08:00
手瓜一十雪
15854c605b style: 强类型大法 2025-02-02 23:22:21 +08:00
手瓜一十雪
ac193cc94a style: lint 2025-02-02 20:17:28 +08:00
手瓜一十雪
d626f872e6 feat: 类型规范 2025-02-02 20:16:11 +08:00
bietiaop
3eb66fa34a fix: 关闭终端启动QQ 2025-02-02 15:34:53 +08:00
bietiaop
0fdd0175b7 fix: 重复关闭 2025-02-02 15:09:38 +08:00
pk5ls20
dec9b477e0 fix: #747 2025-02-02 14:51:36 +08:00
bietiaop
a0a4b0dd1d fix: 频率限制 2025-02-02 14:47:34 +08:00
bietiaop
8dc6da56a7 fix: node-pty 2025-02-02 14:33:39 +08:00
bietiaop
b4e07aacfe chore(terminal): 使用prebuild 2025-02-02 12:59:00 +08:00
bietiaop
19b47f0f42 fix: id生成使用uuid 2025-02-02 12:00:55 +08:00
bietiaop
f9ef3d63c7 fix: id生成使用uuid 2025-02-02 11:57:28 +08:00
bietiaop
2b574d33b5 fix: 更换重命名图标防止误解 2025-02-02 11:46:04 +08:00
bietiaop
6039e9bb46 feat: 文件管理 2025-02-02 11:37:58 +08:00
手瓜一十雪
adfd4b043f docs: fix 2025-02-02 11:02:54 +08:00
bietiaop
719189be55 feat: file manager 2025-02-01 22:47:51 +08:00
bietiaop
ef9907f4b6 style: 优化样式 2025-02-01 20:51:45 +08:00
bietiaop
16b7447df1 style: 优化样式 2025-02-01 20:47:54 +08:00
bietiaop
4157746478 feat: 系统终端 2025-02-01 20:35:01 +08:00
bietiaop
5120786708 Merge branch 'dev-terminal' of https://github.com/NapNeko/NapCatQQ into dev-terminal 2025-02-01 13:44:18 +08:00
bietiaop
0176fa75ef dev: terminal 2025-02-01 13:41:20 +08:00
bietiaop
e6968f2d80 fix(webui): name重复问题 2025-02-01 11:44:30 +08:00
bietiaop
c0dd8a53e8 chore(dep): 更新依赖,移除overrides(strtok3已经修复) 2025-01-31 19:05:40 +08:00
bietiaop
3cb3135235 feat: 登录状态机 2025-01-31 18:48:46 +08:00
bietiaop
28182cac64 chore(dep): 更新webui依赖 2025-01-31 12:07:57 +08:00
bietiaop
73b80d2482 release: v4.4.16 2025-01-30 10:42:46 +08:00
bietiaop
f22eb22409 release: v4.4.16 2025-01-30 10:12:35 +08:00
bietiaop
4a95b17a47 feat(webui): 修改token 2025-01-29 22:08:45 +08:00
bietiaop
f4a71159fd fix(dep): 尝试解决peek-readable依赖问题 2025-01-29 21:34:59 +08:00
bietiaop
c0431e3dc2 feat(webui_api): update token 2025-01-29 20:40:23 +08:00
Mlikiowa
7f87cee282 release: v4.4.15 2025-01-27 11:53:31 +00:00
手瓜一十雪
c24c704439 fix: clone object 2025-01-27 19:53:03 +08:00
Mlikiowa
232e5d55b8 release: v4.4.14 2025-01-27 11:31:15 +00:00
手瓜一十雪
da24ae7e1c fix: 空json5 2025-01-27 19:30:45 +08:00
Mlikiowa
8fc13f8a8f release: v4.4.13 2025-01-27 11:26:17 +00:00
手瓜一十雪
7e1fe31085 fix: http支持json5 2025-01-27 19:25:51 +08:00
Mlikiowa
c3cba8ba4e release: v4.4.12 2025-01-27 10:44:43 +00:00
手瓜一十雪
ba619986c9 fix: #739 2025-01-27 18:42:26 +08:00
bietiaop
dcef3f3c3b fix: 终端字符宽度&微调样式&路由切换动画 2025-01-27 15:58:27 +08:00
bietiaop
823faa2790 fix: 质量检查 2025-01-26 21:58:04 +08:00
bietiaop
ef4248d2a3 fix: 依赖缺失 2025-01-26 21:52:35 +08:00
bietiaop
3917cb0dc9 feat: webui检查更新&修复日志字体渲染 2025-01-26 21:48:45 +08:00
Mlikiowa
520cec0eaa release: v4.4.11 2025-01-26 13:03:34 +00:00
手瓜一十雪
e7655e0ff6 Merge pull request #737 from NapNeko/ffmpeg
feat: no spawn ffmpeg
2025-01-26 21:02:57 +08:00
手瓜一十雪
350ced55c0 fix 2025-01-26 21:00:47 +08:00
手瓜一十雪
2ca6d0a00e feat: 移除测试代码 2025-01-26 20:44:27 +08:00
手瓜一十雪
844abad0d0 fix: 彻底完成迁移 2025-01-26 20:42:05 +08:00
手瓜一十雪
d278e9d8bc feat: ffmpeg 2025-01-26 16:26:21 +08:00
手瓜一十雪
6e261f30c2 fix: typo 2025-01-26 13:29:47 +08:00
bietiaop
84f0e43369 feat: debug 配置记忆 2025-01-26 10:44:31 +08:00
Nepenthe
941b30847b Merge branch 'NapNeko:main' into main 2025-01-25 23:14:26 +08:00
Mlikiowa
3223a06983 release: v4.4.10 2025-01-25 12:08:25 +00:00
手瓜一十雪
1b874a0264 fix: defaultHttpUrl 2025-01-25 20:00:24 +08:00
Mlikiowa
26525a0ff9 release: v4.4.9 2025-01-25 10:59:08 +00:00
手瓜一十雪
49234ea5c7 fix: music proxy 2025-01-25 18:34:11 +08:00
Mlikiowa
c474158a09 release: v4.4.8 2025-01-25 05:40:09 +00:00
bietiaop
813a541e7f fix: config首次加载 2025-01-25 13:36:56 +08:00
Mlikiowa
efd489bfd4 release: v4.4.7 2025-01-25 04:56:25 +00:00
手瓜一十雪
9c88fcb610 style: lint 2025-01-25 12:56:02 +08:00
手瓜一十雪
c1cac8de19 fix: #736 2025-01-25 12:54:39 +08:00
手瓜一十雪
b03fa54e63 fix: fonts 2025-01-25 12:40:01 +08:00
bietiaop
9785731f25 fix: 字体路径 2025-01-25 10:50:47 +08:00
手瓜一十雪
566afde18b fix 2025-01-25 10:41:15 +08:00
手瓜一十雪
bae8dabc5c fix: 兼容JSON配置 异常检查 2025-01-25 10:24:39 +08:00
手瓜一十雪
0a9dfea20a fix 2025-01-25 01:21:06 +08:00
Mlikiowa
50498aa52d release: v4.4.6 2025-01-24 16:41:07 +00:00
手瓜一十雪
cc99fa8346 fix: renderer 2025-01-25 00:37:33 +08:00
bietiaop
500c10ea7a fix: 移除部分字体 2025-01-24 22:44:18 +08:00
bietiaop
71a62caf8f fix: sse配置缺失 2025-01-24 22:36:40 +08:00
bietiaop
fe9c565ad4 fix: 字体缺失 2025-01-24 22:23:45 +08:00
手瓜一十雪
f5f915dc91 fix: 移除nc_openwebui_ex_react 2025-01-24 22:09:41 +08:00
手瓜一十雪
eba17fd9b4 Merge pull request #734 from bietiaop/main
feat: 新版webui
2025-01-24 21:56:32 +08:00
bietiaop
86f6caa714 fix: 移除统计&添加环境变量 2025-01-24 21:44:29 +08:00
手瓜一十雪
8ec5a4d071 Merge branch 'main' into pr/734 2025-01-24 21:42:49 +08:00
手瓜一十雪
eae49667ef fix: GetProfileLike 2025-01-24 21:39:53 +08:00
bietiaop
9a87b5ec1a Merge branch 'NapNeko:main' into main 2025-01-24 21:13:57 +08:00
bietiaop
ee1291e42c feat: 新版webui 2025-01-24 21:13:44 +08:00
手瓜一十雪
15aa1fd0b8 fix: NTVoteInfo type 2025-01-24 21:03:15 +08:00
手瓜一十雪
e069e0e8aa re: 移除 FetchUserProfileLike 2025-01-24 20:42:19 +08:00
手瓜一十雪
57e72c197f fix: #733 2025-01-24 20:39:52 +08:00
手瓜一十雪
1d0d25eea2 fix: re at
6b041becb0
2025-01-24 18:55:10 +08:00
手瓜一十雪
1a6194b38c fix: Schema miss 2025-01-22 15:36:49 +08:00
手瓜一十雪
22057083ce fix 2025-01-22 15:35:21 +08:00
Wesley F. Young
6b041becb0 fix: convert signed atUin into unsigned int 2025-01-22 15:31:56 +08:00
Mlikiowa
96d4a91ee9 release: v4.4.4 2025-01-22 07:13:53 +00:00
手瓜一十雪
3069900202 fix: fallback 2025-01-22 15:07:44 +08:00
pk5ls20
c46fb0f48a fix: 6.9.63 -> 6.9.65 [skip ci] 2025-01-22 14:50:48 +08:00
pk5ls20
07cd8f883e feat: 31363 2025-01-22 14:47:48 +08:00
手瓜一十雪
cfdb9d64ad fix: #722 2025-01-22 14:45:02 +08:00
Mlikiowa
b73e3aa3b7 release: v4.4.3 2025-01-22 01:35:33 +00:00
手瓜一十雪
cd315b0e71 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-01-22 09:33:23 +08:00
手瓜一十雪
4d4d79e66f fix #727 2025-01-22 09:33:19 +08:00
手瓜一十雪
395ce97a78 Merge pull request #726 from Caulic/main
fix: 无法修改网络配置表单值
2025-01-22 09:16:53 +08:00
Caulic
e44e8423d0 fix: 无法修改网络配置表单值 2025-01-22 01:16:35 +08:00
手瓜一十雪
fa13a56697 Merge pull request #725 from clansty/feat/face_resultId
update face list
2025-01-21 22:45:25 +08:00
Clansty
6383164aec update face list 2025-01-21 22:42:37 +08:00
Mlikiowa
d9adfad1c0 release: v4.4.2 2025-01-21 14:15:28 +00:00
手瓜一十雪
901828f5a6 Merge pull request #724 from clansty/feat/face_resultId
为新的接龙表情提供 resultId 和 chainCount 返回
2025-01-21 22:14:18 +08:00
Clansty
2a4b0cbc09 force resultId to string 2025-01-21 22:10:52 +08:00
Clansty
c5434efd56 为新的接龙表情提供 resultId 和 chainCount 返回 2025-01-21 22:02:35 +08:00
手瓜一十雪
b73f283095 Merge pull request #717 from NapNeko/dependabot/npm_and_yarn/file-type-20.0.0
chore(deps-dev): bump file-type from 19.6.0 to 20.0.0
2025-01-21 21:55:45 +08:00
Mlikiowa
24ef54f01c release: v4.4.1 2025-01-21 13:46:31 +00:00
手瓜一十雪
bff3b85337 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-01-21 21:46:08 +08:00
手瓜一十雪
811d9a7237 fix 2025-01-21 21:46:00 +08:00
Mlikiowa
a764cb8dc2 release: v4.4.0 2025-01-21 13:45:37 +00:00
手瓜一十雪
9204b9b286 fix: #723 2025-01-21 21:43:11 +08:00
手瓜一十雪
da94faa9bb fix 2025-01-21 20:41:13 +08:00
手瓜一十雪
4b53e9a895 fix: 进一步重构 2025-01-21 20:40:52 +08:00
Mlikiowa
f5db96187b release: v4.3.9 2025-01-20 09:35:55 +00:00
手瓜一十雪
857b191b03 feat: sse完全体 2025-01-20 17:35:31 +08:00
Mlikiowa
09014d1ab5 release: v4.3.8 2025-01-20 09:18:32 +00:00
手瓜一十雪
7557b71869 fix: httpSseServers DefaultConfig 2025-01-20 17:02:52 +08:00
dependabot[bot]
9c4751794f chore(deps-dev): bump file-type from 19.6.0 to 20.0.0
Bumps [file-type](https://github.com/sindresorhus/file-type) from 19.6.0 to 20.0.0.
- [Release notes](https://github.com/sindresorhus/file-type/releases)
- [Commits](https://github.com/sindresorhus/file-type/compare/v19.6.0...v20.0.0)

---
updated-dependencies:
- dependency-name: file-type
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 08:59:49 +00:00
Mlikiowa
d07187bd5d release: v4.3.7 2025-01-20 08:48:13 +00:00
手瓜一十雪
2c6a6ba440 fix: type 2025-01-20 16:47:06 +08:00
手瓜一十雪
4592bf7817 fix: nickname可能为null 2025-01-20 16:06:34 +08:00
Mlikiowa
afd6d450a0 release: v4.3.6 2025-01-20 06:51:29 +00:00
手瓜一十雪
b134849dcf feat: 支持文件名发送&兼容单空格问题 2025-01-20 14:51:08 +08:00
手瓜一十雪
e7d0f6d6da feat: 更合适的记录与rkey限制 2025-01-19 15:55:56 +08:00
手瓜一十雪
16a29b0127 feat: file 2025-01-19 15:47:09 +08:00
pk5ls20
1f5596ef16 Merge pull request #715 from FfmpegZZZ/main
chore:移除失效链接
2025-01-19 15:19:01 +08:00
Ffmpeg
bef05432d0 Update README.md 2025-01-19 15:10:18 +08:00
Nepenthe
4c5a26698e Merge branch 'NapNeko:main' into main 2025-01-15 21:33:43 +08:00
手瓜一十雪
67533d7743 docs: 已重写部分实现 2025-01-13 20:37:43 +08:00
Mlikiowa
0cc86c6348 release: v4.3.5 2025-01-13 12:36:23 +00:00
手瓜一十雪
607dd68620 refactor: 标准化与提高缓存策略 2025-01-13 20:35:52 +08:00
手瓜一十雪
7c8cbc0799 style: lint 2025-01-13 20:30:57 +08:00
手瓜一十雪
ec0c2e8c33 refactor: 大部分文件处理部分 2025-01-13 20:30:08 +08:00
手瓜一十雪
7f3dbe0552 fix: groupfile file_size 2025-01-13 19:38:29 +08:00
手瓜一十雪
0e9044e0c8 drop: umami 2025-01-13 19:32:34 +08:00
手瓜一十雪
3171640193 Merge pull request #701 from Stapxs/main
HTTP SSE 消息上报模式
2025-01-13 19:26:56 +08:00
手瓜一十雪
a56cee3485 fix: #682 2025-01-13 19:25:08 +08:00
pk5ls20
c8ee371982 feat: packet ocr 2025-01-12 10:13:12 +08:00
pk5ls20
5778daeb60 refactor: packet 2025-01-11 16:23:02 +08:00
手瓜一十雪
f51f3b9861 fix 2025-01-11 14:29:44 +08:00
Mlikiowa
44dd1a0b02 release: v4.3.4 2025-01-11 04:16:54 +00:00
pk5ls20
61a00ffcbf feat: 31245 2025-01-11 12:10:46 +08:00
pk5ls20
4b0a0f0a32 feat: #702 2025-01-10 15:23:40 +08:00
stapxs
a3088fb8bc 质量保障 2025-01-09 13:42:55 +08:00
stapxs
88fd1f9eb1 http sse 消息上报模式 2025-01-09 13:22:46 +08:00
手瓜一十雪
15156bac1e fix: log 2025-01-07 20:49:49 +08:00
Mlikiowa
a898d2e7be release: v4.3.3 2025-01-07 11:31:56 +00:00
手瓜一十雪
95b003802c fix: win 31245 2025-01-07 19:31:35 +08:00
Mlikiowa
95c9eae4ed release: v4.3.2 2025-01-07 11:30:35 +00:00
手瓜一十雪
e3814403e4 fix: #682 2025-01-07 19:28:46 +08:00
手瓜一十雪
3d16d52dd8 fix: #696 fallback 2025-01-07 18:51:04 +08:00
Mlikiowa
1ae47fffb4 release: v4.2.68 2025-01-06 13:08:43 +00:00
手瓜一十雪
4e7096b9e2 feat: offset win 31219 2025-01-06 21:08:18 +08:00
Mlikiowa
8cc9b7f6a7 release: v4.2.67 2025-01-05 02:50:38 +00:00
手瓜一十雪
fb45c1020e revert: 再也不用umami了 2025-01-05 10:49:49 +08:00
Mlikiowa
e9db4ae8f4 release: v4.2.66 2025-01-04 11:52:38 +00:00
手瓜一十雪
c46ec32bd6 fix: 简化代码 2025-01-04 13:12:04 +08:00
手瓜一十雪
c58a26ed99 fix 2025-01-04 13:06:37 +08:00
手瓜一十雪
a66f5e4971 fix 2025-01-04 13:03:29 +08:00
pk5ls20
574c8c6089 fix: SendGroupAiRecord
- smtx
2025-01-04 12:48:53 +08:00
手瓜一十雪
67afd95910 feat: getUserDetailInfoV2 2025-01-03 21:38:57 +08:00
手瓜一十雪
f7d0cb0be7 fix 2025-01-03 21:07:26 +08:00
手瓜一十雪
be9b68a0b1 feat: CancelableTask&Fallback 2025-01-03 20:46:51 +08:00
Mlikiowa
4637414af2 release: v4.2.65 2025-01-03 05:14:05 +00:00
手瓜一十雪
4bd92a72bd fix: ua agent 2025-01-03 13:09:43 +08:00
手瓜一十雪
a3be26f3e4 fix: error 2025-01-03 12:44:55 +08:00
Mlikiowa
675c906cbf release: v4.2.64 2024-12-31 14:42:17 +00:00
手瓜一十雪
6be6023236 Merge pull request #679 from wu-yafeng/main
fix #678
2024-12-31 22:41:34 +08:00
WuYafeng
42cee0d018 在没有写入权限时记录warn日志 2024-12-31 22:38:01 +08:00
WuYafeng
041f725748 Update config.ts 2024-12-31 22:25:05 +08:00
WuYafeng
0594d61631 Update config.ts 2024-12-31 22:20:34 +08:00
Mlikiowa
15cae6b765 release: v4.2.63 2024-12-31 13:52:37 +00:00
pk5ls20
b984116c35 fix: #677 2024-12-31 21:34:45 +08:00
Mlikiowa
13bda6e3f4 release: v4.2.62 2024-12-31 08:35:55 +00:00
手瓜一十雪
c0d18549d1 fix 2024-12-31 15:09:08 +08:00
手瓜一十雪
3caff72fce fix: cache trace 2024-12-31 13:10:58 +08:00
Mlikiowa
1313e9c3f4 release: v4.2.61 2024-12-30 15:59:33 +00:00
手瓜一十雪
0848d5a39e feat: clouflare 2024-12-30 23:59:09 +08:00
手瓜一十雪
7660646059 fix 2024-12-30 22:16:09 +08:00
Mlikiowa
bcd90fc744 release: v4.2.60 2024-12-30 12:48:11 +00:00
手瓜一十雪
638fc22d62 fix:error 2024-12-30 20:47:50 +08:00
手瓜一十雪
c87d365b88 fix 2024-12-30 20:42:23 +08:00
Mlikiowa
aee9602f25 release: v4.2.59 2024-12-30 12:29:33 +00:00
手瓜一十雪
976fbd0220 fix: 修复精确统计 2024-12-30 20:27:28 +08:00
手瓜一十雪
afd955d06f fix: error 2024-12-30 20:14:51 +08:00
手瓜一十雪
4d548da66b fix: console-log 2024-12-30 19:56:06 +08:00
手瓜一十雪
41b70f53d1 fix: LoginErrorCode 2024-12-30 19:29:14 +08:00
Wesley F. Young
a47a618bcd make author NapNeko in manifest.json
毕竟我已经脱离一线开发挺久了(
2024-12-30 18:40:57 +08:00
Mlikiowa
62170a30af release: v4.2.58 2024-12-30 09:56:02 +00:00
手瓜一十雪
780c5ac23c fix 2024-12-30 17:55:40 +08:00
手瓜一十雪
9fba519a5a fix 2024-12-30 17:55:13 +08:00
Mlikiowa
3cd0e7d26b release: v4.2.57 2024-12-30 09:54:40 +00:00
手瓜一十雪
a8fd6af994 Merge pull request #676 from NapNeko/dependabot/npm_and_yarn/commander-13.0.0
chore(deps-dev): bump commander from 12.1.0 to 13.0.0
2024-12-30 17:54:03 +08:00
手瓜一十雪
4000b89644 fix 2024-12-30 17:53:05 +08:00
Mlikiowa
9c00bbc0b7 release: v4.2.56 2024-12-30 08:55:36 +00:00
手瓜一十雪
a2989d3b38 fix:workname 2024-12-30 16:54:40 +08:00
Mlikiowa
fc731b60d5 release: v4.2.54 2024-12-30 08:47:57 +00:00
手瓜一十雪
193980dd4a fix: trace 2024-12-30 16:47:33 +08:00
dependabot[bot]
35427b0768 chore(deps-dev): bump commander from 12.1.0 to 13.0.0
Bumps [commander](https://github.com/tj/commander.js) from 12.1.0 to 13.0.0.
- [Release notes](https://github.com/tj/commander.js/releases)
- [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tj/commander.js/compare/v12.1.0...v13.0.0)

---
updated-dependencies:
- dependency-name: commander
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-30 08:36:27 +00:00
Mlikiowa
73ea130e40 release: v4.2.53 2024-12-30 08:34:23 +00:00
手瓜一十雪
5667e6aaee fix 2024-12-30 16:34:01 +08:00
Mlikiowa
fbd626131d release: v4.2.52 2024-12-30 08:17:53 +00:00
手瓜一十雪
7b82444338 fix 2024-12-30 16:17:29 +08:00
Mlikiowa
8108b9f565 release: v4.2.51 2024-12-30 08:15:48 +00:00
手瓜一十雪
c6ddd00cd9 Merge pull request #675 from NapNeko/test
fix: 重写远程遥测
2024-12-30 16:14:48 +08:00
手瓜一十雪
20c0c00fa0 fixfix 2024-12-30 16:14:32 +08:00
手瓜一十雪
1f90364ba6 fix: 重写远程遥测 2024-12-30 16:10:38 +08:00
手瓜一十雪
49ea4d31a5 fix 2024-12-30 13:23:33 +08:00
Mlikiowa
dc35f1456a release: v4.2.50 2024-12-30 04:33:08 +00:00
手瓜一十雪
0ebeb90804 fix 2024-12-30 12:31:43 +08:00
手瓜一十雪
3ef5436c98 Merge pull request #673 from NapNeko/fixseq
fix: #670
2024-12-30 12:28:29 +08:00
手瓜一十雪
de7996d789 refactor: 进群入群 2024-12-30 12:27:42 +08:00
手瓜一十雪
ac52d9bae2 fix: 算了能用就行 2024-12-30 11:59:16 +08:00
手瓜一十雪
cb02df3b76 Merge pull request #672 from clansty/feat/download_file-file-proto
feat: download_file 支持 file://
2024-12-29 22:45:27 +08:00
手瓜一十雪
5fc5a6f1a6 fix: #670 2024-12-29 22:44:25 +08:00
手瓜一十雪
726a0d0394 fix 2024-12-29 22:30:33 +08:00
Clansty
6edf5345a3 chore: remove unused import 2024-12-29 22:27:41 +08:00
Clansty
242bbfdb14 chore: uriToLocalFile 2024-12-29 22:03:04 +08:00
Clansty
89e7712676 feat: download_file 支持 file:// 2024-12-29 21:41:44 +08:00
手瓜一十雪
9525786929 fix 2024-12-29 16:20:41 +08:00
Mlikiowa
72088e41a8 release: v4.2.46 2024-12-29 07:47:38 +00:00
手瓜一十雪
a3ed9ff2ef Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-29 15:47:18 +08:00
手瓜一十雪
ff16dc73ec fix: typo 2024-12-29 15:47:16 +08:00
Mlikiowa
2da4ef5f0f release: v4.2.45 2024-12-29 07:46:08 +00:00
手瓜一十雪
eaf481799d feat: 追踪退出 2024-12-29 15:44:37 +08:00
手瓜一十雪
1f72863aba fix: 追踪登录失败 2024-12-29 15:25:51 +08:00
Mlikiowa
6b353fd8d8 release: v4.2.44 2024-12-29 06:45:43 +00:00
手瓜一十雪
56cde4ad79 fix: #669 2024-12-29 14:44:47 +08:00
手瓜一十雪
3b86d3c632 feat: umami 统计追踪 2024-12-29 14:39:17 +08:00
Mlikiowa
4ac7a25afb release: v4.2.43 2024-12-29 04:04:55 +00:00
手瓜一十雪
8248011a12 fix: #667 2024-12-29 11:54:09 +08:00
手瓜一十雪
5f454456d2 fix: #655 #663 2024-12-29 11:47:09 +08:00
手瓜一十雪
e99a619c23 Merge pull request #665 from clansty/feat/group_name
fix: wrong notice_type sub_type
2024-12-28 21:42:14 +08:00
Clansty
1fc791bb68 fix: wrong notice_type sub_type 2024-12-28 21:40:32 +08:00
手瓜一十雪
f1d83f7c16 Merge pull request #664 from clansty/feat/group_name
feat: 群名称变更事件
2024-12-28 21:05:50 +08:00
Clansty
527bb72bcf feat: 群名称变更事件 2024-12-28 20:54:41 +08:00
手瓜一十雪
d78409fd07 Merge pull request #662 from Shua-github/main
新增send_poke
2024-12-27 15:16:11 +08:00
手瓜一十雪
d5e7e8944f feat: send_poke 2024-12-27 15:14:57 +08:00
Shua-github
fb405a5c1c all_poke替换成send_poke 2024-12-27 13:15:10 +08:00
Shua-github
a9e471deca 新增all_poke 2024-12-27 00:24:35 +08:00
Mlikiowa
9cd15ae337 release: v4.2.42 2024-12-26 12:37:35 +00:00
手瓜一十雪
8ed4cc4b0a feat: send_packet 2024-12-26 20:36:53 +08:00
Mlikiowa
a62de441cf release: v4.2.41 2024-12-26 05:31:09 +00:00
手瓜一十雪
02a8999410 Merge pull request #652 from JerryZRF/main
feat: add `get_clientkey`
2024-12-25 12:51:44 +08:00
手瓜一十雪
59c7979d69 readme: new 2024-12-25 12:28:53 +08:00
手瓜一十雪
bb7b28cd8f feat: 调整logo 2024-12-25 12:24:03 +08:00
手瓜一十雪
056497b98a Merge pull request #657 from FfmpegZZZ/main
chore:修改文档链接
2024-12-24 20:22:59 +08:00
手瓜一十雪
ac2fb032c4 Merge branch 'main' into main 2024-12-24 20:22:40 +08:00
Ffmpeg
c933bdd5d9 chore:修改链接
## 我是猪咪

文档链接打错了
2024-12-24 20:07:11 +08:00
Ffmpeg
89c71a58fa 添加文档地址 (#656) 2024-12-24 19:45:34 +08:00
Ffmpeg
27ba85b4ff 添加文档地址 2024-12-24 19:41:35 +08:00
手瓜一十雪
79a75fed8e feat: 30899 2024-12-24 15:38:53 +08:00
Mlikiowa
ee7a76b29f release: v4.2.40 2024-12-24 07:29:39 +00:00
手瓜一十雪
c53bdc3ce0 feat: 30899 2024-12-24 15:19:44 +08:00
Mlikiowa
f36e328751 release: v4.2.39 2024-12-22 13:32:51 +00:00
pk5ls20
871b5688c2 feat: webui api (/QQVersion & /GetSysStatusRealTime) 2024-12-22 21:31:14 +08:00
JerryZRF
b96076b297 fix: incorrect import 2024-12-22 13:28:20 +08:00
pk5ls20
d4488e40cf feat: better system status helper
- cpu usage diff
2024-12-22 03:56:24 +08:00
pk5ls20
7e61497243 chore: remove console logs 2024-12-22 02:57:00 +08:00
pk5ls20
e71ccdd12a feat: system status helper
- remove duplicate os import
2024-12-22 02:55:49 +08:00
pk5ls20
202129d491 feat: system status helper
- remove pidusage
2024-12-22 02:24:10 +08:00
JerryZRF
a1700dd503 fix: incorrect import 2024-12-22 01:33:42 +08:00
JerryZRF
2954776539 feat: add get_clientkey 2024-12-21 20:43:15 +08:00
手瓜一十雪
fb1f122ef7 feat: 9.9.17-30851 2024-12-21 14:29:41 +08:00
手瓜一十雪
96c63e4689 refactor: 移除不再使用的代码 2024-12-21 14:19:03 +08:00
手瓜一十雪
c94936d3dc refactor: #637 2024-12-21 14:09:57 +08:00
pk5ls20
8c22f11087 feat: system status helper 2024-12-21 13:11:10 +08:00
Mlikiowa
8a089c84a9 release: v4.2.38 2024-12-20 11:46:02 +00:00
手瓜一十雪
b631e6f8a2 fix: 更精确的筛选 2024-12-20 19:44:57 +08:00
手瓜一十雪
b3b48b032c fix: 锁住esbuild版本 以缓解问题 2024-12-20 19:32:20 +08:00
手瓜一十雪
f3e8230eca fix: 暂时指定esbuild版本以缓解上游破坏
see https://github.com/evanw/esbuild/issues/4010
2024-12-20 19:30:30 +08:00
手瓜一十雪
cc9adf9d40 feat: 过滤空消息 2024-12-20 18:58:59 +08:00
手瓜一十雪
15a640d1dc fix: #637 2024-12-20 18:55:30 +08:00
凌莞~(=^▽^=)
c25b9f86db 优化私聊戳一戳事件上报 (#643) 2024-12-18 21:27:44 +08:00
Mlikiowa
ecfd033afb release: v4.2.37 2024-12-17 07:56:18 +00:00
手瓜一十雪
f3ed8c7dff fix #633 2024-12-17 15:55:18 +08:00
Mlikiowa
6089046721 release: v4.2.36 2024-12-17 03:37:26 +00:00
手瓜一十雪
44ff92ad4b style: lint 2024-12-17 09:25:10 +08:00
手瓜一十雪
892262eb85 Merge pull request #635 from NapNeko/ref/ob-network
refactor: adjust onebot network
2024-12-17 09:07:39 +08:00
pk5ls20
2d9cc4d198 fix: as design 2024-12-17 07:07:04 +08:00
pk5ls20
a0c479485d refactor: adjust onebot network 2024-12-17 05:26:27 +08:00
手瓜一十雪
5650f18e50 Merge pull request #634 from bietiaop/main
fix: handleQuickOperation error
2024-12-17 00:29:16 +08:00
bietiaop
553885d025 fix: handleQuickOperation 2024-12-17 00:27:56 +08:00
Mlikiowa
35de00c4af release: v4.2.35 2024-12-16 14:10:08 +00:00
手瓜一十雪
09583e5de5 fuck javascript 2024-12-16 22:09:37 +08:00
Mlikiowa
38b0b7cd00 release: v4.2.34 2024-12-16 13:17:43 +00:00
手瓜一十雪
8b9c7b0c27 Merge pull request #632 from q8018414/patch-1
Update AboutUs.vue
2024-12-16 21:16:39 +08:00
手瓜一十雪
1005619bf3 Merge pull request #630 from NapNeko/dependabot/npm_and_yarn/rollup/plugin-typescript-12.1.2
chore(deps-dev): bump @rollup/plugin-typescript from 11.1.6 to 12.1.2
2024-12-16 21:16:01 +08:00
手瓜一十雪
3e09cff9cb Merge branch 'main' into dependabot/npm_and_yarn/rollup/plugin-typescript-12.1.2 2024-12-16 21:15:52 +08:00
手瓜一十雪
c24384e454 Merge pull request #629 from NapNeko/dependabot/npm_and_yarn/rollup/plugin-node-resolve-16.0.0
chore(deps-dev): bump @rollup/plugin-node-resolve from 15.3.1 to 16.0.0
2024-12-16 21:15:23 +08:00
手瓜一十雪
f87a543406 fix: #631 2024-12-16 21:14:14 +08:00
手瓜一十雪
f752136283 fix: #631 2024-12-16 21:06:51 +08:00
my_key
7e71622a44 Update AboutUs.vue
新增用于显示New NapCat的tag,便于区分当前版本和最新版本
2024-12-16 20:09:40 +08:00
dependabot[bot]
da92afb379 chore(deps-dev): bump @rollup/plugin-typescript from 11.1.6 to 12.1.2
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/HEAD/packages/typescript) from 11.1.6 to 12.1.2.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/typescript/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/typescript-v12.1.2/packages/typescript)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:44:42 +00:00
dependabot[bot]
d3062de5f9 chore(deps-dev): bump @rollup/plugin-node-resolve from 15.3.1 to 16.0.0
Bumps [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve) from 15.3.1 to 16.0.0.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/node-resolve/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/commonjs-v16.0.0/packages/node-resolve)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-node-resolve"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:43:46 +00:00
Mlikiowa
f1440b03a8 release: v4.2.33 2024-12-16 05:03:59 +00:00
手瓜一十雪
9a8b266cef Merge pull request #627 from bietiaop/main
feat: 查看登录QQ信息&获取快速登录列表详细信息&获取nc的包信息&优化了部分写法
2024-12-16 13:03:09 +08:00
手瓜一十雪
2a9bc57120 fix: #628 2024-12-16 13:00:07 +08:00
bietiaop
2ed83a0e30 feat: 查看登录QQ信息&获取快速登录列表详细信息&获取nc的包信息&优化了部分写法 2024-12-16 12:46:27 +08:00
Mlikiowa
116e8fd30a release: v4.2.32 2024-12-14 07:03:02 +00:00
手瓜一十雪
891f11173b fix 2024-12-14 15:01:47 +08:00
手瓜一十雪
dfc7996c17 Merge branch 'maybe-feat/type' 2024-12-14 15:00:18 +08:00
手瓜一十雪
dc0561d34f refactor: OB11ActiveHttpAdapter 2024-12-14 14:26:23 +08:00
手瓜一十雪
4fb0845d79 fix: #619 2024-12-14 13:46:25 +08:00
pk5ls20
0e0d4837b8 feat & fix: GetMiniAppArk 2024-12-14 06:56:23 +08:00
pk5ls20
a6adde7966 feat: attempt to enhance type inference 2024-12-14 05:56:49 +08:00
手瓜一十雪
7b693132f9 fix 2024-12-13 23:27:24 +08:00
手瓜一十雪
3c3114b6ab fix: 支持类型推导 2024-12-13 23:23:58 +08:00
Mlikiowa
5cdbf58f59 release: v4.2.31 2024-12-13 14:04:13 +00:00
手瓜一十雪
6f0a4131a2 fix: 异常 2024-12-13 22:03:51 +08:00
Mlikiowa
aa520e2f5d release: v4.2.30 2024-12-13 11:28:32 +00:00
手瓜一十雪
2c3b7e9ee8 fix: fuck! tencent 2024-12-13 19:28:04 +08:00
手瓜一十雪
b86a28092a fix: Login Check 2024-12-13 17:38:27 +08:00
手瓜一十雪
d59e5f2133 fix 2024-12-13 16:45:35 +08:00
手瓜一十雪
3fdd187102 fix: #610 2024-12-13 14:29:11 +08:00
Mlikiowa
3f085fd8ae release: v4.2.29 2024-12-13 04:47:13 +00:00
手瓜一十雪
a4fc131aec feat: 全平台30594 2024-12-13 12:36:51 +08:00
手瓜一十雪
d7d446c3fc fix 2024-12-12 23:02:10 +08:00
手瓜一十雪
212666e603 fix 2024-12-12 18:41:46 +08:00
手瓜一十雪
b545c28340 feat: 30483全平台兼容 2024-12-12 16:57:50 +08:00
手瓜一十雪
72bc345515 feat: linux 3.2.15-30483 2024-12-12 16:53:31 +08:00
手瓜一十雪
cc5082a9e3 feat: mac 6.9.62-30483 2024-12-12 11:43:31 +08:00
手瓜一十雪
45782a6c6c chore: docs 2024-12-12 10:44:09 +08:00
手瓜一十雪
e86d646cce chore: 万里妹妹干坏事 2024-12-12 10:43:13 +08:00
手瓜一十雪
92cfc6b8c8 feat: 更换演示代码 2024-12-12 10:00:38 +08:00
手瓜一十雪
82289d9f1f chore: debug log remove 2024-12-12 09:57:52 +08:00
手瓜一十雪
4cdbdaaf4e feat: win 30483 2024-12-12 09:47:44 +08:00
手瓜一十雪
ecde2427da fix: path 2024-12-12 09:37:51 +08:00
手瓜一十雪
fed1ec5d83 Merge pull request #620 from NapNeko/plugin-support
Feat: 支持快速基于NapCat进行二次开发 不需要通过传统NetWork
2024-12-12 09:34:27 +08:00
手瓜一十雪
4fbd764ced fix 2024-12-12 09:34:13 +08:00
手瓜一十雪
5361079010 Merge pull request #616 from Ander-pixe/webui-new
fix:路由守卫
2024-12-12 09:33:23 +08:00
手瓜一十雪
002d135ef5 fix 2024-12-11 18:26:26 +08:00
手瓜一十雪
a39b0a4a78 feat: plugin 2024-12-11 17:07:21 +08:00
纸凤孤凰
eb5d68422f fix: 路由守卫 2024-12-10 16:59:57 +08:00
纸凤孤凰
3dc13e5c2e Merge remote-tracking branch 'origin/main' into webui-new 2024-12-10 15:29:27 +08:00
huankong233
16881f057a fix: solve the token error
fix: remove useless defineProps
fix: add the missing dependencies to package.json
fix: add ES2022 into tsconfig.json
2024-12-10 09:44:38 +08:00
手瓜一十雪
1cd7d0577f fix: error 2024-12-10 09:44:23 +08:00
Mlikiowa
3c872df97a release: v4.2.28 2024-12-08 14:37:51 +00:00
pk5ls20
218b7bd2a0 fix: #607 2024-12-08 22:23:01 +08:00
Mlikiowa
4552d6970d release: v4.2.27 2024-12-06 03:40:03 +00:00
手瓜一十雪
4b319d15a7 refactor: GetGroupInfo 2024-12-06 11:39:11 +08:00
Mlikiowa
0ae3a4172c release: v4.2.26 2024-12-06 02:40:53 +00:00
手瓜一十雪
bf0c12f1c4 fix: #605 2024-12-06 10:39:49 +08:00
Mlikiowa
cb5eeecb86 release: v4.2.25 2024-12-05 13:13:34 +00:00
手瓜一十雪
8d857cf2be Revert "refactor: CardChangedEvent"
This reverts commit 0e8ceeb6c9.
2024-12-05 21:13:03 +08:00
手瓜一十雪
6f232c465f Merge pull request #604 from Ander-pixe/webui-new
feat:实时日志、关于and部分样式优化
2024-12-05 21:10:08 +08:00
纸凤孤凰
032d444246 Merge remote-tracking branch 'origin/webui-new' into webui-new 2024-12-05 20:27:08 +08:00
纸凤孤凰
49488dd3fb Merge remote-tracking branch 'origin/webui-new' into webui-new 2024-12-05 20:25:14 +08:00
手瓜一十雪
9aec3865ff fix: historyLog 2024-12-05 20:22:55 +08:00
手瓜一十雪
b6b7f2051b fix 2024-12-05 20:02:24 +08:00
手瓜一十雪
46254a699a fix 2024-12-05 19:56:36 +08:00
手瓜一十雪
7b3c287137 fix: cors 2024-12-05 19:37:56 +08:00
手瓜一十雪
1a533742a5 fix: Log 2024-12-05 19:17:07 +08:00
手瓜一十雪
2027266852 fix 2024-12-05 18:56:55 +08:00
手瓜一十雪
946d8b1a7b fix: dependencies error 2024-12-05 18:49:45 +08:00
手瓜一十雪
6d2fb5de6f Merge branch 'main' into pr/604 2024-12-05 18:40:46 +08:00
pk5ls20
91c4a002dd fix: dependencies 2024-12-05 18:15:00 +08:00
纸凤孤凰
4d8112aae5 feat:实时日志、关于and部分样式优化 2024-12-05 15:31:42 +08:00
手瓜一十雪
bb53f245cf style: lint check 2024-12-05 14:50:27 +08:00
手瓜一十雪
9f31cdbf5b fix: 移除不使用api 2024-12-05 14:46:05 +08:00
手瓜一十雪
9a33039d73 fix: 暂时移除 QunAlbum 2024-12-05 14:45:12 +08:00
手瓜一十雪
7cf3be8333 refactor: predict time 2024-12-05 14:42:45 +08:00
Nanako
82afb88e53 Update README.md 2024-12-05 14:37:10 +08:00
Mlikiowa
4aa24b5d67 release: v4.2.24 2024-12-05 06:17:46 +00:00
手瓜一十雪
57112c21a2 refactor: flag handle&onebot标准化 2024-12-05 14:17:09 +08:00
手瓜一十雪
0e8ceeb6c9 refactor: CardChangedEvent 2024-12-05 11:36:06 +08:00
手瓜一十雪
f52b8d1f04 feat: 30366 全平台通用性适配 2024-12-05 11:15:10 +08:00
手瓜一十雪
f374cc77ae chore: readme 2024-12-04 23:04:37 +08:00
手瓜一十雪
7c694e7fae chore: 跑路的规范/困难的实现 2024-12-04 23:03:46 +08:00
Mlikiowa
932ffc2673 release: v4.2.23 2024-12-04 14:18:02 +00:00
手瓜一十雪
3de5438139 fix: poke report 2024-12-04 22:16:59 +08:00
手瓜一十雪
c4b5f34271 fix: 兜底 防止进入影响速度 2024-12-04 22:02:11 +08:00
手瓜一十雪
22d3ac33a2 Refactor: 更新群组通知处理逻辑,优化数据结构和异步处理 2024-12-04 21:59:53 +08:00
Mlikiowa
2e5dd6535a release: v4.2.22 2024-12-04 13:18:58 +00:00
手瓜一十雪
eac58a2a50 fix: 9.9.17-30366 2024-12-04 21:04:24 +08:00
手瓜一十雪
e939ec0e52 fix: #597 2024-12-04 20:37:08 +08:00
Mlikiowa
5b17a14a2a release: v4.2.21 2024-12-04 11:49:26 +00:00
手瓜一十雪
8fb8c888f5 refactor: 移除未使用的uidCache和uinCache逻辑 2024-12-04 19:48:59 +08:00
Mlikiowa
4a2884509e release: v4.2.20 2024-12-04 11:46:12 +00:00
手瓜一十雪
e295235a89 fix: #596 2024-12-04 19:45:46 +08:00
手瓜一十雪
ef515a38d0 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-04 18:50:55 +08:00
手瓜一十雪
02cff040e3 refactor: 移除未使用的createUidFromTinyId和getStatusByUid方法 2024-12-04 18:50:51 +08:00
Mlikiowa
bb0f65a52d release: v4.2.19 2024-12-04 10:42:32 +00:00
手瓜一十雪
d51d6a5cc1 refactor: getUidByUinV2/getUinByUidV2 2024-12-04 18:41:28 +08:00
手瓜一十雪
eb99379a79 fix: 性能优化 2024-12-04 18:29:33 +08:00
Mlikiowa
388eb57d0d release: v4.2.18 2024-12-04 03:40:59 +00:00
手瓜一十雪
0b8131392a chore: 移出调试 2024-12-04 11:40:36 +08:00
手瓜一十雪
229efbd006 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-04 11:39:09 +08:00
手瓜一十雪
a482fa3a8d refactor: 优化调整文件处理 2024-12-04 11:38:59 +08:00
Mlikiowa
6cf047af39 release: v4.2.17 2024-12-04 02:57:21 +00:00
手瓜一十雪
41748c0b3f refactor: 数据清理与刷新 2024-12-04 10:55:56 +08:00
手瓜一十雪
1ce8be3c7e fix: 精简GroupApi实现 2024-12-04 10:47:21 +08:00
手瓜一十雪
32778acf57 refactor: NTQQGroupApi 2024-12-04 10:43:48 +08:00
Mlikiowa
a3c71473ae release: v4.2.16 2024-12-03 14:15:16 +00:00
手瓜一十雪
aceece7e90 fix: code 2024-12-03 22:12:57 +08:00
Mlikiowa
52efb4f9ef release: v4.2.15 2024-12-03 14:07:35 +00:00
手瓜一十雪
6b0d96fe8d fix: 移除复杂信息 2024-12-03 22:07:11 +08:00
Mlikiowa
ad052821b0 release: v4.2.14 2024-12-03 13:50:37 +00:00
手瓜一十雪
da7636e60c fix: #592 2024-12-03 21:50:12 +08:00
Mlikiowa
ef01dd0d77 release: v4.2.13 2024-12-03 13:43:37 +00:00
手瓜一十雪
03f7d4673f fix: code 2024-12-03 21:42:08 +08:00
手瓜一十雪
94e9c87978 fix: 优化处理 2024-12-03 21:42:08 +08:00
手瓜一十雪
501bbbe4df fix 2024-12-03 21:42:08 +08:00
手瓜一十雪
c9122a3fee fix: 临时的抽象方案 2024-12-03 21:42:08 +08:00
手瓜一十雪
8a289d014e fix: error 2024-12-03 21:42:08 +08:00
手瓜一十雪
ddadd38151 refactor: GroupAdminChange 2024-12-03 21:42:08 +08:00
手瓜一十雪
0b8d0e3cac feat: 迁移事件解析原理 2024-12-03 21:42:08 +08:00
Nepenthe
d14a1dd948 Merge branch 'NapNeko:main' into main 2024-11-17 17:28:31 +08:00
Nepenthe
1c0b434f47 Merge branch 'NapNeko:main' into main 2024-10-31 19:15:25 +08:00
Nepenthe
573451bade 修复<get_record>接口 2024-10-30 21:07:01 +08:00
1272 changed files with 95930 additions and 28947 deletions

View File

@@ -12,10 +12,10 @@ insert_final_newline = true
# Set default charset
charset = utf-8
# 2 space indentation
# 4 space indentation
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
indent_style = space
indent_size = 4
indent_size = 2
[*.bat]
charset = latin1

View File

@@ -1,2 +0,0 @@
VITE_BUILD_TYPE = Production
VITE_BUILD_PLATFORM = Framework

View File

@@ -1,2 +0,0 @@
VITE_BUILD_TYPE = Production
VITE_BUILD_PLATFORM = Shell

View File

@@ -1,2 +0,0 @@
VITE_BUILD_TYPE = Production
VITE_BUILD_PLATFORM = Universal

View File

@@ -1,6 +1,6 @@
name: Bug 反馈
description: 报告可能的 NapCat 异常行为
title: '[BUG] '
title: "[BUG] "
labels: bug
body:
- type: markdown
@@ -10,6 +10,10 @@ body:
在提交新的 Bug 反馈前,请确保您:
* 已经搜索了现有的 issues并且没有找到可以解决您问题的方法
* 不与现有的某一 issue 重复
* **不接受因发送不当内容而导致的问题报告**
- 包括但不限于:多媒体发送失败、转发消息失败、消息被拦截等因 18+ 内容、违规内容或触发风控的问题
- 提交 issue 前,请确认您发送的多媒体内容、链接、文本等均为正常合规内容,不会触发平台风控机制
- 因违规内容导致的问题,一律不予受理
- type: input
id: system-version
attributes:
@@ -30,7 +34,7 @@ body:
id: napcat-version
attributes:
label: NapCat 版本
description: 可在 LiteLoaderQQNT 的设置页或是 QQNT 的设置页侧栏中找到
description: 可在 WebUI 的「系统信息」页中找到
placeholder: 1.0.0
validations:
required: true

60
.github/ISSUE_TEMPLATE/feat_request.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Feat 请求
description: 提交新的 NapCat 功能或改进建议
title: '[FEAT] '
labels: enhancement
body:
- type: markdown
attributes:
value: |
欢迎来到 NapCat 的 Issue Tracker请填写以下表格来提交功能请求。
在提交新的功能请求前,请确保您:
* 已经搜索了现有的 issues并且没有找到类似的建议
* 不与现有的某一 issue 重复
- type: input
id: system-version
attributes:
label: 系统版本
description: 运行 QQNT 的系统版本
placeholder: Windows 11 24H2
validations:
required: true
- type: input
id: qqnt-version
attributes:
label: QQNT 版本
description: 可在 QQNT 的「关于」的设置页中找到
placeholder: 9.9.16-29927
validations:
required: true
- type: input
id: napcat-version
attributes:
label: NapCat 版本
description: 可在 WebUI 的「系统信息」页中找到
placeholder: 1.0.0
validations:
required: true
- type: textarea
id: feature-description
attributes:
label: 功能描述
description: 请详细描述你希望添加的功能或改进
validations:
required: true
- type: textarea
id: feature-reason
attributes:
label: 需求背景与理由
description: 请说明为什么需要这个功能,解决了什么问题
validations:
required: true
- type: textarea
id: feature-expected
attributes:
label: 期望的实现方式或效果
description: 请描述你期望的功能实现方式或最终效果
- type: textarea
id: other-info
attributes:
label: 其他补充信息
description: 你还想补充什么?

43
.github/prompt/default.md vendored Normal file
View File

@@ -0,0 +1,43 @@
# {VERSION}
[使用文档](https://napneko.github.io/)
## Windows 一键包
我们为提供了的轻量化一键部署方案
相对于普通需要安装QQ的方案,下面已内置QQ和Napcat 阅读使用文档参考
你可以下载
NapCat.Shell.Windows.OneKey.zip (无头)
启动后可自动化部署一键包,教程参考使用文档安装部分
## 警告
**注意QQ版本推荐使用 40768+ 版本 最低可以使用40768版本**
**默认WebUi密钥为随机密码 控制台查看**
**[9.9.26-44343 X64 Win](https://dldir1.qq.com/qqfile/qq/QQNT/40d6045a/QQ9.9.26.44343_x64.exe)**
[LinuxX64 DEB 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_amd64.deb)
[LinuxX64 RPM 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_x86_64.rpm)
[LinuxArm64 DEB 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_arm64.deb)
[LinuxArm64 RPM 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_aarch64.rpm)
[MAC DMG 40990 ](https://dldir1v6.qq.com/qqfile/qq/QQNT/c6cb0f5d/QQ_v6.9.82.40990.dmg)
## 如果WinX64缺少运行库或者xxx.dll
[安装运行库](https://aka.ms/vs/17/release/vc_redist.x64.exe)
## 更新
### 🐛 修复
1. 修复 WebUI 主题配置在有未保存更改时卸载组件导致字体重置的问题 (ae42eed6)
### ✨ 新增
1. 文件上传相关接口UploadGroupFile/UploadPrivateFile新增 `upload_file` 参数支持 (91e0839e)
2. 消息发送逻辑支持 PTT语音元素过滤确保语音消息正确独立发送 (47983e29)
### 🔧 优化
1. 优化合并转发消息GetForwardMsg的获取与解析逻辑提高兼容性 (334c4233)
2. 改进消息发送方法中发送者 UIN 的处理逻辑 (71bb4f68)
3. 增强 WebUI 系统信息界面中对构建产物的处理与展示 (cb061890)
---
**完整更新日志**: [v4.10.6...v4.10.7](https://github.com/NapNeko/NapCatQQ/compare/v4.10.6...v4.10.7)

111
.github/prompt/release_note_prompt.txt vendored Normal file
View File

@@ -0,0 +1,111 @@
# NapCat Release Note Generator
你是 NapCat 项目的发布说明生成器。请根据提供的 commit 列表生成标准格式的发布说明。
## 核心规则
1. **版本号**:第一行必须是 `# {VERSION}`,使用用户提供的版本号,如果版本号是小写 v 开头(如 v4.10.2),必须转换为大写 V如 V4.10.2
2. **语言**:全部使用简体中文
3. **格式**:严格按照下方模板输出,不要添加额外的 markdown 格式
## Commit 分析规则
将 commit 分类为以下类型:
- 🐛 **修复**bug fix、修复、fix 相关
- ✨ **新增**新功能、feat、add 相关
- 🔧 **优化**优化、重构、refactor、improve、perf 相关
- 📦 **依赖**deps、依赖更新通常可以忽略或合并
- 🔨 **构建**ci、build、workflow 相关(通常可以忽略)
## 合并和筛选
- **合并相似项**:同一功能的多个 commit 合并为一条
- **忽略琐碎项**合并冲突、格式化、typo 等可忽略
- **控制数量**:最终保持 5-15 条更新要点
- **保留 commit hash**:每条末尾附上短 hash格式 `(a1b2c3d)`
## 输出模板 - 必须严格遵守以下格式
```
# {VERSION}
[使用文档](https://napneko.github.io/)
## Windows 一键包
我们为提供了的轻量化一键部署方案
相对于普通需要安装QQ的方案,下面已内置QQ和Napcat 阅读使用文档参考
你可以下载
NapCat.Shell.Windows.OneKey.zip (无头)
启动后可自动化部署一键包,教程参考使用文档安装部分
## 警告
**注意QQ版本推荐使用 40768+ 版本 最低可以使用40768版本**
**默认WebUi密钥为随机密码 控制台查看**
**[9.9.26-44343 X64 Win](https://dldir1.qq.com/qqfile/qq/QQNT/40d6045a/QQ9.9.26.44343_x64.exe)**
[LinuxX64 DEB 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_amd64.deb)
[LinuxX64 RPM 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_x86_64.rpm)
[LinuxArm64 DEB 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_arm64.deb)
[LinuxArm64 RPM 44343 ](https://dldir1.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_aarch64.rpm)
[MAC DMG 40990 ](https://dldir1v6.qq.com/qqfile/qq/QQNT/c6cb0f5d/QQ_v6.9.82.40990.dmg)
## 如果WinX64缺少运行库或者xxx.dll
[安装运行库](https://aka.ms/vs/17/release/vc_redist.x64.exe)
## 更新
### 🐛 修复
1. 修复 xxx 问题 (a1b2c3d)
2. 修复 yyy 崩溃 (b2c3d4e)
### ✨ 新增
1. 新增 xxx 功能 (c3d4e5f)
2. 支持 yyy 特性 (d4e5f6g)
### 🔧 优化
1. 优化 xxx 性能 (e5f6g7h)
2. 重构 yyy 模块 (f6g7h8i)
---
**完整更新日志**: [{PREV_VERSION}...{VERSION}](https://github.com/NapNeko/NapCatQQ/compare/{PREV_VERSION}...{VERSION})
```
**格式要求 - 务必严格遵守:**
- "Windows 一键包"部分的文本必须完全一致,不要修改任何措辞
- "警告"部分必须包含所有 QQ 版本下载链接,保持原有格式
- "如果WinX64缺少运行库或者xxx.dll"这一行必须保持原样
- QQ 版本号和下载链接保持不变40990 版本)
- 只有"## 更新"部分下面的内容需要根据实际 commit 生成
## 重要约束
1. 如果某个分类没有内容,则完全省略该分类
2. 不要编造不存在的更新
3. 保持简洁,每条更新控制在一行内
4. 使用用户友好的语言,避免过于技术化的描述
5. 重大变更Breaking Changes需要在注意事项中加粗提示
## 文件变化分析
用户会提供文件变化统计和具体代码diff帮助你理解变更内容
### 目录含义
- `packages/napcat-core/` → 核心功能、消息处理、QQ接口
- `packages/napcat-onebot/` → OneBot 协议实现、API、事件
- `packages/napcat-webui-backend/` → WebUI 后端接口
- `packages/napcat-webui-frontend/` → WebUI 前端界面
- `packages/napcat-shell/` → Shell 启动器
### 代码diff阅读指南
- `+` 开头的行是新增代码
- `-` 开头的行是删除代码
- 关注函数名、类名的变化来理解功能变更
- 关注 `fix`、`bug`、`error` 等关键词识别修复项
- 关注 `add`、`new`、`feature` 等关键词识别新功能
- 忽略纯重构(代码移动但功能不变)和格式化变更
### 截断说明
- 如果看到 `[... 已截断 ...]`,表示内容过长被截断
- 根据已有信息推断完整变更意图即可

231
.github/scripts/lib/comment.ts vendored Normal file
View File

@@ -0,0 +1,231 @@
/**
* 构建状态评论模板
*/
export const COMMENT_MARKER = '<!-- napcat-pr-build -->';
export type BuildStatus = 'success' | 'failure' | 'cancelled' | 'pending' | 'unknown';
export interface BuildTarget {
name: string;
status: BuildStatus;
error?: string;
downloadUrl?: string; // Artifact 直接下载链接
}
// ============== 辅助函数 ==============
function formatSha (sha: string): string {
return sha && sha.length >= 7 ? sha.substring(0, 7) : sha || 'unknown';
}
function escapeCodeBlock (text: string): string {
// 替换 ``` 为转义形式,避免破坏 Markdown 代码块
return text.replace(/```/g, '\\`\\`\\`');
}
function getTimeString (): string {
return new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
}
// ============== 状态图标 ==============
export function getStatusIcon (status: BuildStatus): string {
switch (status) {
case 'success':
return '✅ 成功';
case 'pending':
return '⏳ 构建中...';
case 'cancelled':
return '⚪ 已取消';
case 'failure':
return '❌ 失败';
default:
return '❓ 未知';
}
}
function getStatusEmoji (status: BuildStatus): string {
switch (status) {
case 'success': return '✅';
case 'pending': return '⏳';
case 'cancelled': return '⚪';
case 'failure': return '❌';
default: return '❓';
}
}
// ============== 构建中评论 ==============
export function generateBuildingComment (prSha: string, targets: string[]): string {
const time = getTimeString();
const shortSha = formatSha(prSha);
const lines: string[] = [
COMMENT_MARKER,
'',
'<div align="center">',
'',
'# 🔨 NapCat 构建中',
'',
'![Building](https://img.shields.io/badge/状态-构建中-yellow?style=for-the-badge&logo=github-actions&logoColor=white)',
'',
'</div>',
'',
'---',
'',
'## 📦 构建目标',
'',
'| 包名 | 状态 | 说明 |',
'| :--- | :---: | :--- |',
...targets.map(name => `| \`${name}\` | ⏳ | 正在构建... |`),
'',
'---',
'',
'## 📋 构建信息',
'',
`| 项目 | 值 |`,
`| :--- | :--- |`,
`| 📝 提交 | \`${shortSha}\` |`,
`| 🕐 开始时间 | ${time} |`,
'',
'---',
'',
'<div align="center">',
'',
'> ⏳ **构建进行中,请稍候...**',
'>',
'> 构建完成后将自动更新此评论',
'',
'</div>',
];
return lines.join('\n');
}
// ============== 构建结果评论 ==============
export function generateResultComment (
targets: BuildTarget[],
prSha: string,
runId: string,
repository: string,
version?: string
): string {
const runUrl = `https://github.com/${repository}/actions/runs/${runId}`;
const shortSha = formatSha(prSha);
const time = getTimeString();
const allSuccess = targets.every(t => t.status === 'success');
const anyCancelled = targets.some(t => t.status === 'cancelled');
const anyFailure = targets.some(t => t.status === 'failure');
// 状态徽章
let statusBadge: string;
let headerTitle: string;
if (allSuccess) {
statusBadge = '![Success](https://img.shields.io/badge/状态-构建成功-success?style=for-the-badge&logo=github-actions&logoColor=white)';
headerTitle = '# ✅ NapCat 构建成功';
} else if (anyCancelled && !anyFailure) {
statusBadge = '![Cancelled](https://img.shields.io/badge/状态-已取消-lightgrey?style=for-the-badge&logo=github-actions&logoColor=white)';
headerTitle = '# ⚪ NapCat 构建已取消';
} else {
statusBadge = '![Failed](https://img.shields.io/badge/状态-构建失败-critical?style=for-the-badge&logo=github-actions&logoColor=white)';
headerTitle = '# ❌ NapCat 构建失败';
}
const downloadLink = (target: BuildTarget) => {
if (target.status !== 'success') return '—';
if (target.downloadUrl) {
return `[📥 下载](${target.downloadUrl})`;
}
return `[📥 下载](${runUrl}#artifacts)`;
};
const lines: string[] = [
COMMENT_MARKER,
'',
'<div align="center">',
'',
headerTitle,
'',
statusBadge,
'',
'</div>',
'',
'---',
'',
'## 📦 构建产物',
'',
'| 包名 | 状态 | 下载 |',
'| :--- | :---: | :---: |',
...targets.map(t => `| \`${t.name}\` | ${getStatusEmoji(t.status)} ${t.status === 'success' ? '成功' : t.status === 'failure' ? '失败' : t.status === 'cancelled' ? '已取消' : '未知'} | ${downloadLink(t)} |`),
'',
'---',
'',
'## 📋 构建信息',
'',
`| 项目 | 值 |`,
`| :--- | :--- |`,
...(version ? [`| 🏷️ 版本号 | \`${version}\` |`] : []),
`| 📝 提交 | \`${shortSha}\` |`,
`| 🔗 构建日志 | [查看详情](${runUrl}) |`,
`| 🕐 完成时间 | ${time} |`,
];
// 添加错误详情
const failedTargets = targets.filter(t => t.status === 'failure' && t.error);
if (failedTargets.length > 0) {
lines.push('', '---', '', '## ⚠️ 错误详情', '');
for (const target of failedTargets) {
lines.push(
`<details>`,
`<summary>🔴 <b>${target.name}</b> 构建错误</summary>`,
'',
'```',
escapeCodeBlock(target.error!),
'```',
'',
'</details>',
''
);
}
}
// 添加底部提示
lines.push('---', '');
if (allSuccess) {
lines.push(
'<div align="center">',
'',
'> 🎉 **所有构建均已成功完成!**',
'>',
'> 点击上方下载链接获取构建产物进行测试',
'',
'</div>'
);
} else if (anyCancelled && !anyFailure) {
lines.push(
'<div align="center">',
'',
'> ⚪ **构建已被取消**',
'>',
'> 可能是由于新的提交触发了新的构建',
'',
'</div>'
);
} else {
lines.push(
'<div align="center">',
'',
'> ⚠️ **部分构建失败**',
'>',
'> 请查看上方错误详情或点击构建日志查看完整输出',
'',
'</div>'
);
}
return lines.join('\n');
}

189
.github/scripts/lib/github.ts vendored Normal file
View File

@@ -0,0 +1,189 @@
/**
* GitHub API 工具库
*/
import { appendFileSync } from 'node:fs';
// ============== 类型定义 ==============
export interface PullRequest {
number: number;
state: string;
head: {
sha: string;
ref: string;
repo: {
full_name: string;
};
};
}
export interface Repository {
owner: {
type: string;
};
}
export interface Artifact {
id: number;
name: string;
size_in_bytes: number;
archive_download_url: string;
}
// ============== GitHub API Client ==========================
export class GitHubAPI {
private token: string;
private baseUrl = 'https://api.github.com';
constructor (token: string) {
this.token = token;
}
private async request<T> (endpoint: string, options: RequestInit = {}): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
Authorization: `Bearer ${this.token}`,
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
...options.headers,
},
});
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
}
return response.json() as Promise<T>;
}
async getPullRequest (owner: string, repo: string, pullNumber: number): Promise<PullRequest> {
return this.request<PullRequest>(`/repos/${owner}/${repo}/pulls/${pullNumber}`);
}
async getCollaboratorPermission (owner: string, repo: string, username: string): Promise<string> {
const data = await this.request<{ permission: string; }>(
`/repos/${owner}/${repo}/collaborators/${username}/permission`
);
return data.permission;
}
async getRepository (owner: string, repo: string): Promise<Repository> {
return this.request(`/repos/${owner}/${repo}`);
}
async checkOrgMembership (org: string, username: string): Promise<boolean> {
try {
await this.request(`/orgs/${org}/members/${username}`);
return true;
} catch {
return false;
}
}
async getRunArtifacts (owner: string, repo: string, runId: string): Promise<Artifact[]> {
const data = await this.request<{ artifacts: Artifact[]; }>(
`/repos/${owner}/${repo}/actions/runs/${runId}/artifacts`
);
return data.artifacts;
}
async createComment (owner: string, repo: string, issueNumber: number, body: string): Promise<void> {
await this.request(`/repos/${owner}/${repo}/issues/${issueNumber}/comments`, {
method: 'POST',
body: JSON.stringify({ body }),
headers: { 'Content-Type': 'application/json' },
});
}
async findComment (owner: string, repo: string, issueNumber: number, marker: string): Promise<number | null> {
let page = 1;
const perPage = 100;
while (page <= 10) { // 最多检查 1000 条评论
const comments = await this.request<Array<{ id: number, body: string; }>>(
`/repos/${owner}/${repo}/issues/${issueNumber}/comments?per_page=${perPage}&page=${page}`
);
if (comments.length === 0) {
return null;
}
const found = comments.find(c => c.body.includes(marker));
if (found) {
return found.id;
}
if (comments.length < perPage) {
return null;
}
page++;
}
return null;
}
async updateComment (owner: string, repo: string, commentId: number, body: string): Promise<void> {
await this.request(`/repos/${owner}/${repo}/issues/comments/${commentId}`, {
method: 'PATCH',
body: JSON.stringify({ body }),
headers: { 'Content-Type': 'application/json' },
});
}
async createOrUpdateComment (
owner: string,
repo: string,
issueNumber: number,
body: string,
marker: string
): Promise<void> {
const existingId = await this.findComment(owner, repo, issueNumber, marker);
if (existingId) {
await this.updateComment(owner, repo, existingId, body);
console.log(`✓ Updated comment #${existingId}`);
} else {
await this.createComment(owner, repo, issueNumber, body);
console.log('✓ Created new comment');
}
}
}
// ============== Output 工具 ==============
export function setOutput (name: string, value: string): void {
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
appendFileSync(outputFile, `${name}=${value}\n`);
}
console.log(` ${name}=${value}`);
}
export function setMultilineOutput (name: string, value: string): void {
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
const delimiter = `EOF_${Date.now()}`;
appendFileSync(outputFile, `${name}<<${delimiter}\n${value}\n${delimiter}\n`);
}
}
// ============== 环境变量工具 ==============
export function getEnv (name: string, required: true): string;
export function getEnv (name: string, required?: false): string | undefined;
export function getEnv (name: string, required = false): string | undefined {
const value = process.env[name];
if (required && !value) {
throw new Error(`Environment variable ${name} is required`);
}
return value;
}
export function getRepository (): { owner: string, repo: string; } {
const repository = getEnv('GITHUB_REPOSITORY', true);
const [owner, repo] = repository.split('/');
return { owner, repo };
}

36
.github/scripts/pr-build-building.ts vendored Normal file
View File

@@ -0,0 +1,36 @@
/**
* PR Build - 更新构建中状态评论
*
* 环境变量:
* - GITHUB_TOKEN: GitHub API Token
* - PR_NUMBER: PR 编号
* - PR_SHA: PR 提交 SHA
*/
import { GitHubAPI, getEnv, getRepository } from './lib/github.ts';
import { generateBuildingComment, COMMENT_MARKER } from './lib/comment.ts';
const BUILD_TARGETS = ['NapCat.Framework', 'NapCat.Shell'];
async function main (): Promise<void> {
console.log('🔨 Updating building status comment\n');
const token = getEnv('GITHUB_TOKEN', true);
const prNumber = parseInt(getEnv('PR_NUMBER', true), 10);
const prSha = getEnv('PR_SHA', true);
const { owner, repo } = getRepository();
console.log(`PR: #${prNumber}`);
console.log(`SHA: ${prSha}`);
console.log(`Repo: ${owner}/${repo}\n`);
const github = new GitHubAPI(token);
const comment = generateBuildingComment(prSha, BUILD_TARGETS);
await github.createOrUpdateComment(owner, repo, prNumber, comment, COMMENT_MARKER);
}
main().catch((error) => {
console.error('❌ Error:', error);
process.exit(1);
});

206
.github/scripts/pr-build-check.ts vendored Normal file
View File

@@ -0,0 +1,206 @@
/**
* PR Build Check Script
* 检查 PR 构建触发条件和用户权限
*
* 环境变量:
* - GITHUB_TOKEN: GitHub API Token
* - GITHUB_EVENT_NAME: 事件名称
* - GITHUB_EVENT_PATH: 事件 payload 文件路径
* - GITHUB_REPOSITORY: 仓库名称 (owner/repo)
* - GITHUB_OUTPUT: 输出文件路径
*/
import { readFileSync } from 'node:fs';
import { GitHubAPI, getEnv, getRepository, setOutput } from './lib/github.ts';
import type { PullRequest } from './lib/github.ts';
// ============== 类型定义 ==============
interface GitHubPayload {
pull_request?: PullRequest;
issue?: {
number: number;
pull_request?: object;
};
comment?: {
body: string;
user: { login: string; };
};
}
interface CheckResult {
should_build: boolean;
pr_number?: number;
pr_sha?: string;
pr_head_repo?: string;
pr_head_ref?: string;
}
// ============== 权限检查 ==============
async function checkUserPermission (
github: GitHubAPI,
owner: string,
repo: string,
username: string
): Promise<boolean> {
// 方法1检查仓库协作者权限
try {
const permission = await github.getCollaboratorPermission(owner, repo, username);
if (['admin', 'write', 'maintain'].includes(permission)) {
console.log(`✓ User ${username} has ${permission} permission`);
return true;
}
console.log(`✗ User ${username} has ${permission} permission (insufficient)`);
} catch (e) {
console.log(`✗ Failed to get collaborator permission: ${(e as Error).message}`);
}
// 方法2检查组织成员身份
try {
const repoInfo = await github.getRepository(owner, repo);
if (repoInfo.owner.type === 'Organization') {
const isMember = await github.checkOrgMembership(owner, username);
if (isMember) {
console.log(`✓ User ${username} is organization member`);
return true;
}
console.log(`✗ User ${username} is not organization member`);
}
} catch (e) {
console.log(`✗ Failed to check org membership: ${(e as Error).message}`);
}
return false;
}
// ============== 事件处理 ==============
function handlePullRequestTarget (payload: GitHubPayload): CheckResult {
const pr = payload.pull_request;
if (!pr) {
console.log('✗ No pull_request in payload');
return { should_build: false };
}
if (pr.state !== 'open') {
console.log(`✗ PR is not open (state: ${pr.state})`);
return { should_build: false };
}
console.log(`✓ PR #${pr.number} is open, triggering build`);
return {
should_build: true,
pr_number: pr.number,
pr_sha: pr.head.sha,
pr_head_repo: pr.head.repo.full_name,
pr_head_ref: pr.head.ref,
};
}
async function handleIssueComment (
payload: GitHubPayload,
github: GitHubAPI,
owner: string,
repo: string
): Promise<CheckResult> {
const { issue, comment } = payload;
if (!issue || !comment) {
console.log('✗ No issue or comment in payload');
return { should_build: false };
}
// 检查是否是 PR 的评论
if (!issue.pull_request) {
console.log('✗ Comment is not on a PR');
return { should_build: false };
}
// 检查是否是 /build 命令
if (!comment.body.trim().startsWith('/build')) {
console.log('✗ Comment is not a /build command');
return { should_build: false };
}
console.log(`→ /build command from @${comment.user.login}`);
// 获取 PR 详情
const pr = await github.getPullRequest(owner, repo, issue.number);
// 检查 PR 状态
if (pr.state !== 'open') {
console.log(`✗ PR is not open (state: ${pr.state})`);
await github.createComment(owner, repo, issue.number, '⚠️ 此 PR 已关闭,无法触发构建。');
return { should_build: false };
}
// 检查用户权限
const username = comment.user.login;
const hasPermission = await checkUserPermission(github, owner, repo, username);
if (!hasPermission) {
console.log(`✗ User ${username} has no permission`);
await github.createComment(
owner,
repo,
issue.number,
`⚠️ @${username} 您没有权限使用 \`/build\` 命令,仅仓库协作者或组织成员可使用。`
);
return { should_build: false };
}
console.log(`✓ Build triggered by @${username}`);
return {
should_build: true,
pr_number: issue.number,
pr_sha: pr.head.sha,
pr_head_repo: pr.head.repo.full_name,
pr_head_ref: pr.head.ref,
};
}
// ============== 主函数 ==============
async function main (): Promise<void> {
console.log('🔍 PR Build Check\n');
const token = getEnv('GITHUB_TOKEN', true);
const eventName = getEnv('GITHUB_EVENT_NAME', true);
const eventPath = getEnv('GITHUB_EVENT_PATH', true);
const { owner, repo } = getRepository();
console.log(`Event: ${eventName}`);
console.log(`Repository: ${owner}/${repo}\n`);
const payload = JSON.parse(readFileSync(eventPath, 'utf-8')) as GitHubPayload;
const github = new GitHubAPI(token);
let result: CheckResult;
switch (eventName) {
case 'pull_request_target':
result = handlePullRequestTarget(payload);
break;
case 'issue_comment':
result = await handleIssueComment(payload, github, owner, repo);
break;
default:
console.log(`✗ Unsupported event: ${eventName}`);
result = { should_build: false };
}
// 输出结果
console.log('\n=== Outputs ===');
setOutput('should_build', String(result.should_build));
setOutput('pr_number', String(result.pr_number ?? ''));
setOutput('pr_sha', result.pr_sha ?? '');
setOutput('pr_head_repo', result.pr_head_repo ?? '');
setOutput('pr_head_ref', result.pr_head_ref ?? '');
}
main().catch((error) => {
console.error('❌ Error:', error);
process.exit(1);
});

90
.github/scripts/pr-build-result.ts vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* PR Build - 更新构建结果评论
*
* 环境变量:
* - GITHUB_TOKEN: GitHub API Token
* - PR_NUMBER: PR 编号
* - PR_SHA: PR 提交 SHA
* - RUN_ID: GitHub Actions Run ID
* - NAPCAT_VERSION: 构建版本号
* - FRAMEWORK_STATUS: Framework 构建状态
* - FRAMEWORK_ERROR: Framework 构建错误信息
* - SHELL_STATUS: Shell 构建状态
* - SHELL_ERROR: Shell 构建错误信息
*/
import { GitHubAPI, getEnv, getRepository } from './lib/github.ts';
import { generateResultComment, COMMENT_MARKER } from './lib/comment.ts';
import type { BuildTarget, BuildStatus } from './lib/comment.ts';
function parseStatus (value: string | undefined): BuildStatus {
if (value === 'success' || value === 'failure' || value === 'cancelled') {
return value;
}
return 'unknown';
}
async function main (): Promise<void> {
console.log('📝 Updating build result comment\n');
const token = getEnv('GITHUB_TOKEN', true);
const prNumber = parseInt(getEnv('PR_NUMBER', true), 10);
const prSha = getEnv('PR_SHA') || 'unknown';
const runId = getEnv('RUN_ID', true);
const version = getEnv('NAPCAT_VERSION') || '';
const { owner, repo } = getRepository();
const frameworkStatus = parseStatus(getEnv('FRAMEWORK_STATUS'));
const frameworkError = getEnv('FRAMEWORK_ERROR');
const shellStatus = parseStatus(getEnv('SHELL_STATUS'));
const shellError = getEnv('SHELL_ERROR');
console.log(`PR: #${prNumber}`);
console.log(`SHA: ${prSha}`);
console.log(`Version: ${version}`);
console.log(`Run: ${runId}`);
console.log(`Framework: ${frameworkStatus}${frameworkError ? ` (${frameworkError})` : ''}`);
console.log(`Shell: ${shellStatus}${shellError ? ` (${shellError})` : ''}\n`);
const github = new GitHubAPI(token);
const repository = `${owner}/${repo}`;
// 获取 artifacts 列表,生成直接下载链接
const artifactMap: Record<string, string> = {};
try {
const artifacts = await github.getRunArtifacts(owner, repo, runId);
console.log(`Found ${artifacts.length} artifacts`);
for (const artifact of artifacts) {
// 生成直接下载链接https://github.com/{owner}/{repo}/actions/runs/{run_id}/artifacts/{artifact_id}
const downloadUrl = `https://github.com/${repository}/actions/runs/${runId}/artifacts/${artifact.id}`;
artifactMap[artifact.name] = downloadUrl;
console.log(` - ${artifact.name}: ${downloadUrl}`);
}
} catch (e) {
console.log(`Warning: Failed to get artifacts: ${(e as Error).message}`);
}
const targets: BuildTarget[] = [
{
name: 'NapCat.Framework',
status: frameworkStatus,
error: frameworkError,
downloadUrl: artifactMap['NapCat.Framework'],
},
{
name: 'NapCat.Shell',
status: shellStatus,
error: shellError,
downloadUrl: artifactMap['NapCat.Shell'],
},
];
const comment = generateResultComment(targets, prSha, runId, repository, version);
await github.createOrUpdateComment(owner, repo, prNumber, comment, COMMENT_MARKER);
}
main().catch((error) => {
console.error('❌ Error:', error);
process.exit(1);
});

149
.github/scripts/pr-build-run.ts vendored Normal file
View File

@@ -0,0 +1,149 @@
/**
* PR Build Runner
* 执行构建步骤
*
* 用法: node pr-build-run.ts <target>
* target: framework | shell
*/
import { execSync } from 'node:child_process';
import { existsSync, renameSync, unlinkSync } from 'node:fs';
import { setOutput } from './lib/github.ts';
type BuildTarget = 'framework' | 'shell';
interface BuildStep {
name: string;
command: string;
errorMessage: string;
}
// ============== 构建步骤 ==============
function getCommonSteps (): BuildStep[] {
return [
{
name: 'Install pnpm',
command: 'npm i -g pnpm',
errorMessage: 'Failed to install pnpm',
},
{
name: 'Install dependencies',
command: 'pnpm i',
errorMessage: 'Failed to install dependencies',
},
{
name: 'Type check',
command: 'pnpm run typecheck',
errorMessage: 'Type check failed',
},
{
name: 'Test',
command: 'pnpm test',
errorMessage: 'Tests failed',
},
{
name: 'Build WebUI',
command: 'pnpm --filter napcat-webui-frontend run build',
errorMessage: 'WebUI build failed',
},
];
}
function getTargetSteps (target: BuildTarget): BuildStep[] {
if (target === 'framework') {
return [
{
name: 'Build Framework',
command: 'pnpm run build:framework',
errorMessage: 'Framework build failed',
},
];
}
return [
{
name: 'Build Shell',
command: 'pnpm run build:shell',
errorMessage: 'Shell build failed',
},
];
}
// ============== 执行器 ==============
function runStep (step: BuildStep): boolean {
console.log(`\n::group::${step.name}`);
console.log(`> ${step.command}\n`);
try {
execSync(step.command, {
stdio: 'inherit',
shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/bash',
});
console.log('::endgroup::');
console.log(`${step.name}`);
return true;
} catch (_error) {
console.log('::endgroup::');
console.log(`${step.name}`);
setOutput('error', step.errorMessage);
return false;
}
}
function postBuild (target: BuildTarget): void {
const srcDir = target === 'framework'
? 'packages/napcat-framework/dist'
: 'packages/napcat-shell/dist';
const destDir = target === 'framework' ? 'framework-dist' : 'shell-dist';
console.log(`\n→ Moving ${srcDir} to ${destDir}`);
if (!existsSync(srcDir)) {
throw new Error(`Build output not found: ${srcDir}`);
}
renameSync(srcDir, destDir);
// Install production dependencies
console.log('→ Installing production dependencies');
execSync('npm install --omit=dev', {
cwd: destDir,
stdio: 'inherit',
shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/bash',
});
// Remove package-lock.json
const lockFile = `${destDir}/package-lock.json`;
if (existsSync(lockFile)) {
unlinkSync(lockFile);
}
console.log(`✓ Build output ready at ${destDir}`);
}
// ============== 主函数 ==============
function main (): void {
const target = process.argv[2] as BuildTarget;
if (!target || !['framework', 'shell'].includes(target)) {
console.error('Usage: node pr-build-run.ts <framework|shell>');
process.exit(1);
}
console.log(`🔨 Building NapCat.${target === 'framework' ? 'Framework' : 'Shell'}\n`);
const steps = [...getCommonSteps(), ...getTargetSteps(target)];
for (const step of steps) {
if (!runStep(step)) {
process.exit(1);
}
}
postBuild(target);
console.log('\n✅ Build completed successfully!');
}
main();

83
.github/workflows/auto-release.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: Auto Release Docker
on:
release:
types: [published]
jobs:
shell-docker:
runs-on: ubuntu-latest
steps:
- name: Trigger NapCat-Docker docker-publish workflow
env:
GH_TOKEN: ${{ secrets.NAPCAT_BUILD }}
run: |
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
https://api.github.com/repos/NapNeko/NapCat-Docker/actions/workflows/docker-publish.yml/dispatches \
-d '{"ref":"main"}'
framework-docker:
runs-on: ubuntu-latest
steps:
- name: Trigger NapCat-Framework-Docker docker-publish workflow
env:
GH_TOKEN: ${{ secrets.NAPCAT_BUILD }}
run: |
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
https://api.github.com/repos/NapNeko/NapCat.Docker.Framework/actions/workflows/docker-image.yml/dispatches \
-d '{"ref":"main"}'
appimage-shell-docker:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Get Latest NapCat Version
id: get_version
run: |
# 获取当前仓库的最新 tag
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1))
# 输出调试信息
echo "Debug: Latest NapCat Version is ${latest_tag}"
echo "latest_tag=${latest_tag}" >> $GITHUB_ENV
- name: Trigger Release NapCat AppImage Workflow
env:
GH_TOKEN: ${{ secrets.NAPCAT_BUILD }}
NAPCAT_VERSION: ${{ env.latest_tag }}
QQ_VERSION_X86_64: 'https://dldir1v6.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_x86_64.AppImage' # 写死 QQ 版本
QQ_VERSION_ARM64: 'https://dldir1v6.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_arm64.AppImage' # 写死 QQ 版本
run: |
echo "Debug: Triggering Release NapCat AppImage with napcat_version=${NAPCAT_VERSION}, qq_version_x86_64=${QQ_VERSION_X86_64}, qq_version_arm64=${QQ_VERSION_ARM64}"
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
https://api.github.com/repos/NapNeko/NapCatAppImageBuild/actions/workflows/release.yml/dispatches \
-d "{\"ref\":\"main\",\"inputs\":{\"napcat_version\":\"${NAPCAT_VERSION}\",\"qq_version_x86_64\":\"${QQ_VERSION_X86_64}\",\"qq_version_arm64\":\"${QQ_VERSION_ARM64}\"}}"
node-shell-docker:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Get Latest NapCat Version
id: get_version
run: |
# 获取当前仓库的最新 tag
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1))
# 输出调试信息
echo "Debug: Latest NapCat Version is ${latest_tag}"
echo "latest_tag=${latest_tag}" >> $GITHUB_ENV
- name: Trigger Release NapCat AppImage Workflow
env:
GH_TOKEN: ${{ secrets.NAPCAT_BUILD }}
NAPCAT_VERSION: ${{ env.latest_tag }}
QQ_VERSION_X86_64: 'https://dldir1v6.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_x86_64.AppImage' # 写死 QQ 版本
QQ_VERSION_ARM64: 'https://dldir1v6.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_arm64.AppImage' # 写死 QQ 版本
run: |
echo "Debug: Triggering Release NapCat AppImage with napcat_version=${NAPCAT_VERSION}, qq_url_amd64=${QQ_VERSION_X86_64}, qq_url_arm64=${QQ_VERSION_ARM64}"
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
https://api.github.com/repos/NapNeko/NapCatLinuxNodeLoader/actions/workflows/release.yml/dispatches \
-d "{\"ref\":\"main\",\"inputs\":{\"napcat_version\":\"${NAPCAT_VERSION}\",\"qq_url_amd64\":\"${QQ_VERSION_X86_64}\",\"qq_url_arm64\":\"${QQ_VERSION_ARM64}\"}}"

View File

@@ -1,47 +1,94 @@
name: "Build Action"
name: Build NapCat Artifacts
on:
push:
pull_request:
workflow_dispatch:
push:
branches:
- main
permissions: write-all
jobs:
Build-LiteLoader:
Build-Framework:
runs-on: ubuntu-latest
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # 需要完整历史来获取 tags
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NapCat.Framework
- name: Generate Version
run: |
npm i && cd napcat.webui && npm i && cd .. || exit 1
npm run build:framework && npm run depend || exit 1
rm package-lock.json
# 获取最近的 release tag (格式: vX.X.X)
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0")
# 去掉 v 前缀
BASE_VERSION="${LATEST_TAG#v}"
SHORT_SHA="${GITHUB_SHA::7}"
VERSION="${BASE_VERSION}-main.${{ github.run_number }}+${SHORT_SHA}"
echo "NAPCAT_VERSION=${VERSION}" >> $GITHUB_ENV
echo "Latest tag: ${LATEST_TAG}"
echo "Build version: ${VERSION}"
- name: Build NapCat.Framework
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NAPCAT_VERSION: ${{ env.NAPCAT_VERSION }}
run: |
npm i -g pnpm
pnpm i
pnpm run typecheck || exit 1
pnpm test || exit 1
pnpm --filter napcat-webui-frontend run build || exit 1
pnpm run build:framework
mv packages/napcat-framework/dist framework-dist
cd framework-dist
npm install --omit=dev
rm ./package-lock.json || exit 0
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Framework
path: dist
path: framework-dist
Build-Shell:
runs-on: ubuntu-latest
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # 需要完整历史来获取 tags
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NapCat.Shell
- name: Generate Version
run: |
npm i && cd napcat.webui && npm i && cd .. || exit 1
npm run build:shell && npm run depend || exit 1
rm package-lock.json
# 获取最近的 release tag (格式: vX.X.X)
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0")
# 去掉 v 前缀
BASE_VERSION="${LATEST_TAG#v}"
SHORT_SHA="${GITHUB_SHA::7}"
VERSION="${BASE_VERSION}-main.${{ github.run_number }}+${SHORT_SHA}"
echo "NAPCAT_VERSION=${VERSION}" >> $GITHUB_ENV
echo "Latest tag: ${LATEST_TAG}"
echo "Build version: ${VERSION}"
- name: Build NapCat.Shell
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NAPCAT_VERSION: ${{ env.NAPCAT_VERSION }}
run: |
npm i -g pnpm
pnpm i
pnpm run typecheck || exit 1
pnpm test || exit 1
pnpm --filter napcat-webui-frontend run build || exit 1
pnpm run build:shell
mv packages/napcat-shell/dist shell-dist
cd shell-dist
npm install --omit=dev
rm ./package-lock.json || exit 0
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell
path: dist
path: shell-dist

303
.github/workflows/pr-build.yml vendored Normal file
View File

@@ -0,0 +1,303 @@
# =============================================================================
# PR 构建工作流
# =============================================================================
# 功能:
# 1. 在 PR 提交时自动构建 Framework 和 Shell 包
# 2. 支持通过 /build 命令手动触发构建(仅协作者/组织成员)
# 3. 在 PR 中发布构建状态评论,并持续更新(不会重复创建)
# 4. 支持 Fork PR 的构建(使用 pull_request_target 获取写权限)
#
# 安全说明:
# - 使用 pull_request_target 事件,在 base 分支上下文运行
# - 构建脚本始终从 base 分支 checkout避免恶意 PR 篡改脚本
# - PR 代码单独 checkout 到 workspace 目录
# =============================================================================
name: PR Build
# =============================================================================
# 触发条件
# =============================================================================
on:
# PR 事件:打开、同步(新推送)、重新打开时触发
# 注意:使用 pull_request_target 而非 pull_request以便对 Fork PR 有写权限
pull_request_target:
types: [opened, synchronize, reopened]
# Issue 评论事件:用于响应 /build 命令
issue_comment:
types: [created]
# =============================================================================
# 权限配置
# =============================================================================
permissions:
contents: read # 读取仓库内容
pull-requests: write # 写入 PR 评论
issues: write # 写入 Issue 评论(/build 命令响应)
actions: read # 读取 Actions 信息(获取构建日志链接)
# =============================================================================
# 并发控制
# =============================================================================
# 同一 PR 的多次构建会取消之前未完成的构建,避免资源浪费
# 注意:只有在 should_build=true 时才会进入实际构建流程,
# issue_comment 事件如果不是 /build 命令,会在 check-build 阶段快速退出,
# 不会取消正在进行的构建(因为 cancel-in-progress 只影响同 group 的后续任务)
concurrency:
# 使用不同的 group 策略:
# - pull_request_target: 使用 PR 号
# - issue_comment: 只有确认是 /build 命令时才使用 PR 号,否则使用 run_id不冲突
group: pr-build-${{ github.event_name == 'pull_request_target' && github.event.pull_request.number || github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/build') && github.event.issue.number || github.run_id }}
cancel-in-progress: true
# =============================================================================
# 任务定义
# =============================================================================
jobs:
# ---------------------------------------------------------------------------
# Job 1: 检查构建条件
# ---------------------------------------------------------------------------
# 判断是否应该触发构建:
# - pull_request_target 事件:总是触发
# - issue_comment 事件:检查是否为 /build 命令,且用户有权限
# ---------------------------------------------------------------------------
check-build:
runs-on: ubuntu-latest
outputs:
should_build: ${{ steps.check.outputs.should_build }} # 是否应该构建
pr_number: ${{ steps.check.outputs.pr_number }} # PR 编号
pr_sha: ${{ steps.check.outputs.pr_sha }} # PR 最新提交 SHA
pr_head_repo: ${{ steps.check.outputs.pr_head_repo }} # PR 源仓库(用于 Fork
pr_head_ref: ${{ steps.check.outputs.pr_head_ref }} # PR 源分支
steps:
# 仅 checkout 脚本目录,加快速度
- name: Checkout scripts
uses: actions/checkout@v4
with:
sparse-checkout: .github/scripts
sparse-checkout-cone-mode: false
# 使用 Node.js 24 以支持原生 TypeScript 执行
- name: Setup Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24
# 执行检查脚本,判断是否触发构建
- name: Check trigger condition
id: check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node --experimental-strip-types .github/scripts/pr-build-check.ts
# ---------------------------------------------------------------------------
# Job 2: 更新评论为"构建中"状态
# ---------------------------------------------------------------------------
# 在 PR 中创建或更新评论,显示构建正在进行中
# ---------------------------------------------------------------------------
update-comment-building:
needs: check-build
if: needs.check-build.outputs.should_build == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout scripts
uses: actions/checkout@v4
with:
sparse-checkout: .github/scripts
sparse-checkout-cone-mode: false
- name: Setup Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24
# 更新 PR 评论,显示构建中状态
- name: Update building comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ needs.check-build.outputs.pr_number }}
PR_SHA: ${{ needs.check-build.outputs.pr_sha }}
run: node --experimental-strip-types .github/scripts/pr-build-building.ts
# ---------------------------------------------------------------------------
# Job 3: 构建 Framework 包
# ---------------------------------------------------------------------------
# 执行 napcat-framework 的构建流程
# ---------------------------------------------------------------------------
build-framework:
needs: [check-build, update-comment-building]
if: needs.check-build.outputs.should_build == 'true'
runs-on: ubuntu-latest
outputs:
status: ${{ steps.build.outcome }} # 构建结果success/failure
error: ${{ steps.build.outputs.error }} # 错误信息(如有)
version: ${{ steps.version.outputs.version }} # 构建版本号
steps:
# 【安全】先从 base 分支 checkout 构建脚本
# 这样即使 PR 中修改了脚本,也不会被执行
- name: Checkout scripts from base
uses: actions/checkout@v4
with:
sparse-checkout: .github/scripts
sparse-checkout-cone-mode: false
path: _scripts
# 将 PR 代码 checkout 到单独的 workspace 目录
- name: Checkout PR code
uses: actions/checkout@v4
with:
repository: ${{ needs.check-build.outputs.pr_head_repo }}
ref: ${{ needs.check-build.outputs.pr_sha }}
path: workspace
fetch-depth: 0 # 需要完整历史来获取 tags
- name: Setup Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24
# 获取最新 release tag 并生成版本号
- name: Generate Version
id: version
working-directory: workspace
run: |
# 获取最近的 release tag (格式: vX.X.X)
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0")
# 去掉 v 前缀
BASE_VERSION="${LATEST_TAG#v}"
SHORT_SHA="${{ needs.check-build.outputs.pr_sha }}"
SHORT_SHA="${SHORT_SHA::7}"
VERSION="${BASE_VERSION}-pr.${{ needs.check-build.outputs.pr_number }}.${{ github.run_number }}+${SHORT_SHA}"
echo "NAPCAT_VERSION=${VERSION}" >> $GITHUB_ENV
echo "Latest tag: ${LATEST_TAG}"
echo "Build version: ${VERSION}"
# 执行构建,使用 base 分支的脚本处理 workspace 中的代码
- name: Build
id: build
working-directory: workspace
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NAPCAT_VERSION: ${{ env.NAPCAT_VERSION }}
run: node --experimental-strip-types ../_scripts/.github/scripts/pr-build-run.ts framework
continue-on-error: true # 允许失败,后续更新评论时处理
# 构建成功时上传产物
- name: Upload Artifact
if: steps.build.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: NapCat.Framework
path: workspace/framework-dist
retention-days: 7 # 保留 7 天
# ---------------------------------------------------------------------------
# Job 4: 构建 Shell 包
# ---------------------------------------------------------------------------
# 执行 napcat-shell 的构建流程(与 Framework 并行执行)
# ---------------------------------------------------------------------------
build-shell:
needs: [check-build, update-comment-building]
if: needs.check-build.outputs.should_build == 'true'
runs-on: ubuntu-latest
outputs:
status: ${{ steps.build.outcome }} # 构建结果success/failure
error: ${{ steps.build.outputs.error }} # 错误信息(如有)
version: ${{ steps.version.outputs.version }} # 构建版本号
steps:
# 【安全】先从 base 分支 checkout 构建脚本
- name: Checkout scripts from base
uses: actions/checkout@v4
with:
sparse-checkout: .github/scripts
sparse-checkout-cone-mode: false
path: _scripts
# 将 PR 代码 checkout 到单独的 workspace 目录
- name: Checkout PR code
uses: actions/checkout@v4
with:
repository: ${{ needs.check-build.outputs.pr_head_repo }}
ref: ${{ needs.check-build.outputs.pr_sha }}
path: workspace
fetch-depth: 0 # 需要完整历史来获取 tags
- name: Setup Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24
# 获取最新 release tag 并生成版本号
- name: Generate Version
id: version
working-directory: workspace
run: |
# 获取最近的 release tag (格式: vX.X.X)
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0")
# 去掉 v 前缀
BASE_VERSION="${LATEST_TAG#v}"
SHORT_SHA="${{ needs.check-build.outputs.pr_sha }}"
SHORT_SHA="${SHORT_SHA::7}"
VERSION="${BASE_VERSION}-pr.${{ needs.check-build.outputs.pr_number }}.${{ github.run_number }}+${SHORT_SHA}"
echo "NAPCAT_VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Latest tag: ${LATEST_TAG}"
echo "Build version: ${VERSION}"
# 执行构建
- name: Build
id: build
working-directory: workspace
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NAPCAT_VERSION: ${{ env.NAPCAT_VERSION }}
run: node --experimental-strip-types ../_scripts/.github/scripts/pr-build-run.ts shell
continue-on-error: true
# 构建成功时上传产物
- name: Upload Artifact
if: steps.build.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell
path: workspace/shell-dist
retention-days: 7 # 保留 7 天
# ---------------------------------------------------------------------------
# Job 5: 更新评论为构建结果
# ---------------------------------------------------------------------------
# 汇总所有构建结果,更新 PR 评论显示最终状态
# 使用 always() 确保即使构建失败/取消也会执行
# ---------------------------------------------------------------------------
update-comment-result:
needs: [check-build, update-comment-building, build-framework, build-shell]
if: always() && needs.check-build.outputs.should_build == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout scripts
uses: actions/checkout@v4
with:
sparse-checkout: .github/scripts
sparse-checkout-cone-mode: false
- name: Setup Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24
# 更新评论,显示构建结果和下载链接
- name: Update result comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ needs.check-build.outputs.pr_number }}
PR_SHA: ${{ needs.check-build.outputs.pr_sha }}
RUN_ID: ${{ github.run_id }}
# 构建版本号
NAPCAT_VERSION: ${{ needs.build-framework.outputs.version || needs.build-shell.outputs.version || '' }}
# 获取构建状态,如果 job 被跳过则标记为 cancelled
FRAMEWORK_STATUS: ${{ needs.build-framework.outputs.status || 'cancelled' }}
FRAMEWORK_ERROR: ${{ needs.build-framework.outputs.error }}
SHELL_STATUS: ${{ needs.build-shell.outputs.status || 'cancelled' }}
SHELL_ERROR: ${{ needs.build-shell.outputs.error }}
run: node --experimental-strip-types .github/scripts/pr-build-result.ts

View File

@@ -1,152 +1,442 @@
name: "Build Release"
name: Release NapCat
on:
workflow_dispatch:
push:
tags:
- "v*"
- 'v*'
permissions: write-all
env:
OPENROUTER_API_URL: https://91vip.futureppo.top/v1/chat/completions
OPENROUTER_MODEL: "copilot/ant/gemini-3-flash-preview"
RELEASE_NAME: "NapCat"
jobs:
check-version:
# 验证版本号格式
validate-version:
runs-on: ubuntu-latest
outputs:
valid: ${{ steps.check.outputs.valid }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Validate semantic version
id: check
run: |
TAG="${GITHUB_REF#refs/tags/}"
echo "Checking tag: $TAG"
# 语义化版本正则表达式
# 支持: v1.0.0, v1.0.0-beta, v1.0.0-rc.1, v1.0.0-alpha.1+build.123
SEMVER_REGEX="^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?$"
if [[ "$TAG" =~ $SEMVER_REGEX ]]; then
echo "✅ Valid semantic version: $TAG"
echo "valid=true" >> $GITHUB_OUTPUT
echo "version=$TAG" >> $GITHUB_OUTPUT
else
echo "❌ Invalid version format: $TAG"
echo "Expected format: vX.Y.Z or vX.Y.Z-prerelease"
echo "Examples: v1.0.0, v1.2.3-beta, v2.0.0-rc.1"
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi
Build-Framework:
needs: validate-version
if: needs.validate-version.outputs.valid == 'true'
runs-on: ubuntu-latest
steps:
- name: Clone Repository
- name: Clone Main Repository
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NapCat.Framework
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm i -g pnpm
pnpm i
pnpm --filter napcat-webui-frontend run build || exit 1
pnpm run build:framework
mv packages/napcat-framework/dist framework-dist
cd framework-dist
npm install --omit=dev
rm ./package-lock.json || exit 0
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Framework
path: framework-dist
- name: Check Version
run: |
ls
node ./script/checkVersion.cjs
sh ./checkVersion.sh
Build-LiteLoader:
needs: [check-version]
runs-on: ubuntu-latest
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
ref: main
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NuCat Framework
run: |
npm i
cd napcat.webui
npm i
cd ..
npm run build:framework
cd dist
npm i --omit=dev
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Framework
path: dist
Build-Shell:
needs: validate-version
if: needs.validate-version.outputs.valid == 'true'
runs-on: ubuntu-latest
needs: [check-version]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
ref: main
token: ${{ secrets.NAPCAT_BUILD }}
- name: Clone Main Repository
uses: actions/checkout@v4
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NapCat.Shell
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm i -g pnpm
pnpm i
pnpm --filter napcat-webui-frontend run build || exit 1
pnpm run build:shell
mv packages/napcat-shell/dist shell-dist
cd shell-dist
npm install --omit=dev
rm ./package-lock.json || exit 0
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell
path: shell-dist
Download-QNX64:
needs: Build-Shell
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NuCat Shell
run: |
npm i
cd napcat.webui
npm i
cd ..
npm run build:shell
cd dist
npm i --omit=dev
cd ..
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell
path: dist
- name: Setup tools
run: |
sudo apt update
sudo apt install -y aria2 unzip zip p7zip-full curl jq
- name: Download QQ x64, Node.js and Assemble NapCat.Shell.Windows.Node.zip
run: |
set -euo pipefail
TMPDIR=$(mktemp -d)
cd "$TMPDIR"
# -----------------------------
# 1) 下载 QQ x64
# -----------------------------
# JS_URL="https://cdn-go.cn/qq-web/im.qq.com_new/latest/rainbow/windowsConfig.js"
# JS_URL="https://slave.docadan488.workers.dev/proxy?url=https://cdn-go.cn/qq-web/im.qq.com_new/latest/rainbow/windowsConfig.js"
# NT_URL=$(curl -fsSL "$JS_URL" | grep -oP '"ntDownloadX64Url"\s*:\s*"\K[^"]+')
NT_URL="https://dldir1v6.qq.com/qqfile/qq/QQNT/eb263b35/QQ9.9.23.42086_x64.exe"
QQ_ZIP="$(basename "$NT_URL")"
aria2c -x16 -s16 -k1M -o "$QQ_ZIP" "$NT_URL"
QQ_EXTRACT="$TMPDIR/qq_extracted"
mkdir -p "$QQ_EXTRACT"
7z x -y -o"$QQ_EXTRACT" "$QQ_ZIP" >/dev/null
# -----------------------------
# 2) 下载 Node.js Windows x64 zip 22.11.0
# -----------------------------
NODE_VER="22.11.0"
NODE_URL="https://nodejs.org/dist/v$NODE_VER/node-v$NODE_VER-win-x64.zip"
NODE_ZIP="node-v$NODE_VER-win-x64.zip"
aria2c -x1 -s1 -k1M -o "$NODE_ZIP" "$NODE_URL"
NODE_EXTRACT="$TMPDIR/node_extracted"
mkdir -p "$NODE_EXTRACT"
unzip -q "$NODE_ZIP" -d "$NODE_EXTRACT"
# -----------------------------
# 3) 创建输出目录
# -----------------------------
OUT_DIR="$GITHUB_WORKSPACE/NapCat.Shell.Windows.Node"
mkdir -p "$OUT_DIR/NapCat.Shell.Windows.Node"
# -----------------------------
# 4) 解压 NapCat.Shell.zip 到 napcat
# -----------------------------
cp -a "$GITHUB_WORKSPACE/artifacts/NapCat.Shell/." "$OUT_DIR/napcat/"
# -----------------------------
# 5) 拷贝 QQ 文件到 NapCat.Shell.Windows.Node
# -----------------------------
QQ_TARGETS=("avif_convert.dll" "broadcast_ipc.dll" "config.json" "libglib-2.0-0.dll" "libgobject-2.0-0.dll" "libvips-42.dll" "ncnn.dll" "opencv.dll" "package.json" "QBar.dll" "wrapper.node")
for name in "${QQ_TARGETS[@]}"; do
find "$QQ_EXTRACT" -iname "$name" -exec cp -a {} "$OUT_DIR" \; || true
done
# -----------------------------
# 6) 拷贝仓库文件 napcat.bat 和 index.js
# -----------------------------
cp -a "$GITHUB_WORKSPACE/packages/napcat-develop/napcat.bat" "$OUT_DIR/" || true
cp -a "$GITHUB_WORKSPACE/packages/napcat-develop/index.js" "$OUT_DIR/" || true
cp -a "$GITHUB_WORKSPACE/packages/napcat-develop/QQNT.dll" "$OUT_DIR/" || true
# -----------------------------
# 7) 拷贝 Node.exe 到 NapCat.Shell.Windows.Node
# -----------------------------
cp -a "$NODE_EXTRACT/node-v$NODE_VER-win-x64/node.exe" "$OUT_DIR/" || true
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell.Windows.Node
path: NapCat.Shell.Windows.Node
release-napcat:
needs: [Build-LiteLoader,Build-Shell]
needs: [Build-Framework, Build-Shell, Download-QNX64]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
ref: main
token: ${{ secrets.NAPCAT_BUILD }}
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Download All Artifact
uses: actions/download-artifact@v4
- name: Compress subdirectories
run: |
cd ./NapCat.Shell/
zip -q -r NapCat.Shell.zip *
cd ..
cd ./NapCat.Framework/
zip -q -r NapCat.Framework.zip *
cd ..
rm ./NapCat.Shell.zip -rf
rm ./NapCat.Framework.zip -rf
mv ./NapCat.Shell/NapCat.Shell.zip ./
mv ./NapCat.Framework/NapCat.Framework.zip ./
- name: Download NapCat.Shell.Windows.OneKey.zip
run: |
curl -L -o NapCat.Shell.Windows.OneKey.zip https://github.com/NapNeko/NapCatResource/raw/main/NapCat.Shell.Windows.OneKey.zip
mkdir ./NapCat.Framework.Windows.Once
unzip -q ./external/LiteLoaderWrapper.zip -d ./NapCat.Framework.Windows.Once
cd ./NapCat.Framework.Windows.Once
ls
mkdir -p ./LL/plugins/NapCatQQ
unzip -q ../NapCat.Framework.zip -d ./LL/plugins/NapCatQQ
zip -q -r NapCat.Framework.Windows.Once.zip *
cd ..
mv ./NapCat.Framework.Windows.Once/NapCat.Framework.Windows.Once.zip ./
- name: Extract version from tag
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Clone Changes Log
run: curl -o CHANGELOG.md https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/docs/changelogs/CHANGELOG.v${{ env.VERSION }}.md
- name: Create Release Draft and Upload Artifacts
uses: softprops/action-gh-release@v1
with:
name: NapCat V${{ env.VERSION }}
token: ${{ secrets.GITHUB_TOKEN }}
body_path: CHANGELOG.md
files: |
NapCat.Framework.zip
NapCat.Shell.zip
NapCat.Framework.Windows.Once.zip
draft: true
- name: Zip Artifacts
run: |
cd artifacts
[ -d NapCat.Framework ] && (cd NapCat.Framework && zip -qr ../../NapCat.Framework.zip .)
[ -d NapCat.Shell ] && (cd NapCat.Shell && zip -qr ../../NapCat.Shell.zip .)
[ -d NapCat.Shell.Windows.Node ] && (cd NapCat.Shell.Windows.Node && zip -qr ../../NapCat.Shell.Windows.Node.zip .)
cd ..
- name: Generate release note via OpenRouter
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
OPENROUTER_API_URL: ${{ env.OPENROUTER_API_URL }}
OPENROUTER_MODEL: ${{ env.OPENROUTER_MODEL }}
GITHUB_OWNER: "NapNeko" # 替换成你的 repo owner
GITHUB_REPO: "NapCatQQ" # 替换成你的 repo 名
run: |
set -euo pipefail
# 当前 tag
CURRENT_TAG="${GITHUB_REF#refs/tags/}"
echo "Current tag: $CURRENT_TAG"
# 从 GitHub API 获取 tag 列表
TAGS_JSON=$(curl -s "https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/tags?per_page=100")
TAGS=( $(echo "$TAGS_JSON" | jq -r '.[].name' | sort -V) )
# 找到上一个 tag
PREV_TAG=""
for i in "${!TAGS[@]}"; do
if [ "${TAGS[$i]}" = "$CURRENT_TAG" ]; then
if [ $i -gt 0 ]; then
PREV_TAG="${TAGS[$((i-1))]}"
fi
break
fi
done
if [ -z "$PREV_TAG" ]; then
echo "⚠️ Could not find previous tag for $CURRENT_TAG, using first commit"
PREV_TAG=$(git rev-list --max-parents=0 HEAD | head -1)
fi
echo "Previous tag: $PREV_TAG"
# 强制拉取上一个 tag 和当前 tag
git fetch origin "refs/tags/$PREV_TAG:refs/tags/$PREV_TAG" --force || true
git fetch origin "refs/tags/$CURRENT_TAG:refs/tags/$CURRENT_TAG" --force || true
# 获取 commit使用更清晰的格式
# 格式: <type>: <subject> (<hash>)
COMMITS=$(git log --pretty=format:'- %s (%h)' "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null || git log --pretty=format:'- %s (%h)' -20)
echo "Commit list from $PREV_TAG to $CURRENT_TAG:"
echo "$COMMITS"
# 获取文件变化统计
echo "Getting file change statistics..."
FILE_STATS=$(git diff --stat "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null || echo "")
# 获取总体统计(最后一行)
SUMMARY_LINE=$(echo "$FILE_STATS" | tail -1)
echo "Summary: $SUMMARY_LINE"
# 获取每个文件的变化(去掉最后一行汇总)
# 截断过长的输出最多50个文件每行最多80字符
FILE_CHANGES=$(echo "$FILE_STATS" | head -n -1 | head -50 | cut -c1-80)
# 如果文件变化太多,进一步精简:只保留主要目录的变化
FILE_COUNT=$(echo "$FILE_STATS" | head -n -1 | wc -l)
if [ "$FILE_COUNT" -gt 50 ]; then
echo "Too many files ($FILE_COUNT), grouping by directory..."
# 按目录分组统计
DIR_STATS=$(git diff --stat "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null | head -n -1 | \
sed 's/|.*//g' | \
awk -F'/' '{if(NF>1) print $1"/"$2; else print $1}' | \
sort | uniq -c | sort -rn | head -20)
FILE_CHANGES="[按目录分组统计 - 共 $FILE_COUNT 个文件变更]
$DIR_STATS"
fi
echo "File changes:"
echo "$FILE_CHANGES"
# 获取具体代码变化关键文件的diff
echo "Getting code diff for key files..."
# 定义关键目录(优先展示这些目录的变化)
KEY_DIRS="packages/napcat-core packages/napcat-onebot packages/napcat-webui-backend"
# 获取变更的关键文件列表(排除测试、配置等)
# 使用 || true 防止 grep 无匹配时返回非零退出码
KEY_FILES=$(git diff --name-only "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null | \
grep -E "^packages/napcat-(core|onebot|webui-backend|shell)/" || true | \
grep -E "\.(ts|js)$" || true | \
grep -v -E "(test|spec|\.d\.ts|config)" || true | \
head -15) || true
CODE_DIFF=""
DIFF_CHAR_LIMIT=6000 # 总diff字符限制
CURRENT_CHARS=0
if [ -n "$KEY_FILES" ]; then
for file in $KEY_FILES; do
if [ "$CURRENT_CHARS" -ge "$DIFF_CHAR_LIMIT" ]; then
CODE_DIFF="$CODE_DIFF
[... 更多文件变化已截断 ...]"
break
fi
# 获取单个文件的diff限制每个文件最多50行
FILE_DIFF=$(git diff "$PREV_TAG".."$CURRENT_TAG" -- "$file" 2>/dev/null | head -50) || true
FILE_DIFF_LEN=${#FILE_DIFF}
# 如果单个文件diff超过1500字符截断
if [ "$FILE_DIFF_LEN" -gt 1500 ]; then
FILE_DIFF=$(echo "$FILE_DIFF" | head -c 1500)
FILE_DIFF="$FILE_DIFF
[... 文件 $file 变化已截断 ...]"
fi
if [ -n "$FILE_DIFF" ]; then
CODE_DIFF="$CODE_DIFF
### $file
\`\`\`diff
$FILE_DIFF
\`\`\`"
CURRENT_CHARS=$((CURRENT_CHARS + FILE_DIFF_LEN))
fi
done
fi
# 如果没有关键文件变化获取前5个变更文件的diff
if [ -z "$CODE_DIFF" ]; then
echo "No key files changed, getting top changed files..."
TOP_FILES=$(git diff --name-only "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null | \
grep -E "\.(ts|js|yml|md)$" | head -5) || true
if [ -n "$TOP_FILES" ]; then
for file in $TOP_FILES; do
FILE_DIFF=$(git diff "$PREV_TAG".."$CURRENT_TAG" -- "$file" 2>/dev/null | head -30) || true
if [ -n "$FILE_DIFF" ] && [ ${#FILE_DIFF} -lt 1000 ]; then
CODE_DIFF="$CODE_DIFF
### $file
\`\`\`diff
$FILE_DIFF
\`\`\`"
fi
done
fi
fi
# 如果仍然没有代码变化,添加说明
if [ -z "$CODE_DIFF" ]; then
CODE_DIFF="[本次更新主要涉及配置文件和文档变更,无核心代码变化]"
fi
echo "Code diff preview:"
echo "$CODE_DIFF" | head -50
# 读取 prompt
PROMPT_FILE=".github/prompt/release_note_prompt.txt"
SYSTEM_PROMPT=$(<"$PROMPT_FILE")
# 构建用户内容传递更多上下文包含文件变化和代码diff
USER_CONTENT="当前版本: $CURRENT_TAG
上一版本: $PREV_TAG
## 提交列表
$COMMITS
## 文件变化统计
$SUMMARY_LINE
## 变更文件列表
$FILE_CHANGES
## 关键代码变化
$CODE_DIFF"
# 构建请求 JSON增加 max_tokens 以获取更完整的输出
BODY=$(jq -n \
--arg system "$SYSTEM_PROMPT" \
--arg user "$USER_CONTENT" \
--arg model "$OPENROUTER_MODEL" \
'{model: $model, messages:[{role:"system", content:$system},{role:"user", content:$user}], temperature:0.2, max_tokens:1500}')
echo "=== OpenRouter request body ==="
echo "$BODY" | jq .
# 调用 OpenRouter
if RESPONSE=$(curl -s -X POST "$OPENROUTER_API_URL" \
-H "Authorization: Bearer $OPENAI_KEY" \
-H "Content-Type: application/json" \
-d "$BODY"); then
echo "=== raw response ==="
echo "$RESPONSE"
echo "=== OpenRouter raw response ==="
if echo "$RESPONSE" | jq . >/dev/null 2>&1; then
echo "$RESPONSE" | jq .
else
echo "jq failed to parse response"
fi
# 提取生成内容
RELEASE_BODY=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // .choices[0].text // ""' 2>/dev/null || echo "")
if [ -z "$RELEASE_BODY" ]; then
echo "❌ OpenRouter failed to generate release note, using default.md"
# 替换默认模板中的版本占位符
sed "s/{VERSION}/$CURRENT_TAG/g" .github/prompt/default.md > CHANGELOG.md
else
# 后处理:确保版本号正确,并添加比较链接
echo -e "$RELEASE_BODY" > CHANGELOG.md
# 替换可能的占位符
sed -i "s/{VERSION}/$CURRENT_TAG/g" CHANGELOG.md
sed -i "s/{PREV_VERSION}/$PREV_TAG/g" CHANGELOG.md
fi
else
echo "❌ Curl failed, using default.md"
sed "s/{VERSION}/$CURRENT_TAG/g" .github/prompt/default.md > CHANGELOG.md
fi
echo "=== generated release note ==="
cat CHANGELOG.md
- name: Create Release Draft and Upload Artifacts
uses: softprops/action-gh-release@v1
with:
name: NapCat ${{ github.ref_name }}
token: ${{ secrets.GITHUB_TOKEN }}
body_path: CHANGELOG.md
files: |
NapCat.Shell.Windows.Node.zip
NapCat.Framework.zip
NapCat.Shell.zip
NapCat.Shell.Windows.OneKey.zip
draft: true

7
.gitignore vendored
View File

@@ -1,14 +1,12 @@
# Develop
node_modules/
package-lock.json
pnpm-lock.yaml
out/
dist/
/src/core.lib/common/
/localdebug/
devconfig/*
# Editor
.vscode/*
!.vscode/extensions.json
.idea/*
@@ -16,3 +14,6 @@ dist/
*.db
checkVersion.sh
bun.lockb
tests/run/
guild1.db-wal
guild1.db-shm

View File

@@ -1,10 +0,0 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"bracketSpacing": true,
"arrowParens": "always",
"printWidth": 120,
"endOfLine": "auto"
}

12
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node-terminal",
"request": "launch",
"name": "调试程序",
"command": "pnpm run dev:shell",
"cwd": "${workspaceFolder}"
}
]
}

37
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
".env.universal": ".env.*",
"vite.config.ts": "vite*.ts",
"README.md": "CODE_OF_CONDUCT.md, RELEASES.md, CONTRIBUTING.md, CHANGELOG.md, SECURITY.md",
"tsconfig.json": "tsconfig.*.json, env.d.ts",
"package.json": "package-lock.json, eslint*, .prettier*, .editorconfig, manifest.json, logo.png, .gitignore, LICENSE"
},
"css.customData": [
".vscode/tailwindcss.json"
],
"editor.detectIndentation": false,
"editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.formatOnType": false,
"editor.formatOnPaste": true,
"editor.formatOnSaveMode": "file",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"files.autoSave": "onFocusChange",
"javascript.preferences.quoteStyle": "single",
"typescript.preferences.quoteStyle": "single",
"javascript.format.semicolons": "insert",
"typescript.format.semicolons": "insert",
"javascript.format.insertSpaceBeforeFunctionParenthesis": true,
"typescript.format.insertSpaceBeforeFunctionParenthesis": true,
"typescript.format.insertSpaceAfterConstructor": true,
"javascript.format.insertSpaceAfterConstructor": true,
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "minimal",
"javascript.preferences.importModuleSpecifier": "non-relative",
"javascript.preferences.importModuleSpecifierEnding": "minimal",
"typescript.disableAutomaticTypeAcquisition": true
}

55
.vscode/tailwindcss.json vendored Normal file
View File

@@ -0,0 +1,55 @@
{
"version": 1.1,
"atDirectives": [
{
"name": "@tailwind",
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
}
]
},
{
"name": "@apply",
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that youd like to extract to a new component.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
}
]
},
{
"name": "@responsive",
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
}
]
},
{
"name": "@screen",
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
}
]
},
{
"name": "@variants",
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
}
]
}
]
}

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
nanaeonn@outlook.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,53 +1,89 @@
<img src="https://napneko.github.io/assets/newnewlogo.png" width = "305" height = "411" alt="NapCat" align=right />
<div align="center">
![Logo](https://socialify.git.ci/NapNeko/NapCatQQ/image?font=Jost&logo=https%3A%2F%2Fnapneko.github.io%2Fassets%2Flogo.png&name=1&owner=1&pattern=Diagonal%20Stripes&stargazers=1&theme=Auto)
# NapCat
_Modern protocol-side framework implemented based on NTQQ._
> 云起兮风生,心向远方兮路未曾至.
</div>
---
## 欢迎回家
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
## 特性介绍
- [x] **安装简单**:就算是笨蛋也能使用
- [x] **性能友好**:就算是低内存也能使用
- [x] **接口丰富**:就算是没有也能使用
- [x] **稳定好用**:就算是被捉也能使用
## New Feature
## 使用框架
在 v4.8.115+ 版本开始
1. NapCatQQ 支持 [Stream Api](https://napneko.github.io/develop/file)
2. NapCatQQ 推荐 message_id/user_id/group_id 均使用字符串类型
- [1] 解决 Docker/跨设备/大文件 的多媒体上下传问题
- [2] 采用字符串可以解决扩展到int64的问题同时也可以解决部分语言如JavaScript对大整数支持不佳的问题增加极少成本。
## Welcome
- NapCatQQ is a modern implementation of the Bot protocol based on NTQQ.
- NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
## Feature
- **Easy to Use**
- 作为初学者能够轻松使用.
- **Quick and Efficient**
- 在低内存操作系统长时运行.
- **Rich API Interface**
- 完整实现了大部分标准接口.
- **Stable and Reliable**
- 持续稳定的开发与维护.
## Quick Start
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
**首次使用**请务必查看如下文档看使用教程
### 文档地址
> 项目非盈利,涉及 对接问题/基础问题/下层框架问题 请自行搜索解决,本项目社区不提供此类解答。
[Cloudflare.Worker](https://doc.napneko.icu/)
## Link
[Cloudflare.HKServer](https://napcat.napneko.icu/)
| Docs | [![Github.IO](https://img.shields.io/badge/docs%20on-Github.IO-orange)](https://napneko.github.io/) | [![Cloudflare.Worker](https://img.shields.io/badge/docs%20on-Cloudflare.Worker-black)](https://doc.napneko.icu/) | [![Cloudflare.HKServer](https://img.shields.io/badge/docs%20on-Cloudflare.HKServer-informational)](https://napcat.napneko.icu/) |
|:-:|:-:|:-:|:-:|
[Github.IO](https://napneko.github.io/)
| Docs | [![Cloudflare.Pages](https://img.shields.io/badge/docs%20on-Cloudflare.Pages-blue)](https://napneko.pages.dev/) | [![Server.Other](https://img.shields.io/badge/docs%20on-Server.Other-green)](https://napcat.cyou/) | [![NapCat.Wiki](https://img.shields.io/badge/docs%20on-NapCat.Wiki-red)](https://www.napcat.wiki) |
|:-:|:-:|:-:|:-:|
[Cloudflare.Pages](https://napneko.pages.dev/)
| QQ Group | [![QQ Group#4](https://img.shields.io/badge/QQ%20Group%234-Join-blue)](https://qm.qq.com/q/CMmPbGw0jA) | [![QQ Group#3](https://img.shields.io/badge/QQ%20Group%233-Join-blue)](https://qm.qq.com/q/8zJMLjqy2Y) | [![QQ Group#2](https://img.shields.io/badge/QQ%20Group%232-Join-blue)](https://qm.qq.com/q/CMmPbGw0jA) | [![QQ Group#1](https://img.shields.io/badge/QQ%20Group%231-Join-blue)](https://qm.qq.com/q/I6LU87a0Yq) |
|:-:|:-:|:-:|:-:|:-:|
[Server.Other](https://napcat.cyou/)
| Telegram | [![Telegram](https://img.shields.io/badge/Telegram-napcatqq-blue)](https://t.me/napcatqq) |
|:-:|:-:|
| DeepWiki | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/NapNeko/NapCatQQ) |
|:-:|:-:|
## 回家旅途
[QQ Group](https://qm.qq.com/q/I6LU87a0Yq)
> 请不要在其余社区提及本项目(包括其余协议端/相关应用端项目)引发争论如有建议到达官方交流群讨论或PR。
## 感谢他们
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
## Thanks
感谢 Tencent Tdesign / Vue3 强力驱动 NapCat.WebUi
- [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
不过最最重要的 还是需要感谢屏幕前的你哦~
- [AstrBot](https://github.com/AstrBotDevs/AstrBot) 是完美适配本项目的LLM Bot框架 在此推荐一下
- [MaiBot](https://github.com/MaiM-with-u/MaiBot) 一只赛博群友 麦麦 Bot框架 在此推荐一下
- [qq-chat-exporter](https://github.com/shuakami/qq-chat-exporter/) 基于NapCat的消息导出工具 在此推荐一下
- 不过最最重要的 还是需要感谢屏幕前的你哦~
---
## 特殊感谢
[LLOneBot](https://github.com/LLOneBot/LLOneBot) 相关的开发曾参与本项目
## License
## 开源附加
本项目采用 混合协议 开源,因此使用本项目时,你需要注意以下几点:
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经仓库主作者授权二次分发或基于 NapCat 代码开发。**
1. 第三方库代码或修改部分遵循其原始开源许可.
2. 本项目获取部分项目授权而不受部分约束
2. 项目其余逻辑代码采用[本仓库开源许可](./LICENSE).
**本仓库仅用于提高易用性,实现消息推送类功能,此外,禁止任何项目未经仓库主作者授权基于 NapCat 代码开发。使用请遵守当地法律法规,由此造成的问题由使用者和提供违规使用教程者负责。**

11
SECURITY.md Normal file
View File

@@ -0,0 +1,11 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| > 4.0 | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
you should open an issue

52
eslint.config.js Normal file
View File

@@ -0,0 +1,52 @@
import neostandard from 'neostandard';
/** 尾随逗号 */
const commaDangle = val => {
if (val?.rules?.['@stylistic/comma-dangle']?.[0] === 'warn') {
const rule = val?.rules?.['@stylistic/comma-dangle']?.[1];
Object.keys(rule).forEach(key => {
rule[key] = 'always-multiline';
});
val.rules['@stylistic/comma-dangle'][1] = rule;
}
/** 三元表达式 */
if (val?.rules?.['@stylistic/indent']) {
val.rules['@stylistic/indent'][2] = {
...val.rules?.['@stylistic/indent']?.[2],
flatTernaryExpressions: true,
offsetTernaryExpressions: false,
};
}
/** 支持下划线 - 禁用 camelcase 规则 */
if (val?.rules?.camelcase) {
val.rules.camelcase = 'off';
}
/** 未使用的变量强制报错 */
if (val?.rules?.['@typescript-eslint/no-unused-vars']) {
val.rules['@typescript-eslint/no-unused-vars'] = ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
}];
}
return val;
};
/** 忽略的文件 */
const ignores = [
'node_modules',
'**/dist/**',
'launcher',
];
const options = neostandard({
ts: true,
ignores,
semi: true, // 强制使用分号
}).map(commaDangle);
export default options;

View File

@@ -1,70 +0,0 @@
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import _import from "eslint-plugin-import";
import { fixupPluginRules } from "@eslint/compat";
import globals from "globals";
import tsParser from "@typescript-eslint/parser";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
const compat = new FlatCompat({
baseDirectory: dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
export default [{
ignores: ["src/core/proto/"],
}, ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), {
plugins: {
"@typescript-eslint": typescriptEslint,
import: fixupPluginRules(_import),
},
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
parser: tsParser,
ecmaVersion: "latest",
sourceType: "module",
},
settings: {
"import/parsers": {
"@typescript-eslint/parser": [".ts"],
},
"import/resolver": {
typescript: {
alwaysTryTypes: true,
},
},
},
rules: {
indent: ["error", 4],
semi: ["error", "always"],
"no-unused-vars": "off",
"no-async-promise-executor": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-var-requires": "off",
"object-curly-spacing": ["error", "always"],
},
}, {
files: ["**/.eslintrc.{js,cjs}"],
languageOptions: {
globals: {
...globals.node,
},
ecmaVersion: 5,
sourceType: "commonjs",
},
}];

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +0,0 @@
@echo off
REM ./launcher.bat 123456
REM ./launcher-win10.bat 123456
REM 带有REM的为注释 删掉你需要的系统的那行REM这三个单词 修改QQ本脚本启动即可

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

After

Width:  |  Height:  |  Size: 250 KiB

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["Vue.volar"]
}

View File

@@ -1,5 +0,0 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

View File

@@ -1,52 +0,0 @@
import globals from 'globals';
import ts from 'typescript-eslint';
import vue from 'eslint-plugin-vue';
import prettier from 'eslint-plugin-prettier/recommended';
export default [
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
...ts.configs.recommended,
{
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-var-requires': 'warn',
},
},
...vue.configs['flat/base'],
{
files: ['*.vue', '**/*.vue'],
languageOptions: {
parserOptions: {
parser: ts.parser,
},
},
},
{
rules: {
indent: ['error', 4],
semi: ['error', 'always'],
'no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-var-requires': 'warn',
'object-curly-spacing': ['error', 'always'],
'vue/v-for-delimiter-style': ['error', 'in'],
'vue/require-name-property': 'warn',
'vue/prefer-true-attribute-shorthand': 'warn',
'prefer-arrow-callback': 'warn',
},
},
prettier,
{
rules: {
'prettier/prettier': 'warn',
},
},
];

View File

@@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./logo_webui.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NapCat WebUI</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>

View File

@@ -1,34 +0,0 @@
{
"name": "napcat.webui",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"webui:lint": "eslint --fix src/**/*.{js,ts,vue}",
"webui:dev": "vite",
"webui:build": "vite build",
"webui:preview": "vite preview"
},
"dependencies": {
"eslint-plugin-prettier": "^5.2.1",
"qrcode": "^1.5.4",
"tdesign-icons-vue-next": "^0.3.3",
"tdesign-vue-next": "^1.10.3",
"vue": "^3.5.13",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@types/qrcode": "^1.5.5",
"@vitejs/plugin-legacy": "^5.4.3",
"@vitejs/plugin-vue": "^5.1.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-vue": "^9.31.0",
"globals": "^15.12.0",
"terser": "^5.36.0",
"typescript": "~5.6.2",
"vite": "^5.4.10",
"vue-tsc": "^2.1.8"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,112 +0,0 @@
<template>
<div id="app" theme-mode="dark">
<router-view />
</div>
<div v-if="show">
<t-sticky-tool shape="round" placement="right-bottom" :offset="[-50, 10]" @click="changeTheme">
<t-sticky-item label="浅色" popup="切换浅色模式">
<template #icon><sunny-icon /></template>
</t-sticky-item>
<t-sticky-item label="深色" popup="切换深色模式">
<template #icon><mode-dark-icon /></template>
</t-sticky-item>
<t-sticky-item label="自动" popup="跟随系统">
<template #icon><control-platform-icon /></template>
</t-sticky-item>
</t-sticky-tool>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, onUnmounted, ref } from 'vue';
import { ControlPlatformIcon, ModeDarkIcon, SunnyIcon } from 'tdesign-icons-vue-next';
const smallScreen = window.matchMedia('(max-width: 768px)');
interface Item {
label: string;
popup: string;
}
interface Context {
item: Item;
}
enum ThemeMode {
Dark = 'dark',
Light = 'light',
Auto = 'auto',
}
const themeLabelMap: Record<string, ThemeMode> = {
"浅色": ThemeMode.Light,
"深色": ThemeMode.Dark,
"自动": ThemeMode.Auto,
};
const show = ref<boolean>(true);
const createSetThemeAttributeFunction = () => {
let mediaQueryForAutoTheme: MediaQueryList | null = null;
return (mode: ThemeMode | null) => {
const element = document.documentElement;
if (mode === ThemeMode.Dark) {
element.setAttribute('theme-mode', ThemeMode.Dark);
} else if (mode === ThemeMode.Light) {
element.removeAttribute('theme-mode');
} else if (mode === ThemeMode.Auto) {
mediaQueryForAutoTheme = window.matchMedia('(prefers-color-scheme: dark)');
const handleMediaChange = (e: MediaQueryListEvent) => {
if (e.matches) {
element.setAttribute('theme-mode', ThemeMode.Dark);
} else {
element.removeAttribute('theme-mode');
}
};
mediaQueryForAutoTheme.addEventListener('change', handleMediaChange);
const event = new Event('change');
Object.defineProperty(event, 'matches', {
value: mediaQueryForAutoTheme.matches,
writable: false,
});
mediaQueryForAutoTheme.dispatchEvent(event);
onBeforeUnmount(() => {
if (mediaQueryForAutoTheme) {
mediaQueryForAutoTheme.removeEventListener('change', handleMediaChange);
}
});
}
};
};
const setThemeAttribute = createSetThemeAttributeFunction();
const getStoredTheme = (): ThemeMode | null => {
return localStorage.getItem('theme') as ThemeMode | null;
};
const initTheme = () => {
const storedTheme = getStoredTheme();
if (storedTheme === null) {
setThemeAttribute(ThemeMode.Auto);
} else {
setThemeAttribute(storedTheme);
}
};
const changeTheme = (context: Context) => {
const themeLabel = themeLabelMap[context.item.label] as ThemeMode;
console.log(themeLabel);
setThemeAttribute(themeLabel);
localStorage.setItem('theme', themeLabel);
};
const haddingFbars = () => {
show.value = !smallScreen.matches;
if (smallScreen.matches) {
localStorage.setItem('theme', 'auto');
}
};
onMounted(() => {
initTheme();
haddingFbars();
window.addEventListener('resize', haddingFbars);
});
onUnmounted(() => {
window.removeEventListener('resize', haddingFbars);
});
</script>
<style scoped></style>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

Before

Width:  |  Height:  |  Size: 496 B

View File

@@ -1,205 +0,0 @@
import { OneBotConfig } from '../../../src/onebot/config/config';
export class QQLoginManager {
private retCredential: string;
private readonly apiPrefix: string;
//调试时http://127.0.0.1:6099/api 打包时 ../api
constructor(retCredential: string, apiPrefix: string = '../api') {
this.retCredential = retCredential;
this.apiPrefix = apiPrefix;
}
// TODO:
public async GetOB11Config(): Promise<OneBotConfig> {
try {
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (ConfigResponse.status == 200) {
const ConfigResponseJson = await ConfigResponse.json();
if (ConfigResponseJson.code == 0) {
return ConfigResponseJson?.data as OneBotConfig;
}
}
} catch (error) {
console.error('Error getting OB11 config:', error);
}
return {} as OneBotConfig;
}
public async SetOB11Config(config: OneBotConfig): Promise<boolean> {
try {
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
body: JSON.stringify({ config: JSON.stringify(config) }),
});
if (ConfigResponse.status == 200) {
const ConfigResponseJson = await ConfigResponse.json();
if (ConfigResponseJson.code == 0) {
return true;
}
}
} catch (error) {
console.error('Error setting OB11 config:', error);
}
return false;
}
public async checkQQLoginStatus(): Promise<boolean> {
try {
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (QQLoginResponse.status == 200) {
const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data.isLogin;
}
}
} catch (error) {
console.error('Error checking QQ login status:', error);
}
return false;
}
public async checkQQLoginStatusWithQrcode(): Promise<{ qrcodeurl: string; isLogin: string } | undefined> {
try {
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (QQLoginResponse.status == 200) {
const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data;
}
}
} catch (error) {
console.error('Error checking QQ login status:', error);
}
return undefined;
}
public async checkWebUiLogined(): Promise<boolean> {
try {
const LoginResponse = await fetch(`${this.apiPrefix}/auth/check`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (LoginResponse.status == 200) {
const LoginResponseJson = await LoginResponse.json();
if (LoginResponseJson.code == 0) {
return true;
}
}
} catch (error) {
console.error('Error checking web UI login status:', error);
}
return false;
}
public async loginWithToken(token: string): Promise<string | null> {
try {
const loginResponse = await fetch(`${this.apiPrefix}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ token: token }),
});
const loginResponseJson = await loginResponse.json();
const retCode = loginResponseJson.code;
if (retCode === 0) {
this.retCredential = loginResponseJson.data.Credential;
return this.retCredential;
}
} catch (error) {
console.error('Error logging in with token:', error);
}
return null;
}
public async getQQLoginQrcode(): Promise<string> {
try {
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQQLoginQrcode`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (QQLoginResponse.status == 200) {
const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data.qrcode || '';
}
}
} catch (error) {
console.error('Error getting QQ login QR code:', error);
}
return '';
}
public async getQQQuickLoginList(): Promise<string[]> {
try {
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQuickLoginList`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (QQLoginResponse.status == 200) {
const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data || [];
}
}
} catch (error) {
console.error('Error getting QQ quick login list:', error);
}
return [];
}
public async setQuickLogin(uin: string): Promise<{ result: boolean; errMsg: string }> {
try {
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/SetQuickLogin`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
body: JSON.stringify({ uin: uin }),
});
if (QQLoginResponse.status == 200) {
const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) {
return { result: true, errMsg: '' };
} else {
return { result: false, errMsg: QQLoginResponseJson.message };
}
}
} catch (error) {
console.error('Error setting quick login:', error);
}
return { result: false, errMsg: '接口异常' };
}
}

View File

@@ -1,58 +0,0 @@
<template>
<t-layout class="dashboard-container">
<div ref="menuRef">
<SidebarMenu :menu-items="menuItems" class="sidebar-menu" />
</div>
<t-layout>
<router-view />
</t-layout>
</t-layout>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import SidebarMenu from './webui/Nav.vue';
import emitter from '@/ts/event-bus';
interface MenuItem {
value: string;
icon: string;
label: string;
route: string;
}
const menuItems = ref<MenuItem[]>([
{ value: 'item1', icon: 'dashboard', label: '基础信息', route: '/dashboard/basic-info' },
{ value: 'item3', icon: 'wifi-1', label: '网络配置', route: '/dashboard/network-config' },
{ value: 'item4', icon: 'setting', label: '其余配置', route: '/dashboard/other-config' },
{ value: 'item5', icon: 'system-log', label: '日志查看', route: '/dashboard/log-view' },
{ value: 'item6', icon: 'info-circle', label: '关于我们', route: '/dashboard/about-us' },
]);
const menuRef = ref<HTMLDivElement | null>(null);
emitter.on('sendMenu', (event) => {
emitter.emit('sendWidth', menuRef.value?.offsetWidth);
localStorage.setItem('menuWidth', menuRef.value?.offsetWidth?.toString() || '0');
});
onMounted(() => {
localStorage.setItem('menuWidth', menuRef.value?.offsetWidth?.toString() || '0');
});
</script>
<style scoped>
.dashboard-container {
display: flex;
flex-direction: row;
height: 100vh;
width: 100%;
}
.sidebar-menu {
position: relative;
z-index: 2;
}
@media (max-width: 768px) {
.content {
padding: 10px;
}
}
</style>

View File

@@ -1,185 +0,0 @@
<template>
<t-card class="layout">
<div class="login-container">
<h2 class="sotheby-font">QQ Login</h2>
<div class="login-methods">
<t-tooltip content="快速登录">
<t-button
id="quick-login"
class="login-method"
:class="{ active: loginMethod === 'quick' }"
@click="loginMethod = 'quick'"
>Quick Login</t-button
>
</t-tooltip>
<t-tooltip content="二维码登录">
<t-button
id="qrcode-login"
class="login-method"
:class="{ active: loginMethod === 'qrcode' }"
@click="loginMethod = 'qrcode'"
>QR Code</t-button
>
</t-tooltip>
</div>
<div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form">
<t-select
id="quick-login-select"
v-model="selectedAccount"
placeholder="Select Account"
@change="selectAccount"
>
<t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option>
</t-select>
</div>
<div v-show="loginMethod === 'qrcode'" id="qrcode" class="qrcode">
<canvas ref="qrcodeCanvas"></canvas>
</div>
</div>
<t-footer class="footer">Power By NapCat.WebUi</t-footer>
</t-card>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as QRCode from 'qrcode';
import { useRouter } from 'vue-router';
import { MessagePlugin } from 'tdesign-vue-next';
import { QQLoginManager } from '@/backend/shell';
const router = useRouter();
const loginMethod = ref<'quick' | 'qrcode'>('quick');
const quickLoginList = ref<string[]>([]);
const selectedAccount = ref<string>('');
const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
let heartBeatTimer: number | null = null;
let qrcodeUrl: string = '';
const selectAccount = async (accountName: string): Promise<void> => {
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
if (result) {
if (heartBeatTimer) {
clearInterval(heartBeatTimer);
}
await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' });
} else {
await MessagePlugin.error('登录失败,' + errMsg);
}
};
const generateQrCode = (data: string, canvas: HTMLCanvasElement | null): void => {
if (!canvas) {
console.error('Canvas element not found');
return;
}
QRCode.toCanvas(canvas, data, function (error: Error | null | undefined) {
if (error) {
console.error('Error generating QR Code:', error);
} else {
console.log('QR Code generated!');
}
});
};
const HeartBeat = async (): Promise<void> => {
const isLogined = await qqLoginManager.checkQQLoginStatusWithQrcode();
if (isLogined?.isLogin) {
if (heartBeatTimer) {
clearInterval(heartBeatTimer);
}
// //判断是否已经调转
// if (router.currentRoute.value.path !== '/dashboard/basic-info') {
// return;
// }
await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' });
} else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) {
qrcodeUrl = isLogined.qrcodeurl;
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
}
};
const InitPages = async (): Promise<void> => {
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
qrcodeUrl = await qqLoginManager.getQQLoginQrcode();
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
heartBeatTimer = window.setInterval(HeartBeat, 3000);
};
onMounted(() => {
InitPages();
});
</script>
<style scoped>
.layout {
height: 100vh;
}
.login-container {
padding: 20px;
border-radius: 5px;
max-width: 400px;
min-width: 300px;
position: relative;
margin: 50px auto;
}
@media (max-width: 600px) {
.login-container {
width: 90%;
min-width: unset;
}
}
.login-methods {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.login-method {
padding: 10px 15px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
.login-method.active {
background-color: #e6f0ff;
color: #007bff;
}
.login-form,
.qrcode {
display: flex;
flex-direction: column;
gap: 15px;
}
.qrcode {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
text-align: center;
}
.sotheby-font {
font-family: Sotheby, Helvetica, monospace;
font-size: 3.125rem;
line-height: 1.2;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.footer {
text-align: center;
margin: 0;
font-size: 0.875rem;
color: #888;
position: fixed;
bottom: 20px;
left: 0;
right: 0;
}
</style>

View File

@@ -1,153 +0,0 @@
<template>
<t-card class="layout">
<div class="login-container">
<h2 class="sotheby-font">WebUi Login</h2>
<t-form ref="form" :data="formData" colon :label-width="0" @submit="onSubmit">
<t-form-item name="password">
<t-input v-model="formData.token" type="password" clearable placeholder="请输入Token">
<template #prefix-icon>
<lock-on-icon />
</template>
</t-input>
</t-form-item>
<t-form-item>
<t-button theme="primary" type="submit" block>登录</t-button>
</t-form-item>
</t-form>
</div>
<t-footer class="footer">Power By NapCat.WebUi</t-footer>
</t-card>
</template>
<script setup lang="ts">
import '../css/style.css';
import '../css/font.css';
import { reactive, onMounted } from 'vue';
import { MessagePlugin } from 'tdesign-vue-next';
import { LockOnIcon } from 'tdesign-icons-vue-next';
import { useRouter } from 'vue-router';
import { QQLoginManager } from '@/backend/shell';
const router = useRouter();
interface FormData {
token: string;
}
const formData: FormData = reactive({
token: '',
});
const handleLoginSuccess = async (credential: string) => {
localStorage.setItem('auth', credential);
await checkLoginStatus();
};
const handleLoginFailure = (message: string) => {
MessagePlugin.error(message);
};
const checkLoginStatus = async () => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
return;
}
const loginManager = new QQLoginManager(storedCredential);
const isWenUiLoggedIn = await loginManager.checkWebUiLogined();
console.log('isWenUiLoggedIn', isWenUiLoggedIn);
if (!isWenUiLoggedIn) {
return;
}
const isQQLoggedIn = await loginManager.checkQQLoginStatus();
if (isQQLoggedIn) {
await router.push({ path: '/dashboard/basic-info' });
} else {
await router.push({ path: '/qqlogin' });
}
};
const loginWithToken = async (token: string) => {
const loginManager = new QQLoginManager('');
const credential = await loginManager.loginWithToken(token);
if (credential) {
await handleLoginSuccess(credential);
} else {
handleLoginFailure('登录失败请检查Token');
}
};
onMounted(() => {
const url = new URL(window.location.href);
const token = url.searchParams.get('token');
if (token) {
loginWithToken(token);
}
checkLoginStatus();
});
const onSubmit = async ({ validateResult }: { validateResult: boolean }) => {
if (validateResult) {
await loginWithToken(formData.token);
} else {
handleLoginFailure('请填写Token');
}
};
</script>
<style scoped>
.layout {
height: 100vh;
}
.login-container {
padding: 20px;
border-radius: 5px;
max-width: 400px;
min-width: 300px;
position: relative;
margin: 50px auto;
}
@media (max-width: 600px) {
.login-container {
width: 90%;
min-width: unset;
}
}
.tdesign-demo-block-column {
display: flex;
flex-direction: column;
row-gap: 16px;
}
.tdesign-demo-block-column-large {
display: flex;
flex-direction: column;
row-gap: 32px;
}
.tdesign-demo-block-row {
display: flex;
column-gap: 16px;
align-items: center;
}
.sotheby-font {
font-family: Sotheby, Helvetica, monospace;
font-size: 3.125rem;
line-height: 1.2;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.footer {
text-align: center;
margin: 0;
font-size: 0.875rem;
color: #888;
position: fixed;
bottom: 20px;
left: 0;
right: 0;
}
</style>

View File

@@ -1,127 +0,0 @@
<template>
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
<template #logo>
<div class="logo">
<img class="logo-img" :width="collapsed ? 35 : 'auto'" src="@/assets/logo_webui.png" alt="logo" />
<div class="logo-textBox">
<div class="logo-text">{{ collapsed ? '' : 'NapCat' }}</div>
</div>
</div>
</template>
<router-link v-for="item in menuItems" :key="item.value" :to="item.route">
<t-tooltip :disabled="!collapsed" :content="item.label" placement="right">
<t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item">
<template #icon>
<t-icon :name="item.icon" />
</template>
{{ item.label }}
</t-menu-item>
</t-tooltip>
</router-link>
<template #operations>
<t-button
:disabled="disBtn"
class="t-demo-collapse-btn"
variant="text"
shape="square"
@click="changeCollapsed"
>
<template #icon><t-icon :name="iconName" /></template>
</t-button>
</template>
</t-menu>
</template>
<script setup lang="ts">
import { ref, defineProps, onMounted, watch } from 'vue';
import emitter from '@/ts/event-bus';
type MenuItem = {
value: string;
label: string;
route: string;
icon?: string;
disabled?: boolean;
};
defineProps<{
menuItems: MenuItem[];
}>();
const collapsed = ref<boolean>(localStorage.getItem('sidebar-collapsed') === 'true');
const iconName = ref<string>(collapsed.value ? 'menu-unfold' : 'menu-fold');
const disBtn = ref<boolean>(false);
const changeCollapsed = (): void => {
collapsed.value = !collapsed.value;
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
localStorage.setItem('sidebar-collapsed', collapsed.value.toString());
};
watch(collapsed, (newValue, oldValue) => {
setTimeout(() => {
emitter.emit('sendMenu', collapsed.value);
}, 300);
});
onMounted(() => {
const mediaQuery = window.matchMedia('(max-width: 800px)');
const handleMediaChange = (e: MediaQueryListEvent) => {
disBtn.value = e.matches;
if (e.matches) {
collapsed.value = e.matches;
}
};
mediaQuery.addEventListener('change', handleMediaChange);
const event = new Event('change');
Object.defineProperty(event, 'matches', {
value: mediaQuery.matches,
writable: false,
});
mediaQuery.dispatchEvent(event);
return () => {
mediaQuery.removeEventListener('change', handleMediaChange);
};
});
</script>
<style scoped>
.sidebar-menu {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 200px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
@media (max-width: 768px) {
.sidebar-menu {
width: 100px; /* 移动端侧边栏宽度 */
}
}
.logo {
display: flex;
width: auto;
height: 100%;
}
.logo-img {
object-fit: contain;
margin-top: 8px;
margin-bottom: 8px;
}
.logo-textBox {
display: flex;
align-items: center;
margin-left: 10px;
}
.logo-text {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 22px;
font-family: Sotheby, Helvetica, monospace;
}
.menu-item {
margin-bottom: 10px;
}
</style>

View File

@@ -1,6 +0,0 @@
@font-face {
font-family: 'Sotheby';
src: url('../assets/Sotheby.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}

View File

@@ -1,84 +0,0 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
height: 100%;
width: 100%;
margin: 0;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@@ -1,87 +0,0 @@
import { createApp } from 'vue';
import App from './App.vue';
import {
Button as TButton,
Input as TInput,
Form as TForm,
FormItem as TFormItem,
Select as TSelect,
Option as TOption,
Menu as TMenu,
MenuItem as TMenuItem,
Icon as TIcon,
Submenu as TSubmenu,
Col as TCol,
Row as TRow,
Card as TCard,
Divider as TDivider,
Link as TLink,
List as TList,
Alert as TAlert,
Tag as TTag,
Descriptions as TDescriptionsProps,
DescriptionsItem as TDescriptionsItem,
Collapse as TCollapse,
CollapsePanel as TCollapsePanel,
ListItem as TListItem,
Tabs as TTabs,
TabPanel as TTabPanel,
Space as TSpace,
Checkbox as TCheckbox,
Popup as TPopup,
Dialog as TDialog,
Switch as TSwitch,
Tooltip as Tooltip,
StickyTool as TStickyTool,
StickyItem as TStickyItem,
Layout as TLayout,
Content as TContent,
Footer as TFooter,
Aside as TAside,
Popconfirm as Tpopconfirm,
Empty as TEmpty,
} from 'tdesign-vue-next';
import { router } from './router';
import 'tdesign-vue-next/es/style/index.css';
const app = createApp(App);
app.use(router);
app.use(TButton);
app.use(TInput);
app.use(TForm);
app.use(TFormItem);
app.use(TSelect);
app.use(TOption);
app.use(TMenu);
app.use(TMenuItem);
app.use(TIcon);
app.use(TSubmenu);
app.use(TCol);
app.use(TRow);
app.use(TCard);
app.use(TDivider);
app.use(TLink);
app.use(TList);
app.use(TAlert);
app.use(TTag);
app.use(TDescriptionsProps);
app.use(TDescriptionsItem);
app.use(TCollapse);
app.use(TCollapsePanel);
app.use(TListItem);
app.use(TTabs);
app.use(TTabPanel);
app.use(TSpace);
app.use(TCheckbox);
app.use(TPopup);
app.use(TDialog);
app.use(TSwitch);
app.use(Tooltip);
app.use(TStickyTool);
app.use(TStickyItem);
app.use(TLayout);
app.use(TContent);
app.use(TFooter);
app.use(TAside);
app.use(Tpopconfirm);
app.use(TEmpty);
app.mount('#app');

View File

@@ -1,66 +0,0 @@
<template>
<div class="about-us">
<div>
<t-divider content="面板关于信息" align="left" />
<t-alert theme="success" message="NapCat.WebUi is running" />
<t-list class="list">
<t-list-item class="list-item">
<span class="item-label">开发人员:</span>
<span class="item-content">
<t-link href="mailto:nanaeonn@outlook.com">Mlikiowa</t-link>
</span>
</t-list-item>
<t-list-item class="list-item">
<span class="item-label">版本信息:</span>
<span class="item-content">
<t-tag class="tag-item" theme="success"> WebUi: {{ pkg.version }} </t-tag>
<t-tag class="tag-item" theme="success"> NapCat: {{ napCatVersion }} </t-tag>
<t-tag class="tag-item" theme="success">
TDesign: {{ pkg.dependencies['tdesign-vue-next'] }}
</t-tag>
</span>
</t-list-item>
</t-list>
</div>
</div>
</template>
<script setup lang="ts">
import pkg from '../../package.json';
import { napCatVersion } from '../../../src/common/version';
</script>
<style scoped>
.about-us {
padding: 20px;
text-align: left;
}
.list {
display: flex;
flex-direction: column;
}
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-label {
flex: 1;
font-weight: bold;
}
.item-content {
flex: 2;
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
}
.tag-item {
margin-right: 10px;
margin-bottom: 10px;
}
</style>

View File

@@ -1,6 +0,0 @@
<template>
<div class="basic-info">
<h1>面板基础信息</h1>
<p>这里显示面板的基础信息</p>
</div>
</template>

View File

@@ -1,6 +0,0 @@
<template>
<div class="log-view">
<h1>面板日志信息</h1>
<p>这里显示面板的日志信息</p>
</div>
</template>

View File

@@ -1,540 +0,0 @@
<template>
<div ref="headerBox" class="title">
<t-divider content="网络配置" align="left" />
<t-divider align="right">
<t-button @click="addConfig()">
<template #icon><add-icon /></template>
添加配置</t-button>
</t-divider>
</div>
<div v-if="loadPage" ref="setting" class="setting">
<t-tabs ref="tabsRef" :style="{ width: tabsWidth + 'px' }" default-value="all" @change="selectType">
<t-tab-panel value="all" label="全部"></t-tab-panel>
<t-tab-panel value="httpServers" label="HTTP 服务器"></t-tab-panel>
<t-tab-panel value="httpClients" label="HTTP 客户端"></t-tab-panel>
<t-tab-panel value="websocketServers" label="WebSocket 服务器"></t-tab-panel>
<t-tab-panel value="websocketClients" label="WebSocket 客户端"></t-tab-panel>
</t-tabs>
</div>
<div v-if="loadPage" class="card-box" :style="{ width: tabsWidth + 'px' }">
<div class="setting-box" :style="{ maxHeight: cardHeight + 'px' }" v-if="cardConfig.length > 0">
<div v-for="(item, index) in cardConfig" :key="index">
<t-card :title="item.name" :description="item.type" :style="{ width: cardWidth + 'px' }"
:header-bordered="true" class="setting-card">
<template #actions>
<t-space>
<edit2-icon size="20px" @click="editConfig(item)"></edit2-icon>
<t-popconfirm theme="danger" content="确认删除" @confirm="delConfig(item)">
<delete-icon size="20px"></delete-icon>
</t-popconfirm>
</t-space>
</template>
<div class="setting-content">
<t-card class="card-address" :style="{
borderLeft: '7px solid ' + (item.enable ?
'var(--td-success-color)' :
'var(--td-error-color)')
}">
<div class="local-box" v-if="item.host&&item.port">
<server-filled-icon class="local-icon" size="20px"></server-filled-icon>
<strong class="local">{{ item.host }}:{{ item.port }}</strong>
<copy-icon class="copy-icon" size="20px" @click="copyText(item.host + ':' + item.port)"></copy-icon>
</div>
<div class="local-box" v-if="item.url">
<server-filled-icon class="local-icon" size="20px"></server-filled-icon>
<strong class="local" >{{ item.url }}</strong>
<copy-icon class="copy-icon" size="20px" @click="copyText(item.url)"></copy-icon>
</div>
</t-card>
<t-collapse :default-value="[0]" expand-mutex style="margin-top:10px;" class="info-coll">
<t-collapse-panel header="基础信息">
<t-descriptions size="small" :layout="infoOneCol ? 'vertical' : 'horizontal'"
class="setting-base-info">
<t-descriptions-item v-if="item.token" label="连接密钥">
<div v-if="mediumScreen.matches||largeScreen.matches" class="token-view">
<span>{{ showToken ? item.token : '******' }}</span>
<browse-icon class="browse-icon" v-if="showToken" size="18px"
@click="showToken = false"></browse-icon>
<browse-off-icon class="browse-icon" v-else size="18px"
@click="showToken = true"></browse-off-icon>
</div>
<div v-else>
<t-popup :showArrow="true" trigger="click">
<t-tag theme="primary">点击查看</t-tag>
<template #content>
<div @click="copyText(item.token)">{{item.token}}</div>
</template>
</t-popup>
</div>
</t-descriptions-item>
<t-descriptions-item label="消息格式">{{ item.messagePostFormat }}</t-descriptions-item>
</t-descriptions>
</t-collapse-panel>
<t-collapse-panel header="状态信息">
<t-descriptions size="small" :layout="infoOneCol ? 'vertical' : 'horizontal'"
class="setting-base-info">
<t-descriptions-item v-if="item.hasOwnProperty('debug')" label="调试日志">
<t-tag class="tag-item" :theme="item.debug ? 'success' : 'danger'">
{{ item.debug ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableWebsocket')"
label="Websocket 功能">
<t-tag class="tag-item" :theme="item.enableWebsocket ? 'success' : 'danger'">
{{ item.enableWebsocket ? '启用' : '禁用' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableCors')" label="跨域放行">
<t-tag class="tag-item" :theme="item.enableCors ? 'success' : 'danger'">
{{ item.enableCors ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableForcePushEvent')"
label="上报自身消息">
<t-tag class="tag-item" :theme="item.reportSelfMessage ? 'success' : 'danger'">
{{ item.reportSelfMessage ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableForcePushEvent')"
label="强制推送事件">
<t-tag class="tag-item"
:theme="item.enableForcePushEvent ? 'success' : 'danger'">
{{ item.enableForcePushEvent ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
</t-descriptions>
</t-collapse-panel>
</t-collapse>
</div>
</t-card>
</div>
<div style="height: 20vh"></div>
</div>
<t-card v-else>
<t-empty class="card-none" title="暂无网络配置"> </t-empty>
</t-card>
</div>
<t-dialog v-model:visible="visibleBody" :header="dialogTitle" :destroy-on-close="true"
:show-in-attached-element="true" placement="center" :on-confirm="saveConfig" class=".t-dialog__ctx .t-dialog--defaul">
<div slot="body" class="dialog-body" >
<t-form ref="form" :data="newTab" labelAlign="left" :model="newTab">
<t-form-item style="text-align: left" :rules="[{ required: true, message: '请输入名称', trigger: 'blur' }]"
label="名称" name="name">
<t-input v-model="newTab.name" />
</t-form-item>
<t-form-item style="text-align: left" :rules="[{ required: true, message: '请选择类型', trigger: 'change' }]"
label="类型" name="type">
<t-select v-model="newTab.type" @change="onloadDefault">
<t-option value="httpServers">HTTP 服务器</t-option>
<t-option value="httpClients">HTTP 客户端</t-option>
<t-option value="websocketServers">WebSocket 服务器</t-option>
<t-option value="websocketClients">WebSocket 客户端</t-option>
</t-select>
</t-form-item>
<div>
<component :is="resolveDynamicComponent(getComponent(newTab.type as ComponentKey))"
:config="newTab.data" />
</div>
</t-form>
</div>
</t-dialog>
</template>
<script setup lang="ts">
import { AddIcon, DeleteIcon, Edit2Icon, ServerFilledIcon, CopyIcon, BrowseOffIcon, BrowseIcon } from 'tdesign-icons-vue-next';
import { onMounted, onUnmounted, ref, resolveDynamicComponent } from 'vue';
import emitter from '@/ts/event-bus';
import {
mergeNetworkDefaultConfig,
mergeOneBotConfigs,
NetworkConfig,
OneBotConfig,
} from '../../../src/onebot/config/config';
import HttpServerComponent from '@/pages/network/HttpServerComponent.vue';
import HttpClientComponent from '@/pages/network/HttpClientComponent.vue';
import WebsocketServerComponent from '@/pages/network/WebsocketServerComponent.vue';
import WebsocketClientComponent from '@/pages/network/WebsocketClientComponent.vue';
import { MessagePlugin } from 'tdesign-vue-next';
import { QQLoginManager } from '@/backend/shell';
const showToken = ref<boolean>(false);
const infoOneCol = ref<boolean>(true);
const tabsWidth = ref<number>(0);
const menuWidth = ref<number>(0);
const cardWidth = ref<number>(0);
const cardHeight = ref<number>(0);
const mediumScreen = window.matchMedia('(min-width: 768px) and (max-width: 1024px)');
const largeScreen = window.matchMedia('(min-width: 1025px)');
const headerBox = ref<HTMLDivElement | null>(null);
const setting = ref<HTMLDivElement | null>(null);
const loadPage = ref<boolean>(false);
const visibleBody = ref<boolean>(false);
const newTab = ref<{ name: string; data: any; type: string }>({ name: '', data: {}, type: '' });
const dialogTitle = ref<string>('');
type ComponentKey = keyof typeof mergeNetworkDefaultConfig;
const componentMap: Record<
ComponentKey,
| typeof HttpServerComponent
| typeof HttpClientComponent
| typeof WebsocketServerComponent
| typeof WebsocketClientComponent
> = {
httpServers: HttpServerComponent,
httpClients: HttpClientComponent,
websocketServers: WebsocketServerComponent,
websocketClients: WebsocketClientComponent,
};
//操作类型
const operateType = ref<string>('');
//配置项索引
const configIndex = ref<number>(0);
//保存时所用数据
const networkConfig: NetworkConfig & { [key: string]: any; } = {
websocketClients: [],
websocketServers: [],
httpClients: [],
httpServers: [],
};
//挂载的数据
const WebConfg = ref(
new Map<string, Array<null>>([
['all', []],
['httpServers', []],
['httpClients', []],
['websocketServers', []],
['websocketClients', []],
])
);
const typeCh: Record<ComponentKey, string> = {
httpServers: 'HTTP 服务器',
httpClients: 'HTTP 客户端',
websocketServers: 'WebSocket 服务器',
websocketClients: 'WebSocket 客户端',
};
const cardConfig = ref<any>([]);
const getComponent = (type: ComponentKey) => {
return componentMap[type];
};
const getKeyByValue = (obj: typeof typeCh, value: string): string | undefined => {
return Object.entries(obj).find(([_, v]) => v === value)?.[0];
};
const addConfig = () => {
dialogTitle.value = '添加配置';
newTab.value = { name: '', data: {}, type: '' };
operateType.value = 'add';
visibleBody.value = true;
};
const editConfig = (item: any) => {
dialogTitle.value = '修改配置';
const type = getKeyByValue(typeCh, item.type);
if (type) {
newTab.value = { name: item.name, data: item, type: type };
}
operateType.value = 'edit';
configIndex.value = networkConfig[newTab.value.type].findIndex((obj: any) => obj.name === item.name);
visibleBody.value = true;
};
const delConfig = (item: any) => {
const type = getKeyByValue(typeCh, item.type);
if (type) {
newTab.value = { name: item.name, data: item, type: type };
}
configIndex.value = configIndex.value = networkConfig[newTab.value.type].findIndex(
(obj: any) => obj.name === item.name
);
operateType.value = 'delete';
saveConfig();
};
const selectType = (key: ComponentKey) => {
cardConfig.value = WebConfg.value.get(key);
};
const onloadDefault = (key: ComponentKey) => {
console.log(key);
newTab.value.data = structuredClone(mergeNetworkDefaultConfig[key]);
};
//检测重名
const checkName = (name: string) => {
const allConfigs = WebConfg.value.get('all')?.findIndex((obj: any) => obj.name === name);
if (newTab.value.name === '' || newTab.value.type === '') {
MessagePlugin.error('请填写完整信息');
return false;
} else if (allConfigs === -1 || newTab.value.data.name === name) {
return true;
} else {
MessagePlugin.error('名称已存在');
return false;
}
};
//保存
const saveConfig = async () => {
if (operateType.value == 'add') {
if (!checkName(newTab.value.name)) return;
newTab.value.data.name = newTab.value.name;
networkConfig[newTab.value.type].push(newTab.value.data);
} else if (operateType.value == 'edit') {
if (!checkName(newTab.value.name)) return;
newTab.value.data.name = newTab.value.name;
networkConfig[newTab.value.type][configIndex.value] = newTab.value.data;
} else if (operateType.value == 'delete') {
networkConfig[newTab.value.type].splice(configIndex.value, 1);
}
const userConfig = await getOB11Config();
if (!userConfig) return;
userConfig.network = networkConfig;
const success = await setOB11Config(userConfig);
if (success) {
operateType.value = '';
configIndex.value = 0;
MessagePlugin.success('配置保存成功');
await loadConfig();
visibleBody.value = false;
} else {
MessagePlugin.error('配置保存失败');
}
};
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
console.error('No stored credential found');
return;
}
const loginManager = new QQLoginManager(storedCredential);
return await loginManager.GetOB11Config();
};
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
console.error('No stored credential found');
return false;
}
const loginManager = new QQLoginManager(storedCredential);
return await loginManager.SetOB11Config(config);
};
//获取卡片数据
const getAllData = (data: NetworkConfig) => {
cardConfig.value = [];
WebConfg.value.set('all', []);
for (const key in data) {
const configs = data[key as keyof NetworkConfig];
if (key in mergeNetworkDefaultConfig) {
networkConfig[key] = [...configs];
const newConfigsArray = configs.map((config: any) => ({
...config,
type: typeCh[key as ComponentKey],
}));
WebConfg.value.set(key, newConfigsArray);
const allConfigs = WebConfg.value.get('all');
if (allConfigs) {
const newAllConfigs = [...allConfigs, ...newConfigsArray];
WebConfg.value.set('all', newAllConfigs);
}
cardConfig.value = WebConfg.value.get('all');
}
}
};
const loadConfig = async () => {
try {
const userConfig = await getOB11Config();
if (!userConfig) return;
const mergedConfig = mergeOneBotConfigs(userConfig);
getAllData(mergedConfig.network);
} catch (error) {
console.error('Error loading config:', error);
}
};
const copyText = async (text: string) => {
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
await navigator.clipboard.writeText(text);
document.body.removeChild(input);
MessagePlugin.success('复制成功');
};
const handleResize = () => {
// 得根据卡片宽度改,懒得改了;先不管了
// if(window.innerWidth < 540) {
// infoOneCol.value= true
// } else {
// infoOneCol.value= false
// }
tabsWidth.value = window.innerWidth - 41 - menuWidth.value;
if (mediumScreen.matches) {
cardWidth.value = (tabsWidth.value - 20) / 2;
} else if (largeScreen.matches) {
cardWidth.value = (tabsWidth.value - 40) / 3;
} else {
cardWidth.value = tabsWidth.value;
}
loadPage.value = true;
setTimeout(() => {
cardHeight.value = window.innerHeight - (headerBox.value?.offsetHeight ?? 0) - (setting.value?.offsetHeight ?? 0) - 21;
}, 300);
};
emitter.on('sendWidth', (width) => {
if (typeof width === 'number' && !isNaN(width)) {
menuWidth.value = width;
handleResize();
}
});
onMounted(() => {
loadConfig();
const cachedWidth = localStorage.getItem('menuWidth');
if (cachedWidth) {
menuWidth.value = parseInt(cachedWidth);
setTimeout(() => {
handleResize();
}, 300);
}
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped>
.title {
padding: 20px 20px 0 20px;
display: flex;
justify-content: space-between;
}
.setting {
margin: 0 20px;
}
.setting-box {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
overflow-y: auto;
}
.setting-card {
width: 100%;
text-align: left;
}
.setting-content {
width: 100%;
}
.card-address svg {
fill: var(--td-brand-color);
cursor: pointer;
}
.local-box {
display: flex;
margin-top: 2px;
}
.local-icon{
flex: 1;
}
.local {
flex: 6;
margin: 0 10px 0 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.copy-icon {
flex: 1;
cursor: pointer;
flex-direction: row;
}
.token-view {
display: flex;
align-items: center;
}
.token-view span {
flex: 5;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.browse-icon{
flex: 2;
}
:global(.t-dialog__ctx .t-dialog--defaul) {
margin: 0 20px;
}
@media (max-width: 1024px) {
.setting-box {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 786px) {
.setting-box {
grid-template-columns: 1fr;
}
}
.card-box {
margin: 10px 20px 0 20px;
}
.card-none {
line-height: 400px !important;
}
.dialog-body {
max-height: 60vh;
overflow-y: auto;
}
::-webkit-scrollbar {
width: 0;
background: transparent;
}
</style>
<style>
.setting-card .t-card__title {
text-align: left !important;
}
.setting-card .t-card__description {
margin-bottom: 0;
font-size: 12px;
}
.card-address .t-card__body {
display: flex;
flex-direction: row;
align-items: center;
}
.setting-base-info .t-descriptions__header {
font-size: 15px;
margin-bottom: 0;
}
.setting-base-info .t-descriptions__label {
padding: 0 var(--td-comp-paddingLR-l) !important;
}
.setting-base-info tr>td:last-child {
text-align: right;
}
.info-coll .t-collapse-panel__wrapper .t-collapse-panel__content {
padding: var(--td-comp-paddingTB-m) var(--td-comp-paddingLR-l);
}
</style>

View File

@@ -1,148 +0,0 @@
<template>
<div class="title">
<t-divider content="其余配置" align="left" />
</div>
<t-card class="card">
<div class="other-config-container">
<div class="other-config">
<t-form ref="form" :model="otherConfig" :label-align="labelAlign" label-width="auto" colon>
<t-form-item label="音乐签名地址" name="musicSignUrl" class="form-item">
<t-input v-model="otherConfig.musicSignUrl" />
</t-form-item>
<t-form-item label="启用本地文件到URL" name="enableLocalFile2Url" class="form-item">
<t-switch v-model="otherConfig.enableLocalFile2Url" />
</t-form-item>
<t-form-item label="启用上报解析合并消息" name="parseMultMsg" class="form-item">
<t-switch v-model="otherConfig.parseMultMsg" />
</t-form-item>
</t-form>
<div class="button-container">
<t-button @click="saveConfig">保存</t-button>
</div>
</div>
</div>
</t-card>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { MessagePlugin } from 'tdesign-vue-next';
import { OneBotConfig } from '../../../src/onebot/config/config';
import { QQLoginManager } from '@/backend/shell';
const otherConfig = ref<Partial<OneBotConfig>>({
musicSignUrl: '',
enableLocalFile2Url: false,
parseMultMsg: true
});
const labelAlign = ref<string>();
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
console.error('No stored credential found');
return;
}
const loginManager = new QQLoginManager(storedCredential);
return await loginManager.GetOB11Config();
};
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
console.error('No stored credential found');
return false;
}
const loginManager = new QQLoginManager(storedCredential);
return await loginManager.SetOB11Config(config);
};
const loadConfig = async () => {
try {
const userConfig = await getOB11Config();
if (userConfig) {
otherConfig.value.musicSignUrl = userConfig.musicSignUrl;
otherConfig.value.enableLocalFile2Url = userConfig.enableLocalFile2Url;
otherConfig.value.parseMultMsg = userConfig.parseMultMsg;
}
} catch (error) {
console.error('Error loading config:', error);
}
};
const saveConfig = async () => {
try {
const userConfig = await getOB11Config();
if (userConfig) {
userConfig.musicSignUrl = otherConfig.value.musicSignUrl || '';
userConfig.enableLocalFile2Url = otherConfig.value.enableLocalFile2Url ?? false;
userConfig.parseMultMsg = otherConfig.value.parseMultMsg ?? true;
const success = await setOB11Config(userConfig);
if (success) {
MessagePlugin.success('配置保存成功');
} else {
MessagePlugin.error('配置保存失败');
}
}
} catch (error) {
console.error('Error saving config:', error);
MessagePlugin.error('配置保存失败');
}
};
onMounted(() => {
loadConfig();
const mediaQuery = window.matchMedia('(max-width: 768px)');
const handleMediaChange = (e: MediaQueryListEvent) => {
if (e.matches) {
labelAlign.value = 'top';
} else {
labelAlign.value = 'left';
}
};
mediaQuery.addEventListener('change', handleMediaChange);
const event = new Event('change');
Object.defineProperty(event, 'matches', {
value: mediaQuery.matches,
writable: false,
});
mediaQuery.dispatchEvent(event);
return () => {
mediaQuery.removeEventListener('change', handleMediaChange);
};
});
</script>
<style scoped>
.title {
padding: 20px 20px 0 20px;
}
.card {
margin: 0 20px;
padding-top: 20px;
padding-bottom: 20px;
}
.other-config-container {
display: flex;
justify-content: center;
align-items: flex-start;
box-sizing: border-box;
}
.other-config {
width: 100%;
max-width: 500px;
border-radius: 8px;
}
.form-item {
margin-bottom: 20px;
text-align: left;
}
.button-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
</style>

View File

@@ -1,49 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<t-form-item label="启用">
<t-checkbox v-model="config.enable" />
</t-form-item>
<t-form-item label="URL">
<t-input v-model="config.url" />
</t-form-item>
<t-form-item label="消息格式">
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
</t-form-item>
<t-form-item label="报告自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
</t-form>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, watch } from 'vue';
import { HttpClientConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{
config: HttpClientConfig;
}>();
const messageFormatOptions = ref([
{ label: 'Array', value: 'array' },
{ label: 'String', value: 'string' },
]);
watch(
() => props.config.messagePostFormat,
(newValue) => {
if (newValue !== 'array' && newValue !== 'string') {
props.config.messagePostFormat = 'array';
}
}
);
</script>
<style scoped></style>

View File

@@ -1,55 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<t-form-item label="启用">
<t-checkbox v-model="config.enable" />
</t-form-item>
<t-form-item label="端口">
<t-input v-model.number="config.port" type="number" />
</t-form-item>
<t-form-item label="主机">
<t-input v-model="config.host" type="text" />
</t-form-item>
<t-form-item label="启用 CORS">
<t-checkbox v-model="config.enableCors" />
</t-form-item>
<t-form-item label="启用 WS">
<t-checkbox v-model="config.enableWebsocket" />
</t-form-item>
<t-form-item label="消息格式">
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" type="text" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
</t-form>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, watch } from 'vue';
import { HttpServerConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{
config: HttpServerConfig;
}>();
const messageFormatOptions = ref([
{ label: 'Array', value: 'array' },
{ label: 'String', value: 'string' },
]);
watch(
() => props.config.messagePostFormat,
(newValue) => {
if (newValue !== 'array' && newValue !== 'string') {
props.config.messagePostFormat = 'array';
}
}
);
</script>
<style scoped></style>

View File

@@ -1,52 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<t-form-item label="启用">
<t-checkbox v-model="config.enable" />
</t-form-item>
<t-form-item label="URL">
<t-input v-model="config.url" />
</t-form-item>
<t-form-item label="消息格式">
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
</t-form-item>
<t-form-item label="报告自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
<t-form-item label="心跳间隔">
<t-input v-model.number="config.heartInterval" type="number" />
</t-form-item>
</t-form>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, watch } from 'vue';
import { WebsocketClientConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{
config: WebsocketClientConfig;
}>();
const messageFormatOptions = ref([
{ label: 'Array', value: 'array' },
{ label: 'String', value: 'string' },
]);
watch(
() => props.config.messagePostFormat,
(newValue) => {
if (newValue !== 'array' && newValue !== 'string') {
props.config.messagePostFormat = 'array';
}
}
);
</script>
<style scoped></style>

View File

@@ -1,58 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<t-form-item label="启用">
<t-checkbox v-model="config.enable" />
</t-form-item>
<t-form-item label="主机">
<t-input v-model="config.host" />
</t-form-item>
<t-form-item label="端口">
<t-input v-model.number="config.port" type="number" />
</t-form-item>
<t-form-item label="消息格式">
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
</t-form-item>
<t-form-item label="上报自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" />
</t-form-item>
<t-form-item label="强制推送事件">
<t-checkbox v-model="config.enableForcePushEvent" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
<t-form-item label="心跳间隔">
<t-input v-model.number="config.heartInterval" type="number" />
</t-form-item>
</t-form>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, watch } from 'vue';
import { WebsocketServerConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{
config: WebsocketServerConfig;
}>();
const messageFormatOptions = ref([
{ label: 'Array', value: 'array' },
{ label: 'String', value: 'string' },
]);
watch(
() => props.config.messagePostFormat,
(newValue) => {
if (newValue !== 'array' && newValue !== 'string') {
props.config.messagePostFormat = 'array';
}
}
);
</script>
<style scoped></style>

View File

@@ -1,32 +0,0 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import Dashboard from '../components/Dashboard.vue';
import BasicInfo from '../pages/BasicInfo.vue';
import AboutUs from '../pages/AboutUs.vue';
import LogView from '../pages/Log.vue';
import NetWork from '../pages/NetWork.vue';
import QQLogin from '../components/QQLogin.vue';
import WebUiLogin from '../components/WebUiLogin.vue';
import OtherConfig from '../pages/OtherConfig.vue';
const routes: Array<RouteRecordRaw> = [
{ path: '/', redirect: '/webui' },
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
{
path: '/dashboard',
component: Dashboard,
children: [
{ path: '', redirect: 'basic-info' },
{ path: 'basic-info', component: BasicInfo, name: 'BasicInfo' },
{ path: 'network-config', component: NetWork, name: 'NetWork' },
{ path: 'log-view', component: LogView, name: 'LogView' },
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
{ path: 'about-us', component: AboutUs, name: 'AboutUs' },
],
},
];
export const router = createRouter({
history: createWebHashHistory(),
routes,
});

View File

@@ -1,3 +0,0 @@
import mitt from 'mitt';
const emitter = mitt();
export default emitter;

View File

@@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@@ -1,34 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"jsxImportSource": "vue",
"lib": [
"DOM",
"DOM.Iterable"
],
"baseUrl": ".",
"module": "esnext",
"moduleResolution": "bundler",
"paths": {
"@/*": [
"src/*"
]
},
"resolveJsonModule": true,
"types": [
"vite/client"
],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"experimentalDecorators": true,
"useDefineForClassFields": true
},
"include": ["src"],
"exclude": ["node_modules"],
"references": [{"path": "./tsconfig.node.json"}]
}

View File

@@ -1,11 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strictNullChecks": true
},
"include": ["vite.config.ts"]
}

View File

@@ -1,41 +0,0 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import legacy from '@vitejs/plugin-legacy';
import path from 'path';
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ['defaults', 'not IE 11'],
modernPolyfills: ['web.structured-clone'],
}),
],
base: './',
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
server: {
proxy: {
'/api': 'http://localhost:6099',
},
},
build: {
chunkSizeWarningLimit: 4000,
rollupOptions: {
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks(id: string) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
},
},
},
},
});

View File

@@ -2,61 +2,32 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.2.12",
"version": "0.0.1",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
"build:shell": "npm run build:webui && vite build --mode shell || exit 1",
"build:webui": "cd napcat.webui && vite build",
"dev:universal": "vite build --mode universal",
"dev:framework": "vite build --mode framework",
"dev:shell": "vite build --mode shell",
"dev:webui": "cd napcat.webui && npm run webui:dev",
"lint": "eslint --fix src/**/*.{js,ts,vue}",
"depend": "cd dist && npm install --omit=dev"
"build:shell": "pnpm --filter napcat-shell run build || exit 1",
"build:shell:dev": "pnpm --filter napcat-shell run build:dev || exit 1",
"build:framework": "pnpm --filter napcat-framework run build || exit 1",
"build:webui": "pnpm --filter napcat-webui-frontend run build || exit 1",
"dev:shell": "pnpm --filter napcat-develop run dev || exit 1",
"typecheck": "pnpm -r --if-present run typecheck",
"test": "pnpm --filter napcat-test run test",
"test:ui": "pnpm --filter napcat-test run test:ui",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"@babel/preset-typescript": "^7.24.7",
"@eslint/compat": "^1.2.2",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@log4js-node/log4js-api": "^1.0.2",
"@napneko/nap-proto-core": "^0.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/fluent-ffmpeg": "^2.1.24",
"@types/node": "^22.0.1",
"@types/qrcode-terminal": "^0.12.2",
"@types/ws": "^8.5.12",
"@typescript-eslint/eslint-plugin": "^8.3.0",
"@typescript-eslint/parser": "^8.3.0",
"ajv": "^8.13.0",
"async-mutex": "^0.5.0",
"commander": "^12.1.0",
"cors": "^2.8.5",
"eslint": "^9.14.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"fast-xml-parser": "^4.3.6",
"file-type": "^19.0.0",
"globals": "^15.12.0",
"image-size": "^1.1.1",
"typescript": "^5.3.3",
"typescript-eslint": "^8.13.0",
"vite": "^6.0.1",
"vite-plugin-cp": "^4.0.8",
"vite-tsconfig-paths": "^5.1.0",
"winston": "^3.17.0",
"@sinclair/typebox": "^0.34.9"
"@rollup/plugin-node-resolve": "^16.0.3",
"@vitejs/plugin-react-swc": "^4.2.2",
"@vitest/ui": "^4.0.9",
"eslint": "^9.39.1",
"neostandard": "^0.12.2",
"typescript": "^5.3.0",
"vite": "^6.4.1",
"vite-plugin-cp": "^6.0.3",
"vitest": "^4.0.9"
},
"dependencies": {
"express": "^5.0.0",
"fluent-ffmpeg": "^2.1.2",
"piscina": "^4.7.0",
"qrcode-terminal": "^0.12.0",
"silk-wasm": "^3.6.1",
"ws": "^8.18.0"
"ws": "^8.18.3"
}
}
}

View File

@@ -0,0 +1,28 @@
{
"name": "napcat-common",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "src/index.ts",
"scripts": {
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {
".": {
"import": "./src/index.ts"
},
"./src/*": {
"import": "./src/*"
}
},
"dependencies": {
"ajv": "^8.13.0",
"file-type": "^21.0.0"
},
"devDependencies": {
"@types/node": "^22.0.1"
},
"engines": {
"node": ">=18.0.0"
}
}

View File

@@ -0,0 +1,79 @@
export type TaskExecutor<T> = (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void, onCancel: (callback: () => void) => void) => void | Promise<void>;
export class CancelableTask<T> {
private promise: Promise<T>;
private cancelCallback: (() => void) | null = null;
private isCanceled = false;
private cancelListeners: Array<() => void> = [];
constructor (executor: TaskExecutor<T>) {
this.promise = new Promise<T>((resolve, reject) => {
const onCancel = (callback: () => void) => {
this.cancelCallback = callback;
};
const execute = async () => {
try {
await executor(
(value) => {
if (!this.isCanceled) {
resolve(value);
}
},
(reason) => {
if (!this.isCanceled) {
reject(reason);
}
},
onCancel
);
} catch (error) {
if (!this.isCanceled) {
reject(error);
}
}
};
execute();
});
}
public cancel () {
if (this.cancelCallback) {
this.cancelCallback();
}
this.isCanceled = true;
this.cancelListeners.forEach(listener => listener());
}
public isTaskCanceled (): boolean {
return this.isCanceled;
}
public onCancel (listener: () => void) {
this.cancelListeners.push(listener);
}
public then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): Promise<TResult1 | TResult2> {
return this.promise.then(onfulfilled, onrejected);
}
public catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
): Promise<T | TResult> {
return this.promise.catch(onrejected);
}
public finally (onfinally?: (() => void) | undefined | null): Promise<T> {
return this.promise.finally(onfinally);
}
[Symbol.asyncIterator] () {
return {
next: () => this.promise.then(value => ({ value, done: true })),
};
}
}

View File

@@ -0,0 +1,229 @@
import fs from 'fs';
// generate Claude 3.7 Sonet Thinking
interface FileRecord {
filePath: string;
addedTime: number;
retries: number;
}
interface CleanupTask {
fileRecord: FileRecord;
timer: NodeJS.Timeout;
}
class CleanupQueue {
private tasks: Map<string, CleanupTask> = new Map();
private readonly MAX_RETRIES = 3;
private isProcessing: boolean = false;
private pendingOperations: Array<() => void> = [];
/**
* 执行队列中的待处理操作,确保异步安全
*/
private executeNextOperation (): void {
if (this.pendingOperations.length === 0) {
this.isProcessing = false;
return;
}
this.isProcessing = true;
const operation = this.pendingOperations.shift();
operation?.();
// 使用 setImmediate 允许事件循环继续,防止阻塞
setImmediate(() => this.executeNextOperation());
}
/**
* 安全执行操作,防止竞态条件
* @param operation 要执行的操作
*/
private safeExecute (operation: () => void): void {
this.pendingOperations.push(operation);
if (!this.isProcessing) {
this.executeNextOperation();
}
}
/**
* 检查文件是否存在
* @param filePath 文件路径
* @returns 文件是否存在
*/
private fileExists (filePath: string): boolean {
try {
return fs.existsSync(filePath);
} catch (_error) {
// console.log(`检查文件存在出错: ${filePath}`, error);
return false;
}
}
/**
* 添加文件到清理队列
* @param filePath 文件路径
* @param cleanupDelay 清理延迟时间(毫秒)
*/
addFile (filePath: string, cleanupDelay: number): void {
this.safeExecute(() => {
// 如果文件已在队列中,取消原来的计时器
if (this.tasks.has(filePath)) {
this.cancelCleanup(filePath);
}
// 创建新的文件记录
const fileRecord: FileRecord = {
filePath,
addedTime: Date.now(),
retries: 0,
};
// 设置计时器
const timer = setTimeout(() => {
this.cleanupFile(fileRecord, cleanupDelay);
}, cleanupDelay);
// 添加到任务队列
this.tasks.set(filePath, { fileRecord, timer });
});
}
/**
* 批量添加文件到清理队列
* @param filePaths 文件路径数组
* @param cleanupDelay 清理延迟时间(毫秒)
*/
addFiles (filePaths: string[], cleanupDelay: number): void {
this.safeExecute(() => {
for (const filePath of filePaths) {
// 内部直接处理,不通过 safeExecute 以保证批量操作的原子性
if (this.tasks.has(filePath)) {
// 取消已有的计时器,但不使用 cancelCleanup 方法以避免重复的安全检查
const existingTask = this.tasks.get(filePath);
if (existingTask) {
clearTimeout(existingTask.timer);
}
}
const fileRecord: FileRecord = {
filePath,
addedTime: Date.now(),
retries: 0,
};
const timer = setTimeout(() => {
this.cleanupFile(fileRecord, cleanupDelay);
}, cleanupDelay);
this.tasks.set(filePath, { fileRecord, timer });
}
});
}
/**
* 清理文件
* @param record 文件记录
* @param delay 延迟时间,用于重试
*/
private cleanupFile (record: FileRecord, delay: number): void {
this.safeExecute(() => {
// 首先检查文件是否存在,不存在则视为清理成功
if (!this.fileExists(record.filePath)) {
// console.log(`文件已不存在,跳过清理: ${record.filePath}`);
this.tasks.delete(record.filePath);
return;
}
try {
// 尝试删除文件
fs.unlinkSync(record.filePath);
// 删除成功,从队列中移除任务
this.tasks.delete(record.filePath);
} catch (error) {
const err = error as NodeJS.ErrnoException;
// 明确处理文件不存在的情况
if (err.code === 'ENOENT') {
// console.log(`文件在删除时不存在,视为清理成功: ${record.filePath}`);
this.tasks.delete(record.filePath);
return;
}
// 文件没有访问权限等情况
if (err.code === 'EACCES' || err.code === 'EPERM') {
// console.error(`没有权限删除文件: ${record.filePath}`, err);
}
// 其他删除失败情况,考虑重试
if (record.retries < this.MAX_RETRIES - 1) {
// 还有重试机会,增加重试次数
record.retries++;
// console.log(`清理文件失败,将重试(${record.retries}/${this.MAX_RETRIES}): ${record.filePath}`);
// 设置相同的延迟时间再次尝试
const timer = setTimeout(() => {
this.cleanupFile(record, delay);
}, delay);
// 更新任务
this.tasks.set(record.filePath, { fileRecord: record, timer });
} else {
// 已达到最大重试次数,从队列中移除任务
this.tasks.delete(record.filePath);
// console.error(`清理文件失败,已达最大重试次数(${this.MAX_RETRIES}): ${record.filePath}`, error);
}
}
});
}
/**
* 取消文件的清理任务
* @param filePath 文件路径
* @returns 是否成功取消
*/
cancelCleanup (filePath: string): boolean {
let cancelled = false;
this.safeExecute(() => {
const task = this.tasks.get(filePath);
if (task) {
clearTimeout(task.timer);
this.tasks.delete(filePath);
cancelled = true;
}
});
return cancelled;
}
/**
* 获取队列中的文件数量
* @returns 文件数量
*/
getQueueSize (): number {
return this.tasks.size;
}
/**
* 获取所有待清理的文件
* @returns 文件路径数组
*/
getPendingFiles (): string[] {
return Array.from(this.tasks.keys());
}
/**
* 清空所有清理任务
*/
clearAll (): void {
this.safeExecute(() => {
// 取消所有定时器
for (const task of this.tasks.values()) {
clearTimeout(task.timer);
}
this.tasks.clear();
// console.log('已清空所有清理任务');
});
}
}
export const cleanTaskQueue = new CleanupQueue();

9
packages/napcat-common/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
declare global {
interface ImportMetaEnv {
readonly VITE_NAPCAT_VERSION: string;
}
}
export {};

View File

@@ -0,0 +1,42 @@
type Handler<T> = () => T | Promise<T>;
type Checker<T> = (result: T) => T | Promise<T>;
export class Fallback<T> {
private handlers: Handler<T>[] = [];
private checker: Checker<T>;
constructor (checker?: Checker<T>) {
this.checker = checker || (async (result: T) => result);
}
add (handler: Handler<T>): this {
this.handlers.push(handler);
return this;
}
// 执行处理程序链
async run (): Promise<T> {
const errors: Error[] = [];
for (const handler of this.handlers) {
try {
const result = await handler();
const data = await this.checker(result);
if (data) {
return data;
}
} catch (error) {
errors.push(error instanceof Error ? error : new Error(String(error)));
}
}
throw new AggregateError(errors, 'All handlers failed');
}
}
export class FallbackUtil {
static boolchecker<T>(value: T, condition: boolean): T {
if (condition) {
return value;
} else {
throw new Error('Condition is false, throwing error');
}
}
}

View File

@@ -0,0 +1,121 @@
import { Peer } from './types';
import { randomUUID } from 'crypto';
class TimeBasedCache<K, V> {
private cache = new Map<K, { value: V, timestamp: number, frequency: number }>();
private keyList = new Set<K>();
private operationCount = 0;
constructor (private maxCapacity: number, private ttl: number = 30 * 1000 * 60, private cleanupCount: number = 10) {}
public put (key: K, value: V): void {
const timestamp = Date.now();
const cacheEntry = { value, timestamp, frequency: 1 };
this.cache.set(key, cacheEntry);
this.keyList.add(key);
this.operationCount++;
if (this.keyList.size > this.maxCapacity) this.evict();
if (this.operationCount >= this.cleanupCount) this.cleanup(this.cleanupCount);
}
public get (key: K): V | undefined {
const entry = this.cache.get(key);
if (entry && Date.now() - entry.timestamp < this.ttl) {
entry.timestamp = Date.now();
entry.frequency++;
this.operationCount++;
if (this.operationCount >= this.cleanupCount) this.cleanup(this.cleanupCount);
return entry.value;
} else {
this.deleteKey(key);
}
return undefined;
}
private cleanup (count: number): void {
const currentTime = Date.now();
let cleaned = 0;
for (const key of this.keyList) {
if (cleaned >= count) break;
const entry = this.cache.get(key);
if (entry && currentTime - entry.timestamp >= this.ttl) {
this.deleteKey(key);
cleaned++;
}
}
this.operationCount = 0; // 重置操作计数器
}
private deleteKey (key: K): void {
this.cache.delete(key);
this.keyList.delete(key);
}
private evict (): void {
while (this.keyList.size > this.maxCapacity) {
let oldestKey: K | undefined;
let minFrequency = Infinity;
for (const key of this.keyList) {
const entry = this.cache.get(key);
if (entry && entry.frequency < minFrequency) {
minFrequency = entry.frequency;
oldestKey = key;
}
}
if (oldestKey !== undefined) this.deleteKey(oldestKey);
}
}
}
interface FileUUIDData {
peer: Peer;
modelId?: string;
fileId?: string;
msgId?: string;
elementId?: string;
fileUUID?: string;
}
class FileUUIDManager {
private cache: TimeBasedCache<string, FileUUIDData>;
constructor (ttl: number) {
this.cache = new TimeBasedCache<string, FileUUIDData>(5000, ttl);
}
public encode (data: FileUUIDData, endString: string = '', customUUID?: string): string {
const uuid = customUUID || randomUUID().replace(/-/g, '') + endString;
this.cache.put(uuid, data);
return uuid;
}
public decode (uuid: string): FileUUIDData | undefined {
return this.cache.get(uuid);
}
}
export class FileNapCatOneBotUUIDWrap {
private manager: FileUUIDManager;
constructor (ttl: number = 86400000) {
this.manager = new FileUUIDManager(ttl);
}
public encodeModelId (peer: Peer, modelId: string, fileId: string, fileUUID: string = '', endString: string = '', customUUID?: string): string {
return this.manager.encode({ peer, modelId, fileId, fileUUID }, endString, customUUID);
}
public decodeModelId (uuid: string): FileUUIDData | undefined {
return this.manager.decode(uuid);
}
public encode (peer: Peer, msgId: string, elementId: string, fileUUID: string = '', customUUID?: string): string {
return this.manager.encode({ peer, msgId, elementId, fileUUID }, '', customUUID);
}
public decode (uuid: string): FileUUIDData | undefined {
return this.manager.decode(uuid);
}
}
export const FileNapCatOneBotUUID = new FileNapCatOneBotUUIDWrap();

View File

@@ -0,0 +1,209 @@
import fs from 'fs';
import { stat } from 'fs/promises';
import crypto, { randomUUID } from 'crypto';
import path from 'node:path';
import { solveProblem } from '@/napcat-common/src/helper';
export interface HttpDownloadOptions {
url: string;
headers?: Record<string, string> | string;
}
type Uri2LocalRes = {
success: boolean,
errMsg: string,
fileName: string,
path: string;
};
// 定义一个异步函数来检查文件是否存在
export function checkFileExist (path: string, timeout: number = 3000): Promise<void> {
return new Promise((resolve, reject) => {
const startTime = Date.now();
function check () {
if (fs.existsSync(path)) {
resolve();
} else if (Date.now() - startTime > timeout) {
reject(new Error(`文件不存在: ${path}`));
} else {
setTimeout(check, 100);
}
}
check();
});
}
// 定义一个异步函数来检查文件是否存在
export async function checkFileExistV2 (path: string, timeout: number = 3000): Promise<void> {
// 使用 Promise.race 来同时进行文件状态检查和超时计时
// Promise.race 会返回第一个解决resolve或拒绝reject的 Promise
await Promise.race([
checkFile(path),
timeoutPromise(timeout, `文件不存在: ${path}`),
]);
}
// 转换超时时间至 Promise
function timeoutPromise (timeout: number, errorMsg: string): Promise<void> {
return new Promise((_resolve, reject) => {
setTimeout(() => {
reject(new Error(errorMsg));
}, timeout);
});
}
// 异步检查文件是否存在
async function checkFile (path: string): Promise<void> {
try {
await stat(path);
} catch (error: unknown) {
if ((error as Error & { code: string; }).code === 'ENOENT') {
// 如果文件不存在,则抛出一个错误
throw new Error(`文件不存在: ${path}`);
} else {
// 对于 stat 调用的其他错误,重新抛出
throw error;
}
}
// 如果文件存在则无需做任何事情Promise 解决resolve自身
}
export function calculateFileMD5 (filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
// 创建一个流式读取器
const stream = fs.createReadStream(filePath);
const hash = crypto.createHash('md5');
stream.on('data', (data) => {
// 当读取到数据时,更新哈希对象的状态
hash.update(data);
});
stream.on('end', () => {
// 文件读取完成,计算哈希
const md5 = hash.digest('hex');
resolve(md5);
});
stream.on('error', (err: Error) => {
// 处理可能的读取错误
reject(err);
});
});
}
async function tryDownload (options: string | HttpDownloadOptions, useReferer: boolean = false): Promise<Response> {
let url: string;
let headers: Record<string, string> = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
};
if (typeof options === 'string') {
url = options;
headers['Host'] = new URL(url).hostname;
} else {
url = options.url;
if (options.headers) {
if (typeof options.headers === 'string') {
headers = JSON.parse(options.headers);
} else {
headers = options.headers;
}
}
}
if (useReferer && !headers['Referer']) {
headers['Referer'] = url;
}
const fetchRes = await fetch(url, { headers, redirect: 'follow' }).catch((err) => {
if (err.cause) {
throw err.cause;
}
throw err;
});
return fetchRes;
}
export async function httpDownload (options: string | HttpDownloadOptions): Promise<Buffer> {
const useReferer = typeof options === 'string';
let resp = await tryDownload(options);
if (resp.status === 403 && useReferer) {
resp = await tryDownload(options, true);
}
if (!resp.ok) throw new Error(`下载文件失败: ${resp.statusText}`);
const blob = await resp.blob();
const buffer = await blob.arrayBuffer();
return Buffer.from(buffer);
}
export enum FileUriType {
Unknown = 0,
Local = 1,
Remote = 2,
Base64 = 3,
}
export async function checkUriType (Uri: string) {
const LocalFileRet = await solveProblem((uri: string) => {
if (fs.existsSync(path.normalize(uri))) {
return { Uri: path.normalize(uri), Type: FileUriType.Local };
}
return undefined;
}, Uri);
if (LocalFileRet) return LocalFileRet;
const OtherFileRet = await solveProblem((uri: string) => {
// 再判断是否是Http
if (uri.startsWith('http:') || uri.startsWith('https:')) {
return { Uri: uri, Type: FileUriType.Remote };
}
// 再判断是否是Base64
if (uri.startsWith('base64:')) {
return { Uri: uri, Type: FileUriType.Base64 };
}
// 默认file://
if (uri.startsWith('file:')) {
const filePath: string = decodeURIComponent(uri.startsWith('file:///') && process.platform === 'win32' ? uri.slice(8) : uri.slice(7));
return { Uri: filePath, Type: FileUriType.Local };
}
if (uri.startsWith('data:')) {
const data = uri.split(',')[1];
if (data) return { Uri: data, Type: FileUriType.Base64 };
}
return undefined;
}, Uri);
if (OtherFileRet) return OtherFileRet;
return { Uri, Type: FileUriType.Unknown };
}
export async function uriToLocalFile (dir: string, uri: string, filename: string = randomUUID(), headers?: Record<string, string>): Promise<Uri2LocalRes> {
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
const filePath = path.join(dir, filename);
switch (UriType) {
case FileUriType.Local: {
const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
}
case FileUriType.Remote: {
const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} });
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Base64: {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
}
}

View File

@@ -0,0 +1,280 @@
export interface ResourceConfig<T extends any[], R> {
/** 资源获取函数 */
resourceFn: (...args: T) => Promise<R>;
/** 失败后禁用时间(毫秒),默认 30 秒 */
disableTime?: number;
/** 最大重试次数,默认 3 次 */
maxRetries?: number;
/** 主动测试间隔(毫秒),默认 60 秒 */
healthCheckInterval?: number;
/** 最大健康检查失败次数,超过后永久禁用,默认 5 次 */
maxHealthCheckFailures?: number;
/** 健康检查函数,如果提供则优先使用此函数进行健康检查 */
healthCheckFn?: (...args: T) => Promise<boolean>;
/** 测试参数(用于健康检查) */
testArgs?: T;
}
interface ResourceTypeState {
/** 资源配置 */
config: {
resourceFn: (...args: any[]) => Promise<any>;
healthCheckFn?: (...args: any[]) => Promise<boolean>;
disableTime: number;
maxRetries: number;
healthCheckInterval: number;
maxHealthCheckFailures: number;
testArgs?: any[];
};
/** 是否启用 */
isEnabled: boolean;
/** 禁用截止时间 */
disableUntil: number;
/** 当前重试次数 */
currentRetries: number;
/** 健康检查失败次数 */
healthCheckFailureCount: number;
/** 是否永久禁用 */
isPermanentlyDisabled: boolean;
/** 上次健康检查时间 */
lastHealthCheckTime: number;
/** 成功次数统计 */
successCount: number;
/** 失败次数统计 */
failureCount: number;
}
export class ResourceManager {
private resourceTypes = new Map<string, ResourceTypeState>();
private destroyed = false;
/**
* 调用资源(自动注册或复用已有配置)
*/
async callResource<T extends any[], R>(
type: string,
config: ResourceConfig<T, R>,
...args: T
): Promise<R> {
if (this.destroyed) {
throw new Error('ResourceManager has been destroyed');
}
// 获取或创建资源类型状态
let state = this.resourceTypes.get(type);
if (!state) {
// 首次注册
state = {
config: {
resourceFn: config.resourceFn as (...args: any[]) => Promise<any>,
healthCheckFn: config.healthCheckFn as ((...args: any[]) => Promise<boolean>) | undefined,
disableTime: config.disableTime ?? 30000,
maxRetries: config.maxRetries ?? 3,
healthCheckInterval: config.healthCheckInterval ?? 60000,
maxHealthCheckFailures: config.maxHealthCheckFailures ?? 20,
testArgs: config.testArgs as any[] | undefined,
},
isEnabled: true,
disableUntil: 0,
currentRetries: 0,
healthCheckFailureCount: 0,
isPermanentlyDisabled: false,
lastHealthCheckTime: 0,
successCount: 0,
failureCount: 0,
};
this.resourceTypes.set(type, state);
}
// 在调用前检查是否需要进行健康检查
await this.checkAndPerformHealthCheck(state);
// 检查资源状态
if (state.isPermanentlyDisabled) {
throw new Error(`Resource type '${type}' is permanently disabled (success: ${state.successCount}, failure: ${state.failureCount})`);
}
if (!this.isResourceAvailable(type)) {
const disableUntilDate = new Date(state.disableUntil).toISOString();
throw new Error(`Resource type '${type}' is currently disabled until ${disableUntilDate} (success: ${state.successCount}, failure: ${state.failureCount})`);
}
// 调用资源
try {
const result = await config.resourceFn(...args);
this.onResourceSuccess(state);
return result;
} catch (error) {
this.onResourceFailure(state);
throw error;
}
}
/**
* 检查资源类型是否可用
*/
isResourceAvailable (type: string): boolean {
const state = this.resourceTypes.get(type);
if (!state) {
return true; // 未注册的资源类型视为可用
}
if (state.isPermanentlyDisabled || !state.isEnabled) {
return false;
}
return Date.now() >= state.disableUntil;
}
/**
* 获取资源类型统计信息
*/
getResourceStats (type: string): { successCount: number; failureCount: number; isEnabled: boolean; isPermanentlyDisabled: boolean } | null {
const state = this.resourceTypes.get(type);
if (!state) {
return null;
}
return {
successCount: state.successCount,
failureCount: state.failureCount,
isEnabled: state.isEnabled,
isPermanentlyDisabled: state.isPermanentlyDisabled,
};
}
/**
* 获取所有资源类型统计
*/
getAllResourceStats (): Map<string, { successCount: number; failureCount: number; isEnabled: boolean; isPermanentlyDisabled: boolean }> {
const stats = new Map();
for (const [type, state] of this.resourceTypes) {
stats.set(type, {
successCount: state.successCount,
failureCount: state.failureCount,
isEnabled: state.isEnabled,
isPermanentlyDisabled: state.isPermanentlyDisabled,
});
}
return stats;
}
/**
* 注销资源类型
*/
unregister (type: string): boolean {
return this.resourceTypes.delete(type);
}
/**
* 销毁管理器
*/
destroy (): void {
if (this.destroyed) {
return;
}
this.resourceTypes.clear();
this.destroyed = true;
}
/**
* 检查并执行健康检查(如果需要)
*/
private async checkAndPerformHealthCheck (state: ResourceTypeState): Promise<void> {
// 如果资源可用或已永久禁用,无需健康检查
if (state.isEnabled && Date.now() >= state.disableUntil) {
return;
}
if (state.isPermanentlyDisabled) {
return;
}
const now = Date.now();
// 检查是否还在禁用期内
if (now < state.disableUntil) {
return;
}
// 检查是否需要进行健康检查(根据间隔时间)
if (now - state.lastHealthCheckTime < state.config.healthCheckInterval) {
return;
}
// 执行健康检查
await this.performHealthCheck(state);
}
private async performHealthCheck (state: ResourceTypeState): Promise<void> {
state.lastHealthCheckTime = Date.now();
try {
let healthCheckResult: boolean;
if (state.config.healthCheckFn) {
const testArgs = state.config.testArgs || [];
healthCheckResult = await state.config.healthCheckFn(...testArgs);
} else {
const testArgs = state.config.testArgs || [];
await state.config.resourceFn(...testArgs);
healthCheckResult = true;
}
if (healthCheckResult) {
// 健康检查成功,重新启用
state.isEnabled = true;
state.disableUntil = 0;
state.currentRetries = 0;
state.healthCheckFailureCount = 0;
} else {
throw new Error('Health check function returned false');
}
} catch {
// 健康检查失败,增加失败计数
state.healthCheckFailureCount++;
// 检查是否达到最大健康检查失败次数
if (state.healthCheckFailureCount >= state.config.maxHealthCheckFailures) {
// 永久禁用资源
state.isPermanentlyDisabled = true;
state.disableUntil = 0;
} else {
// 继续禁用一段时间
state.disableUntil = Date.now() + state.config.disableTime;
}
}
}
private onResourceSuccess (state: ResourceTypeState): void {
state.currentRetries = 0;
state.disableUntil = 0;
state.healthCheckFailureCount = 0;
state.successCount++;
}
private onResourceFailure (state: ResourceTypeState): void {
state.currentRetries++;
state.failureCount++;
// 如果重试次数达到上限,禁用资源
if (state.currentRetries >= state.config.maxRetries) {
state.disableUntil = Date.now() + state.config.disableTime;
state.currentRetries = 0;
}
}
}
// 创建全局实例
export const resourceManager = new ResourceManager();
// 便捷函数
export async function registerResource<T extends any[], R> (
type: string,
config: ResourceConfig<T, R>,
...args: T
): Promise<R> {
return resourceManager.callResource(type, config, ...args);
}

View File

@@ -0,0 +1,240 @@
import path from 'node:path';
import fs from 'fs';
import os from 'node:os';
import { QQVersionConfigType, QQLevel } from './types';
import { compareSemVer } from './version';
import { getAllGitHubTags as getAllTagsFromMirror } from './mirror';
// 导出 compareSemVer 供其他模块使用
export { compareSemVer } from './version';
export async function solveProblem<T extends (...arg: any[]) => any> (func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> {
return new Promise<ReturnType<T> | undefined>((resolve) => {
try {
const result = func(...args);
resolve(result);
} catch {
resolve(undefined);
}
});
}
export async function solveAsyncProblem<T extends (...args: any[]) => Promise<any>> (func: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>> | undefined> {
return new Promise<Awaited<ReturnType<T>> | undefined>((resolve) => {
func(...args).then((result) => {
resolve(result);
}).catch(() => {
resolve(undefined);
});
});
}
export function sleep (ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function PromiseTimer<T> (promise: Promise<T>, ms: number): Promise<T> {
const timeoutPromise = new Promise<T>((_resolve, reject) =>
setTimeout(() => reject(new Error('PromiseTimer: Operation timed out')), ms)
);
return Promise.race([promise, timeoutPromise]);
}
export async function runAllWithTimeout<T> (tasks: Promise<T>[], timeout: number): Promise<T[]> {
const wrappedTasks = tasks.map((task) =>
PromiseTimer(task, timeout).then(
(result) => ({ status: 'fulfilled', value: result }),
(error) => ({ status: 'rejected', reason: error })
)
);
const results = await Promise.all(wrappedTasks);
return results
.filter((result) => result.status === 'fulfilled')
.map((result) => (result as { status: 'fulfilled'; value: T; }).value);
}
export function isNull (value: any) {
return value === undefined || value === null;
}
export function isNumeric (str: string) {
return /^\d+$/.test(str);
}
export function truncateString (obj: any, maxLength = 500) {
if (obj !== null && typeof obj === 'object') {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'string') {
// 如果是字符串且超过指定长度,则截断
if (obj[key].length > maxLength) {
obj[key] = obj[key].substring(0, maxLength) + '...';
}
} else if (typeof obj[key] === 'object') {
// 如果是对象或数组,则递归调用
truncateString(obj[key], maxLength);
}
});
}
return obj;
}
export function isEqual (obj1: any, obj2: any) {
if (obj1 === obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return obj1 === obj2;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!isEqual(obj1[key], obj2[key])) return false;
}
return true;
}
export function getDefaultQQVersionConfigInfo (): QQVersionConfigType {
if (os.platform() === 'linux') {
return {
baseVersion: '3.2.12.28060',
curVersion: '3.2.12.28060',
prevVersion: '',
onErrorVersions: [],
buildId: '27254',
};
}
if (os.platform() === 'darwin') {
return {
baseVersion: '6.9.53.28060',
curVersion: '6.9.53.28060',
prevVersion: '',
onErrorVersions: [],
buildId: '28060',
};
}
return {
baseVersion: '9.9.15-28131',
curVersion: '9.9.15-28131',
prevVersion: '',
onErrorVersions: [],
buildId: '28131',
};
}
export function getQQPackageInfoPath (exePath: string = '', version?: string): string {
if (process.env['NAPCAT_QQ_PACKAGE_INFO_PATH']) {
return process.env['NAPCAT_QQ_PACKAGE_INFO_PATH'];
}
let packagePath;
if (os.platform() === 'darwin') {
packagePath = path.join(path.dirname(exePath), '..', 'Resources', 'app', 'package.json');
} else if (os.platform() === 'linux') {
packagePath = path.join(path.dirname(exePath), './resources/app/package.json');
} else {
packagePath = path.join(path.dirname(exePath), './versions/' + version + '/resources/app/package.json');
}
// 下面是老版本兼容 未来去掉
if (!fs.existsSync(packagePath)) {
packagePath = path.join(path.dirname(exePath), './resources/app/versions/' + version + '/package.json');
}
return packagePath;
}
export function getQQVersionConfigPath (exePath: string = ''): string | undefined {
if (process.env['NAPCAT_QQ_VERSION_CONFIG_PATH']) {
return process.env['NAPCAT_QQ_VERSION_CONFIG_PATH'];
}
let configVersionInfoPath;
if (os.platform() === 'win32') {
configVersionInfoPath = path.join(path.dirname(exePath), 'versions', 'config.json');
} else if (os.platform() === 'darwin') {
const userPath = os.homedir();
const appDataPath = path.resolve(userPath, './Library/Application Support/QQ');
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
} else {
const userPath = os.homedir();
const appDataPath = path.resolve(userPath, './.config/QQ');
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
}
if (typeof configVersionInfoPath !== 'string') {
return undefined;
}
// 老版本兼容 未来去掉
if (!fs.existsSync(configVersionInfoPath)) {
configVersionInfoPath = path.join(path.dirname(exePath), './resources/app/versions/config.json');
}
if (!fs.existsSync(configVersionInfoPath)) {
return undefined;
}
return configVersionInfoPath;
}
export function calcQQLevel (level?: QQLevel) {
if (!level) return 0;
// const { penguinNum, crownNum, sunNum, moonNum, starNum } = level;
const { crownNum, sunNum, moonNum, starNum } = level;
// 没补类型
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
}
export function stringifyWithBigInt (obj: any) {
return JSON.stringify(obj, (_key, value) =>
typeof value === 'bigint' ? value.toString() : value
);
}
export function parseAppidFromMajor (nodeMajor: string): string | undefined {
const hexSequence = 'A4 09 00 00 00 35';
const sequenceBytes = Buffer.from(hexSequence.replace(/ /g, ''), 'hex');
const filePath = path.resolve(nodeMajor);
const fileContent = fs.readFileSync(filePath);
let searchPosition = 0;
while (true) {
const index = fileContent.indexOf(sequenceBytes, searchPosition);
if (index === -1) {
break;
}
const start = index + sequenceBytes.length - 1;
const end = fileContent.indexOf(0x00, start);
if (end === -1) {
break;
}
const content = fileContent.subarray(start, end);
if (!content.every(byte => byte === 0x00)) {
try {
return content.toString('utf-8');
} catch {
break;
}
}
searchPosition = end + 1;
}
return undefined;
}
// ============== GitHub Tags 获取 ==============
// 使用 mirror 模块统一管理镜像
export async function getAllTags (): Promise<{ tags: string[], mirror: string; }> {
return getAllTagsFromMirror('NapNeko', 'NapCatQQ');
}
export async function getLatestTag (): Promise<string> {
const { tags } = await getAllTags();
// 使用 SemVer 规范排序
tags.sort((a, b) => compareSemVer(a, b));
const latest = tags.at(-1);
if (!latest) {
throw new Error('No tags found');
}
// 去掉开头的 v
return latest.replace(/^v/, '');
}

View File

@@ -0,0 +1,24 @@
export enum LogLevel {
DEBUG = 'debug',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
FATAL = 'fatal',
}
export interface ILogWrapper {
fileLogEnabled: boolean;
consoleLogEnabled: boolean;
cleanOldLogs (logDir: string): void;
setFileAndConsoleLogLevel (fileLogLevel: LogLevel, consoleLogLevel: LogLevel): void;
setLogSelfInfo (selfInfo: { nick: string; uid: string; }): void;
setFileLogEnabled (isEnabled: boolean): void;
setConsoleLogEnabled (isEnabled: boolean): void;
formatMsg (msg: any[]): string;
_log (level: LogLevel, ...args: any[]): void;
log (...args: any[]): void;
logDebug (...args: any[]): void;
logError (...args: any[]): void;
logWarn (...args: any[]): void;
logFatal (...args: any[]): void;
logMessage (msg: unknown, selfInfo: unknown): void;
}

View File

@@ -0,0 +1,43 @@
export class LRUCache<K, V> {
private capacity: number;
public cache: Map<K, V>;
constructor (capacity: number) {
this.capacity = capacity;
this.cache = new Map<K, V>();
}
public get (key: K): V | undefined {
const value = this.cache.get(key);
if (value !== undefined) {
// Move the accessed key to the end to mark it as most recently used
this.cache.delete(key);
this.cache.set(key, value);
}
return value;
}
public put (key: K, value: V): void {
if (this.cache.has(key)) {
// If the key already exists, move it to the end to mark it as most recently used
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// If the cache is full, remove the least recently used key (the first one in the map)
const firstKey = this.cache.keys().next().value;
if (firstKey !== undefined) {
this.cache.delete(firstKey);
}
}
this.cache.set(key, value);
}
public resetCapacity (newCapacity: number): void {
this.capacity = newCapacity;
while (this.cache.size > this.capacity) {
const firstKey = this.cache.keys().next().value;
if (firstKey !== undefined) {
this.cache.delete(firstKey);
}
}
}
}

View File

@@ -0,0 +1,141 @@
import crypto from 'crypto';
import { Peer } from './types';
export class LimitedHashTable<K, V> {
private readonly keyToValue: Map<K, V> = new Map();
private readonly valueToKey: Map<V, K> = new Map();
private maxSize: number;
constructor (maxSize: number) {
this.maxSize = maxSize;
}
resize (count: number) {
this.maxSize = count;
}
set (key: K, value: V): void {
this.keyToValue.set(key, value);
this.valueToKey.set(value, key);
while (this.keyToValue.size !== this.valueToKey.size) {
this.keyToValue.clear();
this.valueToKey.clear();
}
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
const oldestKey = this.keyToValue.keys().next().value;
if (oldestKey !== undefined) {
this.valueToKey.delete(this.keyToValue.get(oldestKey) as V);
this.keyToValue.delete(oldestKey);
}
}
}
getValue (key: K): V | undefined {
return this.keyToValue.get(key);
}
getKey (value: V): K | undefined {
return this.valueToKey.get(value);
}
deleteByValue (value: V): void {
const key = this.valueToKey.get(value);
if (key !== undefined) {
this.keyToValue.delete(key);
this.valueToKey.delete(value);
}
}
deleteByKey (key: K): void {
const value = this.keyToValue.get(key);
if (value !== undefined) {
this.keyToValue.delete(key);
this.valueToKey.delete(value);
}
}
getKeyList (): K[] {
return Array.from(this.keyToValue.keys());
}
// 获取最近刚写入的几个值
getHeads (size: number): { key: K; value: V }[] | undefined {
const keyList = this.getKeyList();
if (keyList.length === 0) {
return undefined;
}
const result: { key: K; value: V }[] = [];
const listSize = Math.min(size, keyList.length);
for (let i = 0; i < listSize; i++) {
const key = keyList[listSize - i];
if (key !== undefined) {
result.push({ key, value: this.keyToValue.get(key)! });
}
}
return result;
}
}
class MessageUniqueWrapper {
private readonly msgDataMap: LimitedHashTable<string, number>;
private readonly msgIdMap: LimitedHashTable<string, number>;
constructor (maxMap: number = 5000) {
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
this.msgDataMap = new LimitedHashTable<string, number>(maxMap);
}
getRecentMsgIds (Peer: Peer, size: number): string[] {
const heads = this.msgIdMap.getHeads(size);
if (!heads) {
return [];
}
const data = heads.map((t) => MessageUnique.getMsgIdAndPeerByShortId(t.value));
const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid);
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
}
createUniqueMsgId (peer: Peer, msgId: string) {
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
const hash = crypto.createHash('md5').update(key).digest();
if (hash[0]) {
// 设置第一个bit为0 保证shortId为正数
hash[0] &= 0x7f;
}
const shortId = hash.readInt32BE(0);
// 减少性能损耗
this.msgIdMap.set(msgId, shortId);
this.msgDataMap.set(key, shortId);
return shortId;
}
getMsgIdAndPeerByShortId (shortId: number): { MsgId: string; Peer: Peer } | undefined {
const data = this.msgDataMap.getKey(shortId);
if (data) {
const [msgId, chatTypeStr, peerUid] = data.split('|');
const peer: Peer = {
chatType: parseInt(chatTypeStr ?? '0'),
peerUid: peerUid ?? '',
guildId: '',
};
return { MsgId: msgId ?? '0', Peer: peer };
}
return undefined;
}
getShortIdByMsgId (msgId: string): number | undefined {
return this.msgIdMap.getValue(msgId);
}
getPeerByMsgId (msgId: string) {
const shortId = this.msgIdMap.getValue(msgId);
if (!shortId) return undefined;
return this.getMsgIdAndPeerByShortId(shortId);
}
resize (maxSize: number): void {
this.msgIdMap.resize(maxSize);
this.msgDataMap.resize(maxSize);
}
}
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import os from 'os';
export class NapCatPathWrapper {
binaryPath: string;
logsPath: string;
configPath: string;
cachePath: string;
staticPath: string;
pluginPath: string;
constructor (mainPath: string = dirname(fileURLToPath(import.meta.url))) {
this.binaryPath = mainPath;
let writePath: string;
if (process.env['NAPCAT_WORKDIR']) {
writePath = process.env['NAPCAT_WORKDIR'];
} else if (os.platform() === 'darwin') {
writePath = path.join(os.homedir(), 'Library', 'Application Support', 'QQ', 'NapCat');
} else {
writePath = this.binaryPath;
}
this.logsPath = path.join(writePath, 'logs');
this.configPath = path.join(writePath, 'config');
this.pluginPath = path.join(writePath, 'plugins');// dynamic load
this.cachePath = path.join(writePath, 'cache');
this.staticPath = path.join(this.binaryPath, 'static');
if (!fs.existsSync(this.logsPath)) {
fs.mkdirSync(this.logsPath, { recursive: true });
}
if (!fs.existsSync(this.configPath)) {
fs.mkdirSync(this.configPath, { recursive: true });
}
if (!fs.existsSync(this.cachePath)) {
fs.mkdirSync(this.cachePath, { recursive: true });
}
}
}

View File

@@ -0,0 +1,43 @@
// 协议管理器 - 用于统一管理多协议适配
export interface ProtocolInfo {
id: string;
name: string;
description: string;
version: string;
enabled: boolean;
}
export interface ProtocolConfig {
protocols: {
[key: string]: {
enabled: boolean;
config: unknown;
};
};
}
export const SUPPORTED_PROTOCOLS: ProtocolInfo[] = [
{
id: 'onebot11',
name: 'OneBot 11',
description: 'OneBot 11 协议适配器,兼容 go-cqhttp',
version: '11.0.0',
enabled: true,
},
{
id: 'satori',
name: 'Satori',
description: 'Satori 协议适配器,跨平台机器人协议',
version: '1.0.0',
enabled: false,
},
];
export function getProtocolInfo (protocolId: string): ProtocolInfo | undefined {
return SUPPORTED_PROTOCOLS.find((p) => p.id === protocolId);
}
export function getSupportedProtocols (): ProtocolInfo[] {
return SUPPORTED_PROTOCOLS;
}

View File

@@ -0,0 +1,130 @@
import https from 'node:https';
import http from 'node:http';
export class RequestUtil {
// 适用于获取服务器下发cookies时获取仅GET
static async HttpsGetCookies (url: string): Promise<{ [key: string]: string; }> {
const client = url.startsWith('https') ? https : http;
return new Promise((resolve, reject) => {
const req = client.get(url, (res) => {
const cookies: { [key: string]: string; } = {};
res.on('data', () => { }); // Necessary to consume the stream
res.on('end', () => {
this.handleRedirect(res, url, cookies)
.then(resolve)
.catch(reject);
});
if (res.headers['set-cookie']) {
this.extractCookies(res.headers['set-cookie'], cookies);
}
});
req.on('error', (error: Error) => {
reject(error);
});
});
}
private static async handleRedirect (res: http.IncomingMessage, url: string, cookies: { [key: string]: string; }): Promise<{ [key: string]: string; }> {
if (res.statusCode === 301 || res.statusCode === 302) {
if (res.headers.location) {
const redirectUrl = new URL(res.headers.location, url);
const redirectCookies = await this.HttpsGetCookies(redirectUrl.href);
// 合并重定向过程中的cookies
return { ...cookies, ...redirectCookies };
}
}
return cookies;
}
private static extractCookies (setCookieHeaders: string[], cookies: { [key: string]: string; }) {
setCookieHeaders.forEach((cookie) => {
const parts = cookie.split(';')[0]?.split('=');
if (parts) {
const key = parts[0];
const value = parts[1];
if (key && value && key.length > 0 && value.length > 0) {
cookies[key] = value;
}
}
});
}
// 请求和回复都是JSON data传原始内容 自动编码json
// 支持 301/302 重定向(最多 5 次)
static async HttpGetJson<T> (url: string, method: string = 'GET', data?: any, headers: {
[key: string]: string;
} = {}, isJsonRet: boolean = true, isArgJson: boolean = true, maxRedirects: number = 5): Promise<T> {
const option = new URL(url);
const protocol = url.startsWith('https://') ? https : http;
const options = {
hostname: option.hostname,
port: option.port,
path: option.pathname + option.search,
method,
headers,
};
// headers: {
// 'Content-Type': 'application/json',
// 'Content-Length': Buffer.byteLength(postData),
// },
return new Promise((resolve, reject) => {
const req = protocol.request(options, (res: http.IncomingMessage) => {
// 处理重定向
if ((res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) && res.headers.location) {
if (maxRedirects <= 0) {
reject(new Error('Too many redirects'));
return;
}
const redirectUrl = new URL(res.headers.location, url).href;
// 递归跟随重定向
this.HttpGetJson<T>(redirectUrl, method, data, headers, isJsonRet, isArgJson, maxRedirects - 1)
.then(resolve)
.catch(reject);
return;
}
let responseBody = '';
res.on('data', (chunk: string | Buffer) => {
responseBody += chunk.toString();
});
res.on('end', () => {
try {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
if (isJsonRet) {
const responseJson = JSON.parse(responseBody);
resolve(responseJson as T);
} else {
resolve(responseBody as T);
}
} else {
reject(new Error(`Unexpected status code: ${res.statusCode}`));
}
} catch (parseError: unknown) {
reject(new Error((parseError as Error).message));
}
});
});
req.on('error', (error: Error) => {
reject(error);
});
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
if (isArgJson) {
req.write(JSON.stringify(data));
} else {
req.write(data);
}
}
req.end();
});
}
// 请求返回都是原始内容
static async HttpGetText (url: string, method: string = 'GET', data?: any, headers: { [key: string]: string; } = {}) {
return this.HttpGetJson<string>(url, method, data, headers, false, false);
}
}

View File

@@ -0,0 +1,24 @@
export interface SystemStatus {
cpu: {
model: string,
speed: string;
usage: {
system: string;
qq: string;
},
core: number;
},
memory: {
total: string;
usage: {
system: string;
qq: string;
};
},
arch: string;
}
export interface IStatusHelperSubscription {
on (event: 'statusUpdate', listener: (status: SystemStatus) => void): this;
off (event: 'statusUpdate', listener: (status: SystemStatus) => void): this;
emit (event: 'statusUpdate', status: SystemStatus): boolean;
}

View File

@@ -0,0 +1,22 @@
class Store {
private store = new Map<string, any>();
set<T> (key: string, value: T, ttl?: number): void {
this.store.set(key, value);
if (ttl) {
setTimeout(() => this.store.delete(key), ttl * 1000);
}
}
get<T> (key: string): T | null {
return this.store.get(key) ?? null;
}
exists (...keys: string[]): number {
return keys.filter(key => this.store.has(key)).length;
}
}
const store = new Store();
export default store;

View File

@@ -0,0 +1,6 @@
export type LogListener = (msg: string) => void;
export interface ISubscription {
subscribe (listener: LogListener): void;
unsubscribe (listener: LogListener): void;
notify (msg: string): void;
}

View File

@@ -5,12 +5,11 @@ import path from 'node:path';
let osName: string;
try {
osName = os.hostname();
} catch (e) {
osName = 'NapCat'; // + crypto.randomUUID().substring(0, 4);
osName = os.hostname();
} catch {
osName = 'NapCat'; // + crypto.randomUUID().substring(0, 4);
}
const homeDir = os.homedir();
export const systemPlatform = os.platform();

View File

@@ -0,0 +1,28 @@
// QQVersionType
export type QQPackageInfoType = {
version: string;
buildVersion: string;
platform: string;
eleArch: string;
};
export type QQVersionConfigType = {
baseVersion: string;
curVersion: string;
prevVersion: string;
onErrorVersions: Array<unknown>;
buildId: string;
};
export type QQAppidTableType = {
[key: string]: { appid: string, qua: string };
};
export interface Peer {
chatType: number; // 聊天类型
peerUid: string; // 对等方的唯一标识符
guildId?: string; // 可选的频道ID
}
export interface QQLevel {
crownNum: number;
sunNum: number;
moonNum: number;
starNum: number;
}

View File

@@ -0,0 +1,118 @@
// @ts-ignore
export const napCatVersion = (typeof import.meta?.env !== 'undefined' && import.meta.env.VITE_NAPCAT_VERSION) || '1.0.0-dev';
/**
* SemVer 2.0 正则表达式
* 格式: 主版本号.次版本号.修订号[-先行版本号][+版本编译信息]
* 参考: https://semver.org/lang/zh-CN/
*/
const SEMVER_REGEX = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
export interface SemVerInfo {
valid: boolean;
normalized: string;
major: number;
minor: number;
patch: number;
prerelease: string | null;
buildmetadata: string | null;
}
/**
* 解析并验证版本号是否符合 SemVer 2.0 规范
* @param version - 版本字符串 (支持 v 前缀)
* @returns SemVer 解析结果
*/
export function parseSemVer (version: string | undefined | null): SemVerInfo {
if (!version || typeof version !== 'string') {
return { valid: false, normalized: '1.0.0-dev', major: 1, minor: 0, patch: 0, prerelease: 'dev', buildmetadata: null };
}
const match = version.trim().match(SEMVER_REGEX);
if (match) {
const major = parseInt(match[1]!, 10);
const minor = parseInt(match[2]!, 10);
const patch = parseInt(match[3]!, 10);
const prerelease = match[4] || null;
const buildmetadata = match[5] || null;
// 构建标准化版本号(不带 v 前缀)
let normalized = `${major}.${minor}.${patch}`;
if (prerelease) normalized += `-${prerelease}`;
if (buildmetadata) normalized += `+${buildmetadata}`;
return { valid: true, normalized, major, minor, patch, prerelease, buildmetadata };
}
return { valid: false, normalized: '1.0.0-dev', major: 1, minor: 0, patch: 0, prerelease: 'dev', buildmetadata: null };
}
/**
* 验证版本号是否符合 SemVer 2.0 规范
* @param version - 版本字符串
* @returns 是否有效
*/
export function isValidSemVer (version: string | undefined | null): boolean {
return parseSemVer(version).valid;
}
/**
* 比较两个 SemVer 版本号
* @param v1 - 版本号1
* @param v2 - 版本号2
* @returns -1 (v1 < v2), 0 (v1 == v2), 1 (v1 > v2)
*/
export function compareSemVer (v1: string, v2: string): -1 | 0 | 1 {
const a = parseSemVer(v1);
const b = parseSemVer(v2);
if (!a.valid || !b.valid) {
return 0;
}
// 比较主版本号
if (a.major !== b.major) return a.major > b.major ? 1 : -1;
// 比较次版本号
if (a.minor !== b.minor) return a.minor > b.minor ? 1 : -1;
// 比较修订号
if (a.patch !== b.patch) return a.patch > b.patch ? 1 : -1;
// 有先行版本号的版本优先级较低
if (a.prerelease && !b.prerelease) return -1;
if (!a.prerelease && b.prerelease) return 1;
// 两者都有先行版本号时,按字典序比较
if (a.prerelease && b.prerelease) {
const aParts = a.prerelease.split('.');
const bParts = b.prerelease.split('.');
const len = Math.max(aParts.length, bParts.length);
for (let i = 0; i < len; i++) {
const aPart = aParts[i];
const bPart = bParts[i];
if (aPart === undefined) return -1;
if (bPart === undefined) return 1;
const aNum = /^\d+$/.test(aPart) ? parseInt(aPart, 10) : NaN;
const bNum = /^\d+$/.test(bPart) ? parseInt(bPart, 10) : NaN;
// 数字 vs 数字
if (!isNaN(aNum) && !isNaN(bNum)) {
if (aNum !== bNum) return aNum > bNum ? 1 : -1;
continue;
}
// 数字优先级低于字符串
if (!isNaN(aNum)) return -1;
if (!isNaN(bNum)) return 1;
// 字符串 vs 字符串
if (aPart !== bPart) return aPart > bPart ? 1 : -1;
}
}
return 0;
}
/**
* 获取解析后的当前版本信息
*/
export const napCatVersionInfo = parseSemVer(napCatVersion);

View File

@@ -0,0 +1,34 @@
import { Worker } from 'worker_threads';
export async function runTask<T, R> (workerScript: string, taskData: T): Promise<R> {
const worker = new Worker(workerScript);
try {
return await new Promise<R>((resolve, reject) => {
worker.on('message', (result: R) => {
if ((result as any)?.log) {
console.error('Worker Log--->:', (result as { log: string }).log);
}
if ((result as any)?.error) {
reject(new Error('Worker error: ' + (result as { error: string }).error));
}
resolve(result);
});
worker.on('error', (error) => {
reject(new Error(`Worker error: ${error.message}`));
});
worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${code}`));
}
});
worker.postMessage(taskData);
});
} catch (error: unknown) {
throw new Error(`Failed to run task: ${(error as Error).message}`);
} finally {
// Ensure the worker is terminated after the promise is settled
worker.terminate();
}
}

View File

@@ -0,0 +1,53 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/*": [
"../*"
]
},
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"dist"
]
}

View File

@@ -0,0 +1,27 @@
import { MsfChangeReasonType, MsfStatusType } from '@/napcat-core/types/adapter';
export class NodeIDependsAdapter {
onMSFStatusChange (_statusType: MsfStatusType, _changeReasonType: MsfChangeReasonType) {
}
onMSFSsoError (_code: number, _desc: string) {
}
getGroupCode (_args: unknown) {
}
// onSendMsfReply (_seq: string, _cmd: string, _uk1: number, _uk2: string, _rsp: {
// ssoRetCode: 0,
// trpcRetCode: 0,
// trpcFuncCode: 0,
// errorMsg: '',
// pbBuffer: Uint8Array,
// transInfoMap: Map<unknown, unknown>;
// }) {
// console.log('[NodeIDependsAdapter] onSendMsfReply', _seq, _cmd, _uk1, _uk2, Buffer.from(_rsp.pbBuffer).toString('hex'));
// }
}

View File

@@ -0,0 +1,10 @@
export class NodeIDispatcherAdapter {
dispatchRequest (_arg: unknown) {
}
dispatchCall (_arg: unknown) {
}
dispatchCallWithJson (_arg: unknown) {
}
}

View File

@@ -0,0 +1,25 @@
export class NodeIGlobalAdapter {
onLog (..._args: unknown[]) {
}
onGetSrvCalTime (..._args: unknown[]) {
}
onShowErrUITips (..._args: unknown[]) {
}
fixPicImgType (..._args: unknown[]) {
}
getAppSetting (..._args: unknown[]) {
}
onInstallFinished (..._args: unknown[]) {
}
onUpdateGeneralFlag (..._args: unknown[]) {
}
onGetOfflineMsg (..._args: unknown[]) {
}
}

View File

@@ -0,0 +1,60 @@
import { InstanceContext, NapCatCore } from '@/napcat-core/index';
export class NTQQCollectionApi {
context: InstanceContext;
core: NapCatCore;
constructor (context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
}
async createCollection (authorUin: string, authorUid: string, authorName: string, brief: string, rawData: string) {
return this.context.session.getCollectionService().createNewCollectionItem({
commInfo: {
bid: 1,
category: 2,
author: {
type: 1,
numId: authorUin,
strId: authorName,
groupId: '0',
groupName: '',
uid: authorUid,
},
customGroupId: '0',
createTime: Date.now().toString(),
sequence: Date.now().toString(),
},
richMediaSummary: {
originalUri: '',
publisher: '',
richMediaVersion: 0,
subTitle: '',
title: '',
brief,
picList: [],
contentType: 1,
},
richMediaContent: {
rawData,
bizDataList: [],
picList: [],
fileList: [],
},
need_share_url: false,
});
}
async getAllCollection (category: number = 0, count: number = 50) {
return this.context.session.getCollectionService().getCollectionItemList({
category,
groupId: -1,
forceSync: true,
forceFromDb: false,
timeStamp: '0',
count,
searchDown: true,
});
}
}

View File

@@ -0,0 +1,390 @@
import {
ChatType,
ElementType,
IMAGE_HTTP_HOST,
IMAGE_HTTP_HOST_NT,
Peer,
PicElement,
RawMessage,
} from '@/napcat-core/types';
import path from 'path';
import fs from 'fs';
import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore, SearchResultItem } from '@/napcat-core/index';
import { fileTypeFromFile } from 'file-type';
import { RkeyManager } from '@/napcat-core/helper/rkey';
import { calculateFileMD5 } from 'napcat-common/src/file';
import { rkeyDataType } from '../types/file';
import { NapProtoMsg } from 'napcat-protobuf';
import { FileId } from '../packet/transformer/proto/misc/fileid';
export class NTQQFileApi {
context: InstanceContext;
core: NapCatCore;
rkeyManager: RkeyManager;
packetRkey: Array<{ rkey: string; time: number; type: number; ttl: bigint; }> | undefined;
private fetchRkeyFailures: number = 0;
private readonly MAX_RKEY_FAILURES: number = 8;
constructor (context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
this.rkeyManager = new RkeyManager([
'http://ss.xingzhige.com/music_card/rkey',
'https://secret-service.bietiaop.com/rkeys',
],
this.context.logger
);
}
private async fetchRkeyWithRetry () {
if (this.fetchRkeyFailures >= this.MAX_RKEY_FAILURES) {
throw new Error('Native.FetchRkey 已被禁用');
}
try {
const ret = await this.core.apis.PacketApi.pkt.operation.FetchRkey();
this.fetchRkeyFailures = 0; // Reset failures on success
return ret;
} catch (error) {
this.fetchRkeyFailures++;
this.context.logger.logError('FetchRkey 失败', (error as Error).message);
throw error;
}
}
async getFileUrl (chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined, timeout: number = 5000) {
if (this.core.apis.PacketApi.packetStatus) {
try {
if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) {
return this.core.apis.PacketApi.pkt.operation.GetGroupFileUrl(+peer, fileUUID, timeout);
} else if (file10MMd5 && fileUUID) {
return this.core.apis.PacketApi.pkt.operation.GetPrivateFileUrl(peer, fileUUID, file10MMd5, timeout);
}
} catch (error) {
this.context.logger.logError('获取文件URL失败', (error as Error).message);
}
}
throw new Error('fileUUID or file10MMd5 is undefined');
}
async getPttUrl (peer: string, fileUUID?: string, timeout: number = 5000) {
if (this.core.apis.PacketApi.packetStatus && fileUUID) {
const appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
try {
if (appid && appid === 1403) {
return this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+peer, {
fileUuid: fileUUID,
storeId: 1,
uploadTime: 0,
ttl: 0,
subType: 0,
}, timeout);
} else if (fileUUID) {
return this.core.apis.PacketApi.pkt.operation.GetPttUrl(peer, {
fileUuid: fileUUID,
storeId: 1,
uploadTime: 0,
ttl: 0,
subType: 0,
}, timeout);
}
} catch (error) {
this.context.logger.logError('获取文件URL失败', (error as Error).message);
}
}
throw new Error('packet cant get ptt url');
}
async getVideoUrlPacket (peer: string, fileUUID?: string, timeout: number = 5000) {
if (this.core.apis.PacketApi.packetStatus && fileUUID) {
const appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
try {
if (appid && appid === 1415) {
return this.core.apis.PacketApi.pkt.operation.GetGroupVideoUrl(+peer, {
fileUuid: fileUUID,
storeId: 1,
uploadTime: 0,
ttl: 0,
subType: 0,
}, timeout);
} else if (fileUUID) {
return this.core.apis.PacketApi.pkt.operation.GetVideoUrl(peer, {
fileUuid: fileUUID,
storeId: 1,
uploadTime: 0,
ttl: 0,
subType: 0,
}, timeout);
}
} catch (error) {
this.context.logger.logError('获取文件URL失败', (error as Error).message);
}
}
throw new Error('packet cant get video url');
}
async copyFile (filePath: string, destPath: string) {
await this.core.util.copyFile(filePath, destPath);
}
async getFileSize (filePath: string): Promise<number> {
return await this.core.util.getFileSize(filePath);
}
async getVideoUrl (peer: Peer, msgId: string, elementId: string) {
return (await this.context.session.getRichMediaService().getVideoPlayUrlV2(peer, msgId, elementId, 0, {
downSourceType: 1,
triggerType: 1,
})).urlResult.domainUrl;
}
async uploadFile (filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0, uploadGroupFile = true) {
const fileMd5 = await calculateFileMD5(filePath);
const extOrEmpty = await fileTypeFromFile(filePath).then(e => e?.ext ?? '').catch(() => '');
const ext = extOrEmpty ? `.${extOrEmpty}` : '';
let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf('.') === -1) {
fileName += ext;
}
const fileSize = await this.getFileSize(filePath);
if (uploadGroupFile) {
const mediaPath = this.context.session.getMsgService().getRichMediaFilePathForGuild({
md5HexStr: fileMd5,
fileName,
elementType,
elementSubType,
thumbSize: 0,
needCreate: true,
downloadType: 1,
file_uuid: '',
});
await this.copyFile(filePath, mediaPath);
return {
md5: fileMd5,
fileName,
path: mediaPath,
fileSize,
ext,
};
}
return {
md5: fileMd5,
fileName,
path: filePath,
fileSize,
ext,
};
}
async downloadFileForModelId (peer: Peer, modelId: string, unknown: string, timeout = 1000 * 60 * 2) {
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/downloadFileForModelId',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[peer, [modelId], unknown],
() => true,
(arg) => arg?.commonFileInfo?.fileModelId === modelId,
1,
timeout
);
return fileTransNotifyInfo.filePath;
}
async downloadRawMsgMedia (msg: RawMessage[]) {
const res = await Promise.all(
msg.map(m =>
Promise.all(
m.elements
.filter(element =>
element.elementType === ElementType.PIC ||
element.elementType === ElementType.VIDEO ||
element.elementType === ElementType.PTT ||
element.elementType === ElementType.FILE
)
.map(element =>
this.downloadMedia(m.msgId, m.chatType, m.peerUid, element.elementId, '', '', 1000 * 60 * 2, true)
)
)
)
);
msg.forEach((m, msgIndex) => {
const elementResults = res[msgIndex];
let elementIndex = 0;
m.elements.forEach(element => {
if (
element.elementType === ElementType.PIC ||
element.elementType === ElementType.VIDEO ||
element.elementType === ElementType.PTT ||
element.elementType === ElementType.FILE
) {
switch (element.elementType) {
case ElementType.PIC:
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.VIDEO:
element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.PTT:
element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.FILE:
element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
}
elementIndex++;
}
});
});
return res.flat();
}
async downloadMedia (msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
// 用于下载文件
if (sourcePath && fs.existsSync(sourcePath)) {
if (force) {
try {
await fsPromises.unlink(sourcePath);
} catch {
//
}
} else {
return sourcePath;
}
}
const [, completeRetData] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelMsgService/downloadRichMedia',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[{
fileModelId: '0',
downSourceType: 0,
downloadSourceType: 0,
triggerType: 1,
msgId,
chatType,
peerUid,
elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
}],
() => true,
(arg) => arg.msgElementId === elementId && arg.msgId === msgId,
1,
timeout
);
return completeRetData.filePath;
}
async searchForFile (keys: string[]): Promise<SearchResultItem | undefined> {
const randomResultId = 100000 + Math.floor(Math.random() * 10000);
let searchId = 0;
const [, searchResult] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelFileAssistantService/searchFile',
'NodeIKernelFileAssistantListener/onFileSearch',
[
keys,
{ resultType: 2, pageLimit: 1 },
randomResultId,
],
(ret) => {
searchId = ret;
return true;
},
result => result.searchId === searchId && result.resultId === randomResultId
);
return searchResult.resultItems[0];
}
async downloadFileById (
fileId: string,
fileSize: number = 1024576,
estimatedTime: number = (fileSize * 1000 / 1024576) + 5000
) {
const [, fileData] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelFileAssistantService/downloadFile',
'NodeIKernelFileAssistantListener/onFileStatusChanged',
[[fileId]],
ret => ret.result === 0,
status => status.fileStatus === 2 && status.fileProgress === '0',
1,
estimatedTime // estimate 1MB/s
);
return fileData.filePath!;
}
async getImageUrl (element: PicElement): Promise<string> {
if (!element) {
return '';
}
const url: string = element.originImageUrl ?? '';
const md5HexStr = element.md5HexStr;
const fileMd5 = element.md5HexStr;
const parsedUrl = new URL(IMAGE_HTTP_HOST + url);
const imageAppid = parsedUrl.searchParams.get('appid');
const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid);
const imageFileId = parsedUrl.searchParams.get('fileid');
if (url && isNTV2 && imageFileId) {
const rkeyData = await this.getRkeyData();
return this.getImageUrlFromParsedUrl(imageFileId, imageAppid, rkeyData);
}
return this.getImageUrlFromMd5(fileMd5, md5HexStr);
}
private async getRkeyData () {
const rkeyData: rkeyDataType = {
private_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qEc3Rbib9LP4',
group_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qffcqm614gds',
online_rkey: false,
};
try {
if (this.core.apis.PacketApi.packetStatus) {
const rkey_expired_private = !this.packetRkey || (this.packetRkey[0] && this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000);
const rkey_expired_group = !this.packetRkey || (this.packetRkey[0] && this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000);
if (rkey_expired_private || rkey_expired_group) {
this.packetRkey = await this.fetchRkeyWithRetry();
}
if (this.packetRkey && this.packetRkey.length > 0) {
rkeyData.group_rkey = this.packetRkey[1]?.rkey.slice(6) ?? '';
rkeyData.private_rkey = this.packetRkey[0]?.rkey.slice(6) ?? '';
rkeyData.online_rkey = true;
}
}
} catch (error: unknown) {
this.context.logger.logDebug('获取native.rkey失败', (error as Error).message);
}
if (!rkeyData.online_rkey) {
try {
const tempRkeyData = await this.rkeyManager.getRkey();
rkeyData.group_rkey = tempRkeyData.group_rkey;
rkeyData.private_rkey = tempRkeyData.private_rkey;
rkeyData.online_rkey = tempRkeyData.expired_time > Date.now() / 1000;
} catch (error: unknown) {
this.context.logger.logDebug('获取remote.rkey失败', (error as Error).message);
}
}
// 进行 fallback.rkey 模式
return rkeyData;
}
private getImageUrlFromParsedUrl (imageFileId: string, appid: string, rkeyData: rkeyDataType): string {
const rkey = appid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
if (rkeyData.online_rkey) {
return IMAGE_HTTP_HOST_NT + `/download?appid=${appid}&fileid=${imageFileId}&rkey=${rkey}`;
}
return IMAGE_HTTP_HOST + `/download?appid=${appid}&fileid=${imageFileId}&rkey=${rkey}&spec=0`;
}
private getImageUrlFromMd5 (fileMd5: string | undefined, md5HexStr: string | undefined): string {
if (fileMd5 || md5HexStr) {
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr ?? '').toUpperCase()}/0`;
}
this.context.logger.logDebug('图片url获取失败', { fileMd5, md5HexStr });
return '';
}
}

Some files were not shown because too many files have changed in this diff Show More