From 9b14acf1bc9c13ce8d7e0d2633dd96e70e8c3ad3 Mon Sep 17 00:00:00 2001 From: sunlei Date: Mon, 15 Jun 2026 18:55:47 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=94=B6=E6=95=9BQQBot=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=8C=85=E7=AC=AC=E4=B8=89=E6=9C=9F=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API.md | 10 +- README.md | 11 +- ...5-api-admin-architecture-convergence-v3.md | 12 +- .../2026-06-15-api-admin-full-refactor-v3.md | 20 +- ...hitecture-convergence-bangdream-rewrite.md | 8 +- nest-cli.json | 12 +- scripts/bangdream-render-smoke.ps1 | 97 ++- sql/qqbot-init.sql | 32 +- sql/refactor-v3/01-seed-core.sql | 181 +++++- sql/refactor-v3/99-verify.sql | 7 + .../qqbot-plugin-argument-parser.service.ts | 4 +- .../qqbot-event-plugin-registry.service.ts | 61 +- .../registry/qqbot-plugin-registry.service.ts | 155 ++++- .../sdk/plugin-http-client.service.ts | 63 +- .../plugin-platform/plugin-platform.module.ts | 18 - .../bangdream-application.service.ts | 52 -- .../application/bangdream-client.service.ts | 35 -- .../application/bangdream-renderer.facade.ts | 555 ------------------ .../application/operation-pipeline.ts | 71 --- .../qqbot-bangdream-command.definitions.ts | 35 -- .../bangDream/provider/file-cache.client.ts | 201 ------- .../bangDream/qqbot-bangdream.plugin.ts | 58 -- .../bangDream/qqbot-bangdream.types.ts | 42 -- .../bangDream/registry/operation-registry.ts | 219 ------- .../search/search-dictionary.repository.ts | 19 - .../bangDream/shared/bangdream-constants.ts | 3 - .../plugins/bangDream/shared/model-utils.ts | 162 ----- .../{bangDream => bangdream}/TSUGU-LICENSE | 0 .../{bangDream => bangdream}/plugin.json | 5 +- .../application/bangdream-command-context.ts | 248 ++++++++ .../src/application}/bangdream-logger.ts | 6 +- .../src/application}/hook/hook-registry.ts | 79 ++- .../src/application}/main-data-store.ts | 38 +- .../src/application}/main-data.repository.ts | 4 +- .../src}/assets/BG/bg_object_big.png | Bin .../src}/assets/BG/common.png | Bin .../src}/assets/BG/live.png | Bin .../src}/assets/BG/star1.png | Bin .../src}/assets/BG/star2.png | Bin .../src}/assets/Card/B.png | Bin .../src}/assets/Card/D.png | Bin .../src}/assets/Card/K.png | Bin .../src}/assets/Card/L.png | Bin .../src}/assets/Card/limitBreakRank.png | Bin .../src}/assets/Card/star.png | Bin .../src}/assets/Card/star_trained.png | Bin .../src}/assets/Card/text.png | Bin .../src}/assets/EventStage/combo.png | Bin .../src}/assets/EventStage/judge.png | Bin .../src}/assets/EventStage/life.png | Bin .../assets/EventStage/presentEventMark.png | Bin .../src}/assets/EventStage/undefined.png | Bin .../src}/assets/Fonts/FangZhengHeiTi_GBK.ttf | Bin .../src}/assets/Fonts/Orbitron Black.ttf | Bin .../src}/assets/Fonts/old.ttf | Bin .../src}/assets/Rank/rank_0.png | Bin .../src}/assets/Rank/rank_1.png | Bin .../src}/assets/Rank/rank_1_1.png | Bin .../src}/assets/Rank/rank_1_2.png | Bin .../src}/assets/Rank/rank_1_3.png | Bin .../src}/assets/Rank/rank_2.png | Bin .../src}/assets/Rank/rank_2_1.png | Bin .../src}/assets/Rank/rank_2_2.png | Bin .../src}/assets/Rank/rank_2_3.png | Bin .../src}/assets/Rank/rank_3.png | Bin .../src}/assets/Rank/rank_3_1.png | Bin .../src}/assets/Rank/rank_3_2.png | Bin .../src}/assets/Rank/rank_3_3.png | Bin .../src}/assets/Rank/rank_4.png | Bin .../src}/assets/Rank/rank_4_0.png | Bin .../src}/assets/Rank/rank_4_1.png | Bin .../src}/assets/Rank/rank_4_2.png | Bin .../src}/assets/Rank/rank_4_3.png | Bin .../src}/assets/Rank/rank_4_4.png | Bin .../src}/assets/Rank/rank_4_5.png | Bin .../src}/assets/Rank/rank_4_6.png | Bin .../src}/assets/Rank/rank_4_7.png | Bin .../src}/assets/Rank/rank_4_8.png | Bin .../src}/assets/Rank/rank_4_9.png | Bin .../src}/assets/Rank/rank_5.png | Bin .../src}/assets/Rank/rank_6.png | Bin .../src}/assets/Skill/damage.png | Bin .../src}/assets/Skill/judge.png | Bin .../src}/assets/Skill/life.png | Bin .../src}/assets/SongChart/jacket.png | Bin .../src}/assets/SongChart/note/Bar.png | Bin .../src}/assets/SongChart/note/Flick.png | Bin .../src}/assets/SongChart/note/FlickTop.png | Bin .../src}/assets/SongChart/note/LeftArrow.png | Bin .../assets/SongChart/note/LeftArrowEnd.png | Bin .../src}/assets/SongChart/note/Long.png | Bin .../src}/assets/SongChart/note/RightArrow.png | Bin .../assets/SongChart/note/RightArrowEnd.png | Bin .../src}/assets/SongChart/note/Sim.png | Bin .../src}/assets/SongChart/note/Single.png | Bin .../src}/assets/SongChart/note/SingleOff.png | Bin .../src}/assets/SongChart/note/Skill.png | Bin .../src}/assets/SongChart/note/Tick.png | Bin .../src}/assets/err.png | Bin .../src}/assets/title.png | Bin .../src}/assets/tw.png | Bin .../config}/dictionary/default-dictionary.ts | 2 +- .../config}/dictionary/dictionary-loader.ts | 6 +- .../src}/config/runtime-config.ts | 39 +- .../src}/config/runtime-options.ts | 2 +- .../src/config/static}/area-item-fix.json | 0 .../src/config/static}/car-keyword.json | 0 .../src/config/static}/cards-cn-fix.json | 0 .../config/static}/fuzzy-search-settings.json | 0 .../src/config/static}/nickname-card.xlsx | Bin .../src/config/static}/nickname-event.xlsx | Bin .../src/config/static}/nickname-song.xlsx | Bin .../src/config/static}/player-number.xlsx | Bin .../src/config/static}/skills-cn-fix.json | 0 .../src/domain}/card/card-art.layout.ts | 0 .../src/domain}/card/card-art.renderer.ts | 22 +- .../src/domain}/card/card-art.repository.ts | 8 +- .../src/domain}/card/card-detail.renderer.ts | 38 +- .../src/domain}/card/card-icon.layout.ts | 2 +- .../src/domain}/card/card-icon.renderer.ts | 8 +- .../src/domain}/card/card-prefix.layout.ts | 2 +- .../src/domain}/card/card-prefix.renderer.ts | 16 +- .../src/domain}/card/card-rarity.layout.ts | 0 .../src/domain}/card/card-rarity.renderer.ts | 8 +- .../domain}/card/card-resource.repository.ts | 10 +- .../domain}/card/card-sd-character.layout.ts | 0 .../card/card-sd-character.renderer.ts | 8 +- .../src/domain}/card/card-search.renderer.ts | 26 +- .../domain}/card/card-skill-list.renderer.ts | 12 +- .../domain}/card/card-skill-text.layout.ts | 0 .../domain}/card/card-skill-text.renderer.ts | 12 +- .../src/domain}/card/card-stat.layout.ts | 0 .../src/domain}/card/card-stat.renderer.ts | 14 +- .../src/domain}/card/card.model.ts | 20 +- .../src/domain}/card/card.repository.ts | 4 +- .../src/domain}/catalog/area-item.model.ts | 6 +- .../catalog/attribute-list.renderer.ts | 6 +- .../catalog/attribute-resource.repository.ts | 4 +- .../src/domain}/catalog/attribute.model.ts | 4 +- .../src/domain}/catalog/band-list.renderer.ts | 8 +- .../catalog/band-resource.repository.ts | 6 +- .../src/domain}/catalog/band.model.ts | 8 +- .../src/domain}/catalog/color.model.ts | 0 .../catalog/costume-resource.repository.ts | 8 +- .../src/domain}/catalog/costume.model.ts | 10 +- .../domain}/catalog/degree-badge.renderer.ts | 8 +- .../src/domain}/catalog/degree-list.layout.ts | 0 .../domain}/catalog/degree-list.renderer.ts | 16 +- .../catalog/degree-resource.repository.ts | 6 +- .../src/domain}/catalog/degree.model.ts | 8 +- .../catalog/item-resource.repository.ts | 8 +- .../src/domain}/catalog/item.model.ts | 10 +- .../catalog/server-resource.repository.ts | 6 +- .../src/domain}/catalog/server.model.ts | 10 +- .../src/domain}/catalog/skill.model.ts | 2 +- .../character/character-detail.renderer.ts | 30 +- .../character/character-list.renderer.ts | 10 +- .../character-resource.repository.ts | 6 +- .../character/character-search.renderer.ts | 22 +- .../src/domain}/character/character.model.ts | 4 +- .../src/domain/common}/bangdream-protocol.ts | 0 .../src/domain/common/model-utils.ts | 43 ++ .../domain/common/qqbot-bangdream.types.ts | 64 ++ .../src/domain}/cutoff/cutoff-all.renderer.ts | 28 +- .../src/domain}/cutoff/cutoff-chart.layout.ts | 0 .../domain}/cutoff/cutoff-chart.renderer.ts | 22 +- .../domain}/cutoff/cutoff-detail.renderer.ts | 28 +- .../domain}/cutoff/cutoff-event-top.model.ts | 8 +- .../cutoff/cutoff-event-top.renderer.ts | 20 +- .../cutoff/cutoff-event-top.repository.ts | 6 +- .../src/domain}/cutoff/cutoff-predictor.ts | 0 .../domain}/cutoff/cutoff-recent.renderer.ts | 28 +- .../src/domain}/cutoff/cutoff.model.ts | 22 +- .../domain}/cutoff/timeline-chart.layout.ts | 0 .../domain}/cutoff/timeline-chart.renderer.ts | 6 +- .../domain}/event/event-data.repository.ts | 8 +- .../domain}/event/event-detail.renderer.ts | 46 +- .../domain}/event/event-search.renderer.ts | 40 +- .../event/event-stage-data.repository.ts | 4 +- .../event/event-stage-list.renderer.ts | 18 +- .../src/domain}/event/event-stage.layout.ts | 0 .../src/domain}/event/event-stage.model.ts | 10 +- .../src/domain}/event/event-stage.renderer.ts | 20 +- .../src/domain}/event/event-time.layout.ts | 0 .../src/domain}/event/event-time.renderer.ts | 14 +- .../src/domain}/event/event.model.ts | 20 +- .../src/domain}/event/event.repository.ts | 4 +- .../domain}/gacha/gacha-detail.renderer.ts | 28 +- .../src/domain}/gacha/gacha-list.layout.ts | 0 .../gacha/gacha-payment-method.renderer.ts | 12 +- .../domain}/gacha/gacha-pick-up.renderer.ts | 14 +- .../src/domain}/gacha/gacha-rate.renderer.ts | 12 +- .../gacha/gacha-resource.repository.ts | 8 +- .../domain}/gacha/gacha-simulate.layout.ts | 0 .../domain}/gacha/gacha-simulate.renderer.ts | 24 +- .../src/domain}/gacha/gacha.model.ts | 14 +- .../src/domain}/gacha/gacha.repository.ts | 4 +- .../domain}/player/deck-rank.repository.ts | 19 +- .../player/player-band-detail.layout.ts | 0 .../player/player-band-detail.renderer.ts | 20 +- .../domain}/player/player-card-icon.layout.ts | 0 .../player/player-card-icon.renderer.ts | 10 +- .../player/player-character-detail.layout.ts | 0 .../player-character-detail.renderer.ts | 14 +- .../domain}/player/player-data.repository.ts | 8 +- .../domain}/player/player-detail.renderer.ts | 26 +- .../player/player-difficulty-detail.layout.ts | 0 .../player-difficulty-detail.renderer.ts | 12 +- .../domain}/player/player-ranking.layout.ts | 0 .../domain}/player/player-ranking.renderer.ts | 16 +- .../player/player-ranking.repository.ts | 6 +- .../src/domain}/player/player.model.ts | 12 +- .../src/domain}/player/player.repository.ts | 4 +- .../policy/cn-event-estimate.policy.ts | 8 +- .../src/domain}/policy/cutoff.policy.ts | 8 +- .../src/domain}/policy/gacha.policy.ts | 2 +- .../src/domain}/policy/server.policy.ts | 2 +- .../src/domain}/search/entity-list-matcher.ts | 48 +- .../search/fuzzy-search-rule.registry.ts | 4 +- .../src/domain}/search/fuzzy-search.ts | 10 +- .../src/domain}/search/fuzzy-search.types.ts | 0 .../src/domain}/search/relation-matcher.ts | 2 +- .../search/search-dictionary.repository.ts | 19 + .../domain}/song/song-chart-preview.layout.ts | 0 .../song/song-chart-preview.renderer.ts | 8 +- .../src/domain}/song/song-chart.renderer.ts | 12 +- .../src/domain}/song/song-detail.renderer.ts | 28 +- .../domain}/song/song-difficulty.layout.ts | 0 .../domain}/song/song-difficulty.renderer.ts | 8 +- .../src/domain}/song/song-list.layout.ts | 4 +- .../src/domain}/song/song-list.renderer.ts | 22 +- .../src/domain}/song/song-meta.renderer.ts | 18 +- .../src/domain}/song/song-random.renderer.ts | 16 +- .../domain}/song/song-resource.repository.ts | 12 +- .../src/domain}/song/song-search.renderer.ts | 30 +- .../src/domain}/song/song.model.ts | 14 +- .../src/domain}/song/song.repository.ts | 4 +- .../qqbot/plugins/bangdream/src/index.ts | 224 +++++++ .../integration}/api-cache.client.ts | 8 +- .../integration}/bangdream-data-provider.ts | 0 .../integration}/bestdori.provider.ts | 17 +- .../integration}/hhwx-tracker.provider.ts | 15 +- .../integration}/provider-decorators.ts | 7 +- .../infrastructure/integration/runtime-io.ts | 123 ++++ .../storage}/asset-cache.client.ts | 16 +- .../src/infrastructure/storage}/cache-path.ts | 4 +- .../infrastructure/storage}/cache-policy.ts | 3 +- .../storage/file-cache.client.ts | 90 +++ .../storage}/static-patch.provider.ts | 8 +- .../src/operations/card-illustration/index.ts | 29 + .../src/operations/card-search/index.ts | 23 + .../src/operations/character-search/index.ts | 26 + .../src/operations/cutoff-all/index.ts | 22 + .../src/operations/cutoff-detail/index.ts | 28 + .../src/operations/cutoff-recent/index.ts | 28 + .../src/operations/event-search/index.ts | 23 + .../src/operations/event-stage/index.ts | 27 + .../src/operations/gacha-search/index.ts | 25 + .../src/operations/gacha-simulate/index.ts | 41 ++ .../plugins/bangdream/src/operations/index.ts | 45 ++ .../bangdream/src/operations/operation.ts | 17 + .../src/operations/player-search/index.ts | 27 + .../src/operations/song-chart/index.ts | 29 + .../src/operations/song-meta/index.ts | 17 + .../src/operations/song-random/index.ts | 24 + .../src/operations/song-search/index.ts | 23 + .../src}/theme/asset-manifest.ts | 4 +- .../src}/theme/canvas-background-scatter.ts | 0 .../src}/theme/canvas-background-text.ts | 2 +- .../src}/theme/canvas-background-triangle.ts | 2 +- .../src}/theme/canvas-background.ts | 12 +- .../src}/theme/canvas-blur-image.ts | 0 .../src}/theme/canvas-dotted-line.ts | 0 .../src}/theme/canvas-image.ts | 16 +- .../src}/theme/canvas-output.ts | 6 +- .../src}/theme/canvas-rect.ts | 6 +- .../src}/theme/canvas-text.layout.ts | 0 .../src}/theme/canvas-text.ts | 6 +- .../src/theme}/data-block.layout.ts | 0 .../src/theme}/data-block.renderer.ts | 8 +- .../src/theme}/detail-block.builder.ts | 6 +- .../src/theme}/detail-block.layout.ts | 0 .../src/theme}/detail-block.renderer.ts | 52 +- .../src/theme}/image-stack.ts | 0 .../src}/theme/layout.ts | 2 +- .../src/theme}/list-entity.layout.ts | 0 .../src/theme}/list-frame.layout.ts | 2 +- .../src/theme}/list-frame.renderer.ts | 18 +- .../src}/theme/render-theme.ts | 0 .../src/theme}/title.layout.ts | 2 +- .../src/theme}/title.renderer.ts | 8 +- .../{ff14Market => ff14-market}/plugin.json | 3 +- .../application/ff14-market-application.ts | 18 + .../src/config/ff14-market-config.ts | 23 + .../src/domain/ff14-market.types.ts} | 0 .../src/domain/ff14-worlds.ts} | 27 +- .../qqbot/plugins/ff14-market/src/index.ts | 85 +++ .../integration/ff14-market-client.ts} | 88 ++- .../ff14-market/src/operations/index.ts | 51 ++ .../src/operations/market-price.ts | 33 ++ .../src/operations/operation-manifest.ts | 18 + .../src/operations/resolve-item.ts | 27 + .../ff14Market/qqbot-ff14-market.plugin.ts | 98 ---- src/modules/qqbot/plugins/fflogs/plugin.json | 2 +- .../plugins/fflogs/qqbot-fflogs.plugin.ts | 96 --- .../src/application/fflogs-application.ts | 17 + .../fflogs/src/config/fflogs-config.ts | 42 ++ .../domain/fflogs.types.ts} | 0 src/modules/qqbot/plugins/fflogs/src/index.ts | 63 ++ .../integration/fflogs-client.ts} | 91 ++- .../storage/oauth-token-cache.ts | 15 + .../src/operations/character-summary.ts | 55 ++ .../plugins/fflogs/src/operations/index.ts | 43 ++ .../src/operations/operation-manifest.ts | 18 + .../qqbot/plugins/repeater/plugin.json | 2 +- .../plugins/repeater/qqbot-repeater.plugin.ts | 245 -------- .../src/application/repeater-application.ts | 157 +++++ .../repeater/src/config/repeater-config.ts | 56 ++ .../repeater/src/domain/repeat-policy.ts | 57 ++ .../repeater/src/domain/repeater.types.ts | 30 + .../repeater/src/events/message/index.ts | 8 + .../qqbot/plugins/repeater/src/index.ts | 35 ++ .../integration/repeater-host.ts | 15 + .../qqbot-current-operation-matrix.spec.ts | 21 +- .../qqbot-module-boundary.spec.ts | 2 +- .../qqbot-plugin-package-boundary.spec.ts | 111 +++- .../core/qqbot-core-module-contract.spec.ts | 43 -- .../bangdream-operation-parity.spec.ts | 61 +- .../runtime-config.spec.ts | 8 +- .../plugin-controller-http-smoke.spec.ts | 69 ++- .../plugins/plugin-platform-migration.spec.ts | 113 +++- .../plugins/plugin-registry-compat.spec.ts | 29 +- .../bangdream-application.service.spec.ts | 73 --- .../bangdream-client.service.spec.ts | 72 --- .../application/operation-pipeline.spec.ts | 119 ---- .../bangDream/registry/command-sql.spec.ts | 69 --- .../registry/operation-registry.spec.ts | 38 -- .../bangdream-package-entry.spec.ts | 134 +++++ .../card/card-art.layout.spec.ts | 2 +- .../card/card-art.repository.spec.ts | 4 +- .../card/card-icon.layout.spec.ts | 4 +- .../card/card-prefix.layout.spec.ts | 4 +- .../card/card-rarity.layout.spec.ts | 2 +- .../card/card-resource.repository.spec.ts | 6 +- .../card/card-sd-character.layout.spec.ts | 2 +- .../card/card-skill-text.layout.spec.ts | 2 +- .../card/card-stat.layout.spec.ts | 2 +- .../attribute-resource.repository.spec.ts | 4 +- .../catalog/band-resource.repository.spec.ts | 4 +- .../costume-resource.repository.spec.ts | 6 +- .../catalog/degree-list.layout.spec.ts | 2 +- .../degree-resource.repository.spec.ts | 6 +- .../catalog/item-resource.repository.spec.ts | 6 +- .../server-resource.repository.spec.ts | 4 +- .../character-resource.repository.spec.ts | 4 +- .../cutoff/chart.layout.spec.ts | 4 +- .../cutoff-event-top.repository.spec.ts | 6 +- .../dictionary/dictionary-loader.spec.ts | 8 +- .../event/event-data.repository.spec.ts | 6 +- .../event/event-stage-data.repository.spec.ts | 4 +- .../event/event-stage.layout.spec.ts | 2 +- .../event/event-time.layout.spec.ts | 2 +- .../gacha/gacha-list.layout.spec.ts | 2 +- .../gacha/gacha-resource.repository.spec.ts | 6 +- .../gacha/gacha-simulate.layout.spec.ts | 2 +- .../hook/hook-registry.spec.ts | 16 +- .../bangdream/manifest/command-sql.spec.ts | 120 ++++ .../manifest/operation-manifest.spec.ts | 55 ++ .../player/deck-rank.repository.spec.ts | 21 +- .../player/player-band-detail.layout.spec.ts | 2 +- .../player/player-card-icon.layout.spec.ts | 2 +- .../player-character-detail.layout.spec.ts | 2 +- .../player/player-data.repository.spec.ts | 6 +- .../player-difficulty-detail.layout.spec.ts | 2 +- .../player/player-ranking.layout.spec.ts | 2 +- .../player/player-ranking.repository.spec.ts | 6 +- .../policy/policy.spec.ts | 12 +- .../provider/cache-path.spec.ts | 2 +- .../provider/cache-policy.spec.ts | 2 +- .../provider/data-provider.spec.ts | 8 +- .../provider/file-cache.client.spec.ts | 47 +- .../search/fuzzy-search.spec.ts | 16 +- .../shared/data-block.layout.spec.ts | 2 +- .../shared/detail-block.builder.spec.ts | 4 +- .../shared/detail-block.layout.spec.ts | 2 +- .../shared/list-entity.layout.spec.ts | 2 +- .../shared/list-frame.layout.spec.ts | 2 +- .../shared/title.layout.spec.ts | 4 +- .../song/song-chart-preview.layout.spec.ts | 2 +- .../song/song-difficulty.layout.spec.ts | 2 +- .../song/song-list.layout.spec.ts | 2 +- .../song/song-resource.repository.spec.ts | 8 +- .../theme/canvas-text.layout.spec.ts | 2 +- .../qqbot-ff14-worlds.spec.ts | 2 +- .../qqbot-fflogs-client.service.spec.ts | 56 +- .../repeater/qqbot-repeater.plugin.spec.ts | 29 +- 396 files changed, 4467 insertions(+), 3669 deletions(-) delete mode 100644 src/modules/qqbot/plugins/bangDream/application/bangdream-application.service.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/application/bangdream-client.service.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/application/operation-pipeline.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/commands/qqbot-bangdream-command.definitions.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/provider/file-cache.client.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/qqbot-bangdream.types.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/registry/operation-registry.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/search/search-dictionary.repository.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/shared/bangdream-constants.ts delete mode 100644 src/modules/qqbot/plugins/bangDream/shared/model-utils.ts rename src/modules/qqbot/plugins/{bangDream => bangdream}/TSUGU-LICENSE (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream}/plugin.json (98%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context.ts rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/application}/bangdream-logger.ts (57%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/application}/hook/hook-registry.ts (54%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/application}/main-data-store.ts (76%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/application}/main-data.repository.ts (91%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/BG/bg_object_big.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/BG/common.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/BG/live.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/BG/star1.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/BG/star2.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/B.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/D.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/K.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/L.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/limitBreakRank.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/star.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/star_trained.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Card/text.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/EventStage/combo.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/EventStage/judge.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/EventStage/life.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/EventStage/presentEventMark.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/EventStage/undefined.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Fonts/FangZhengHeiTi_GBK.ttf (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Fonts/Orbitron Black.ttf (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Fonts/old.ttf (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_0.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_1.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_1_1.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_1_2.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_1_3.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_2.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_2_1.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_2_2.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_2_3.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_3.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_3_1.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_3_2.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_3_3.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_0.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_1.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_2.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_3.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_4.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_5.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_6.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_7.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_8.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_4_9.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_5.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Rank/rank_6.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Skill/damage.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Skill/judge.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/Skill/life.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/jacket.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Bar.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Flick.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/FlickTop.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/LeftArrow.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/LeftArrowEnd.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Long.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/RightArrow.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/RightArrowEnd.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Sim.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Single.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/SingleOff.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Skill.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/SongChart/note/Tick.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/err.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/title.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/assets/tw.png (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/config}/dictionary/default-dictionary.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/config}/dictionary/dictionary-loader.ts (96%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/config/runtime-config.ts (70%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/config/runtime-options.ts (97%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/area-item-fix.json (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/car-keyword.json (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/cards-cn-fix.json (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/fuzzy-search-settings.json (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/nickname-card.xlsx (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/nickname-event.xlsx (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/nickname-song.xlsx (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/player-number.xlsx (100%) rename src/modules/qqbot/plugins/{bangDream/static-config => bangdream/src/config/static}/skills-cn-fix.json (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-art.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-art.renderer.ts (90%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-art.repository.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-detail.renderer.ts (87%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-icon.layout.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-icon.renderer.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-prefix.layout.ts (96%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-prefix.renderer.ts (79%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-rarity.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-rarity.renderer.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-resource.repository.ts (90%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-sd-character.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-sd-character.renderer.ts (78%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-search.renderer.ts (89%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-skill-list.renderer.ts (69%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-skill-text.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-skill-text.renderer.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-stat.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card-stat.renderer.ts (87%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card.model.ts (95%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/card/card.repository.ts (74%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/area-item.model.ts (90%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/attribute-list.renderer.ts (79%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/attribute-resource.repository.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/attribute.model.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/band-list.renderer.ts (81%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/band-resource.repository.ts (83%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/band.model.ts (89%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/color.model.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/costume-resource.repository.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/costume.model.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/degree-badge.renderer.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/degree-list.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/degree-list.renderer.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/degree-resource.repository.ts (95%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/degree.model.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/item-resource.repository.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/item.model.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/server-resource.repository.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/server.model.ts (92%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/catalog/skill.model.ts (98%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/character/character-detail.renderer.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/character/character-list.renderer.ts (79%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/character/character-resource.repository.ts (90%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/character/character-search.renderer.ts (75%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/character/character.model.ts (96%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/domain/common}/bangdream-protocol.ts (100%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/domain/common/model-utils.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types.ts rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-all.renderer.ts (71%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-chart.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-chart.renderer.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-detail.renderer.ts (81%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-event-top.model.ts (93%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-event-top.renderer.ts (70%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-event-top.repository.ts (85%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-predictor.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff-recent.renderer.ts (80%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/cutoff.model.ts (96%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/timeline-chart.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/cutoff/timeline-chart.renderer.ts (92%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-data.repository.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-detail.renderer.ts (90%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-search.renderer.ts (84%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-stage-data.repository.ts (90%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-stage-list.renderer.ts (89%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-stage.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-stage.model.ts (89%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-stage.renderer.ts (77%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-time.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event-time.renderer.ts (89%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event.model.ts (95%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/event/event.repository.ts (78%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-detail.renderer.ts (79%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-list.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-payment-method.renderer.ts (80%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-pick-up.renderer.ts (81%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-rate.renderer.ts (69%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-resource.repository.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-simulate.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha-simulate.renderer.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha.model.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/gacha/gacha.repository.ts (74%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/deck-rank.repository.ts (70%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-band-detail.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-band-detail.renderer.ts (87%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-card-icon.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-card-icon.renderer.ts (81%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-character-detail.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-character-detail.renderer.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-data.repository.ts (87%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-detail.renderer.ts (81%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-difficulty-detail.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-difficulty-detail.renderer.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-ranking.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-ranking.renderer.ts (87%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player-ranking.repository.ts (80%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player.model.ts (96%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/player/player.repository.ts (62%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/policy/cn-event-estimate.policy.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/policy/cutoff.policy.ts (95%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/policy/gacha.policy.ts (98%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/policy/server.policy.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/search/entity-list-matcher.ts (62%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/search/fuzzy-search-rule.registry.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/search/fuzzy-search.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/search/fuzzy-search.types.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/search/relation-matcher.ts (96%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/domain/search/search-dictionary.repository.ts rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-chart-preview.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-chart-preview.renderer.ts (98%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-chart.renderer.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-detail.renderer.ts (79%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-difficulty.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-difficulty.renderer.ts (88%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-list.layout.ts (96%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-list.renderer.ts (84%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-meta.renderer.ts (74%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-random.renderer.ts (72%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-resource.repository.ts (92%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song-search.renderer.ts (73%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song.model.ts (95%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src/domain}/song/song.repository.ts (74%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/index.ts rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/integration}/api-cache.client.ts (79%) rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/integration}/bangdream-data-provider.ts (100%) rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/integration}/bestdori.provider.ts (81%) rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/integration}/hhwx-tracker.provider.ts (78%) rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/integration}/provider-decorators.ts (93%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io.ts rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/storage}/asset-cache.client.ts (84%) rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/storage}/cache-path.ts (93%) rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/storage}/cache-policy.ts (94%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client.ts rename src/modules/qqbot/plugins/{bangDream/provider => bangdream/src/infrastructure/storage}/static-patch.provider.ts (72%) create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/card-illustration/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/card-search/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/character-search/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/cutoff-all/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/cutoff-detail/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/cutoff-recent/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/event-search/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/event-stage/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/gacha-search/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/gacha-simulate/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/operation.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/player-search/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/song-chart/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/song-meta/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/song-random/index.ts create mode 100644 src/modules/qqbot/plugins/bangdream/src/operations/song-search/index.ts rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/asset-manifest.ts (92%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-background-scatter.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-background-text.ts (97%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-background-triangle.ts (98%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-background.ts (93%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-blur-image.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-dotted-line.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-image.ts (82%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-output.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-rect.ts (96%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-text.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/canvas-text.ts (98%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/data-block.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/data-block.renderer.ts (96%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/detail-block.builder.ts (81%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/detail-block.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/detail-block.renderer.ts (86%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/image-stack.ts (100%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/layout.ts (97%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/list-entity.layout.ts (100%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/list-frame.layout.ts (99%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/list-frame.renderer.ts (94%) rename src/modules/qqbot/plugins/{bangDream => bangdream/src}/theme/render-theme.ts (100%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/title.layout.ts (97%) rename src/modules/qqbot/plugins/{bangDream/shared => bangdream/src/theme}/title.renderer.ts (81%) rename src/modules/qqbot/plugins/{ff14Market => ff14-market}/plugin.json (95%) create mode 100644 src/modules/qqbot/plugins/ff14-market/src/application/ff14-market-application.ts create mode 100644 src/modules/qqbot/plugins/ff14-market/src/config/ff14-market-config.ts rename src/modules/qqbot/plugins/{ff14Market/qqbot-ff14-market.types.ts => ff14-market/src/domain/ff14-market.types.ts} (100%) rename src/modules/qqbot/plugins/{ff14Market/qqbot-ff14-worlds.ts => ff14-market/src/domain/ff14-worlds.ts} (93%) create mode 100644 src/modules/qqbot/plugins/ff14-market/src/index.ts rename src/modules/qqbot/plugins/{ff14Market/qqbot-ff14-client.service.ts => ff14-market/src/infrastructure/integration/ff14-market-client.ts} (85%) create mode 100644 src/modules/qqbot/plugins/ff14-market/src/operations/index.ts create mode 100644 src/modules/qqbot/plugins/ff14-market/src/operations/market-price.ts create mode 100644 src/modules/qqbot/plugins/ff14-market/src/operations/operation-manifest.ts create mode 100644 src/modules/qqbot/plugins/ff14-market/src/operations/resolve-item.ts delete mode 100644 src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin.ts delete mode 100644 src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin.ts create mode 100644 src/modules/qqbot/plugins/fflogs/src/application/fflogs-application.ts create mode 100644 src/modules/qqbot/plugins/fflogs/src/config/fflogs-config.ts rename src/modules/qqbot/plugins/fflogs/{qqbot-fflogs.types.ts => src/domain/fflogs.types.ts} (100%) create mode 100644 src/modules/qqbot/plugins/fflogs/src/index.ts rename src/modules/qqbot/plugins/fflogs/{qqbot-fflogs-client.service.ts => src/infrastructure/integration/fflogs-client.ts} (95%) create mode 100644 src/modules/qqbot/plugins/fflogs/src/infrastructure/storage/oauth-token-cache.ts create mode 100644 src/modules/qqbot/plugins/fflogs/src/operations/character-summary.ts create mode 100644 src/modules/qqbot/plugins/fflogs/src/operations/index.ts create mode 100644 src/modules/qqbot/plugins/fflogs/src/operations/operation-manifest.ts delete mode 100644 src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/application/repeater-application.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/config/repeater-config.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/domain/repeat-policy.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/domain/repeater.types.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/events/message/index.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/index.ts create mode 100644 src/modules/qqbot/plugins/repeater/src/infrastructure/integration/repeater-host.ts rename test/modules/qqbot/plugins/{bangDream => bangdream}/runtime-config.spec.ts (52%) delete mode 100644 test/qqbot/plugins/bangDream/application/bangdream-application.service.spec.ts delete mode 100644 test/qqbot/plugins/bangDream/application/bangdream-client.service.spec.ts delete mode 100644 test/qqbot/plugins/bangDream/application/operation-pipeline.spec.ts delete mode 100644 test/qqbot/plugins/bangDream/registry/command-sql.spec.ts delete mode 100644 test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts create mode 100644 test/qqbot/plugins/bangdream/application/bangdream-package-entry.spec.ts rename test/qqbot/plugins/{bangDream => bangdream}/card/card-art.layout.spec.ts (93%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-art.repository.spec.ts (91%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-icon.layout.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-prefix.layout.spec.ts (91%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-rarity.layout.spec.ts (89%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-resource.repository.spec.ts (93%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-sd-character.layout.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-skill-text.layout.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/card/card-stat.layout.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/attribute-resource.repository.spec.ts (89%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/band-resource.repository.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/costume-resource.repository.spec.ts (88%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/degree-list.layout.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/degree-resource.repository.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/item-resource.repository.spec.ts (87%) rename test/qqbot/plugins/{bangDream => bangdream}/catalog/server-resource.repository.spec.ts (90%) rename test/qqbot/plugins/{bangDream => bangdream}/character/character-resource.repository.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/cutoff/chart.layout.spec.ts (86%) rename test/qqbot/plugins/{bangDream => bangdream}/cutoff/cutoff-event-top.repository.spec.ts (85%) rename test/qqbot/plugins/{bangDream => bangdream}/dictionary/dictionary-loader.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/event/event-data.repository.spec.ts (88%) rename test/qqbot/plugins/{bangDream => bangdream}/event/event-stage-data.repository.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/event/event-stage.layout.spec.ts (96%) rename test/qqbot/plugins/{bangDream => bangdream}/event/event-time.layout.spec.ts (94%) rename test/qqbot/plugins/{bangDream => bangdream}/gacha/gacha-list.layout.spec.ts (91%) rename test/qqbot/plugins/{bangDream => bangdream}/gacha/gacha-resource.repository.spec.ts (94%) rename test/qqbot/plugins/{bangDream => bangdream}/gacha/gacha-simulate.layout.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/hook/hook-registry.spec.ts (76%) create mode 100644 test/qqbot/plugins/bangdream/manifest/command-sql.spec.ts create mode 100644 test/qqbot/plugins/bangdream/manifest/operation-manifest.spec.ts rename test/qqbot/plugins/{bangDream => bangdream}/player/deck-rank.repository.spec.ts (76%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-band-detail.layout.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-card-icon.layout.spec.ts (91%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-character-detail.layout.spec.ts (93%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-data.repository.spec.ts (90%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-difficulty-detail.layout.spec.ts (94%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-ranking.layout.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/player/player-ranking.repository.spec.ts (83%) rename test/qqbot/plugins/{bangDream => bangdream}/policy/policy.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/provider/cache-path.spec.ts (91%) rename test/qqbot/plugins/{bangDream => bangdream}/provider/cache-policy.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/provider/data-provider.spec.ts (90%) rename test/qqbot/plugins/{bangDream => bangdream}/provider/file-cache.client.spec.ts (52%) rename test/qqbot/plugins/{bangDream => bangdream}/search/fuzzy-search.spec.ts (85%) rename test/qqbot/plugins/{bangDream => bangdream}/shared/data-block.layout.spec.ts (95%) rename test/qqbot/plugins/{bangDream => bangdream}/shared/detail-block.builder.spec.ts (85%) rename test/qqbot/plugins/{bangDream => bangdream}/shared/detail-block.layout.spec.ts (93%) rename test/qqbot/plugins/{bangDream => bangdream}/shared/list-entity.layout.spec.ts (87%) rename test/qqbot/plugins/{bangDream => bangdream}/shared/list-frame.layout.spec.ts (96%) rename test/qqbot/plugins/{bangDream => bangdream}/shared/title.layout.spec.ts (94%) rename test/qqbot/plugins/{bangDream => bangdream}/song/song-chart-preview.layout.spec.ts (97%) rename test/qqbot/plugins/{bangDream => bangdream}/song/song-difficulty.layout.spec.ts (94%) rename test/qqbot/plugins/{bangDream => bangdream}/song/song-list.layout.spec.ts (94%) rename test/qqbot/plugins/{bangDream => bangdream}/song/song-resource.repository.spec.ts (92%) rename test/qqbot/plugins/{bangDream => bangdream}/theme/canvas-text.layout.spec.ts (94%) rename test/qqbot/plugins/{ff14Market => ff14-market}/qqbot-ff14-worlds.spec.ts (97%) diff --git a/API.md b/API.md index 57c0bb5..63bcb9c 100644 --- a/API.md +++ b/API.md @@ -449,7 +449,7 @@ pnpm qqbot-plugin install-local ### BangDream -插件 key:`bangdream`。旧 `bangDream` 作为兼容别名仍可解析;当前源码根目录为 `src/modules/qqbot/plugins/bangDream`,不再使用旧 `tsugu` 子目录。 +插件 key:`bangdream`。旧 `bangDream` 作为兼容别名仍可解析;当前源码根目录为 `src/modules/qqbot/plugins/bangdream/src`,按第三期插件结构拆分为 `operations`、`domain/*`、`application`、`infrastructure/integration`、`infrastructure/storage`、`config`、`assets` 和 `theme`,不再使用旧 `tsugu` 子目录、宿主 builtins 包装层或纯转接目录。 | operation key | 命令 | 说明 | | ----------------------------- | ------------- | ------------------------ | @@ -469,11 +469,11 @@ pnpm qqbot-plugin install-local | `bangdream.cutoff.all` | `/ycxall` | 全档位预测线 | | `bangdream.cutoff.recent` | `/lsycx` | 历史/近期档线 | -`registry/operation-registry.ts` 是 BangDream operation、handlerName、别名、冷却和说明的单一来源。新增或调整命令必须同步在线命令 SQL,并跑 registry/command-SQL 测试。 +`plugins/bangdream/plugin.json` 是 BangDream operation、handlerName、别名、权限、超时和说明的单一来源。新增或调整命令必须同步在线命令 SQL,并跑 manifest/command-SQL 测试。 ### FF14 Market -插件 key:`ff14-market`。旧 `ff14Market` 作为兼容别名仍可解析。 +插件 key:`ff14-market`。旧 `ff14Market` 作为兼容别名仍可解析;源码按第三期插件结构拆分为 `operations`、`application`、`domain`、`infrastructure/integration` 和 `config`。 | operation key | 说明 | | ------------------- | ------------------------------------------ | @@ -486,6 +486,8 @@ pnpm qqbot-plugin install-local 插件 key:`fflogs`。 +源码按第三期插件结构拆分为 `operations`、`application`、`domain`、`infrastructure/integration`、`infrastructure/storage` 和 `config`。 + | operation key | 说明 | | -------------------------- | --------------------------------------------------------------- | | `fflogs.character.summary` | 查询 FFLogs 角色公开排名;传 `encounter` 时查询指定高难最近记录 | @@ -527,7 +529,7 @@ BangDream 图片 smoke: ```powershell .\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.song.search -Text "夏祭り" -OutFile ".kt-workspace/bangdream-smoke/song.jpg" -.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text "310" -OutFile ".kt-workspace/bangdream-smoke/stage.jpg" +.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text "310" -OutFile ".kt-workspace/bangdream-smoke/stage.jpg" -ExpectedImageCount 5 ``` Jenkins/K8s 发布后还需要观察 rollout、新 Pod 日志,并跑真实运行态 smoke;推送成功不等于发布完成。 diff --git a/README.md b/README.md index 76206f3..4905afa 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ | `wordpress` | WordPress REST 代理、登录态透传、文章/分类/标签/主题配置 | | `qqbot` | QQBot 账号、NapCat 扫码登录、OneBot 反向 WS、在线命令、规则、权限、发送/接收日志和插件平台 | | `modules/qqbot/plugin-platform` | QQBot 插件 manifest 校验、版本安装、运行事件、受控 SDK 和 CLI 脚手架 | -| `qqbot/plugins/bangDream` | BanG Dream 查曲、查卡、查活动、试炼、玩家、卡池、抽卡模拟、档线、谱面出图 | -| `qqbot/plugins/ff14Market` | XIVAPI + Universalis 物品解析和 FF14 市场查价 | +| `qqbot/plugins/bangdream` | BanG Dream 查曲、查卡、查活动、试炼、玩家、卡池、抽卡模拟、档线、谱面出图 | +| `qqbot/plugins/ff14-market` | XIVAPI + Universalis 物品解析和 FF14 市场查价 | | `qqbot/plugins/fflogs` | FFLogs v2 GraphQL 角色排名和指定高难最近记录查询 | | `minio` | Bucket 检查、上传、列表、临时 URL、代理下载、删除 | | `common` | 响应封装、异常过滤、请求日志、日期格式化、字典解码、Snowflake、工具服务 | @@ -160,8 +160,8 @@ API 暴露 `GET /health/runtime` 作为本地 smoke、Jenkins/K8s 和 ktWorkflow - NapCat 设备身份按账号持久化到 `napcat_device_identity`:同一账号重建容器会复用数据目录、hostname、machine-id 和 MAC,并在 Docker run 中注入 `--hostname`、`--mac-address`、只读 `/etc/machine-id`,避免频繁重建被 QQ 判定为全新设备。 - NapCat 新设备验证走同一 scan session:`CaptchaLogin` 返回 `needNewDevice` 后,后端继续调用 `GetNewDeviceQRCode -> PollNewDeviceQR -> NewDeviceLogin`,Admin/SSE 分开展示 `captchaUrl`、`newDeviceQrcode`、已扫码、确认中、验证成功、登录成功/失败等中文进度,不把 `jumpUrl` 当作唯一完成入口。 - NapCat 离线看门狗按 `QQBOT_NAPCAT_WATCHDOG_INTERVAL_MS`(默认 `120000`,最小 `30000`,`QQBOT_NAPCAT_WATCHDOG_ENABLED=false` 关闭)定时巡检在线账号,使掉线/被踢无需管理员打开列表页即可及时发现;检测到离线后先尝试 `ACCOUNT` 历史会话快速登录,再尝试账号保存的登录密码,仍失败时写入离线原因并复用 `super` 站内信告警;看门狗不自动进入扫码阶段。 -- BangDream 当前源码根目录是 `src/modules/qqbot/plugins/bangDream`;不要恢复旧 `tsugu` 层级或旧大桶目录。 -- BangDream 在线命令以 `registry/operation-registry.ts` 为单一来源,新增命令必须同步 SQL/在线命令表并跑 registry/command-SQL 测试。 +- BangDream 当前源码根目录是 `src/modules/qqbot/plugins/bangdream/src`;按第三期插件结构放置真实职责代码:业务在 `domain/*`,编排在 `application`,操作在 `operations`,外部 API 在 `infrastructure/integration`,缓存/静态修正在 `infrastructure/storage`,字典和静态配置在 `config`,视觉渲染公共件在 `theme`;不要恢复旧 `tsugu` 层级、旧大桶目录、纯 re-export 转接文件或空 `.gitkeep` 目录壳。 +- BangDream 在线命令以 `plugins/bangdream/plugin.json` 为单一来源,新增命令必须同步 SQL/在线命令表并跑 manifest/command-SQL 测试。 - BangDream event stage 大图必须保持分页拆图行为,线上 smoke 关注 `imageCount=5`,避免大 canvas OOM 回归。 ## 轻量验证 @@ -184,6 +184,7 @@ BangDream 图片能力改动: ```powershell .\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.song.search -Text "夏祭り" -OutFile ".kt-workspace/bangdream-smoke/song.jpg" +.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text "310" -OutFile ".kt-workspace/bangdream-smoke/stage.jpg" -ExpectedImageCount 5 ``` 接口改动必须启动或复用本地服务,并真实调用一次对应接口。 @@ -196,4 +197,4 @@ BangDream 图片能力改动: | 一级来源 | 使用方式 | License | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ------- | -| [Tsugu BangDream Bot](https://github.com/Yamamoto-2/tsugu-bangdream-bot) | BangDream QQBot 后端能力已重构合入 `src/modules/qqbot/plugins/bangDream`,保留本地 `TSUGU-LICENSE` | MIT | +| [Tsugu BangDream Bot](https://github.com/Yamamoto-2/tsugu-bangdream-bot) | BangDream QQBot 后端能力已重构合入 `src/modules/qqbot/plugins/bangdream/src`,保留本地 `TSUGU-LICENSE` | MIT | diff --git a/docs/superpowers/plans/2026-06-15-api-admin-architecture-convergence-v3.md b/docs/superpowers/plans/2026-06-15-api-admin-architecture-convergence-v3.md index f3263f2..82060f9 100644 --- a/docs/superpowers/plans/2026-06-15-api-admin-architecture-convergence-v3.md +++ b/docs/superpowers/plans/2026-06-15-api-admin-architecture-convergence-v3.md @@ -849,14 +849,14 @@ Run: ```powershell $api = 'D:\MyFiles\KT\Node\kt-template-online-api' $candidateFiles = @( - 'src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts', - 'src/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts', - 'src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin.ts', + 'src/modules/qqbot/plugins/ff14-market/qqbot-ff14-client.service.ts', + 'src/modules/qqbot/plugins/ff14-market/qqbot-ff14-worlds.ts', + 'src/modules/qqbot/plugins/ff14-market/qqbot-ff14-market.plugin.ts', 'src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts', 'src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin.ts', 'src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts', - 'src/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade.ts', - 'src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin.ts' + 'src/modules/qqbot/plugins/bangdream/src/application/bangdream-renderer.facade.ts', + 'src/modules/qqbot/plugins/bangdream/src/qqbot-bangdream.plugin.ts' ) foreach ($file in $candidateFiles) { $absolute = Join-Path $api $file @@ -878,7 +878,7 @@ Record each deletion or keep decision in the inventory table. Run: ```powershell -pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --runTestsByPath test/modules/qqbot/plugin-platform/manifest.spec.ts test/modules/qqbot/plugin-platform/cli.spec.ts test/modules/qqbot/plugin-platform/persistence-contract.spec.ts test/modules/qqbot/plugin-platform/plugin-platform-api-contract.spec.ts test/modules/qqbot/plugin-platform/worker-runtime.spec.ts test/modules/qqbot/plugins/plugin-registry-compat.spec.ts test/modules/qqbot/plugins/plugin-platform-migration.spec.ts test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts test/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts test/qqbot/plugins/bangDream/registry/command-sql.spec.ts +pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --runTestsByPath test/modules/qqbot/plugin-platform/manifest.spec.ts test/modules/qqbot/plugin-platform/cli.spec.ts test/modules/qqbot/plugin-platform/persistence-contract.spec.ts test/modules/qqbot/plugin-platform/plugin-platform-api-contract.spec.ts test/modules/qqbot/plugin-platform/worker-runtime.spec.ts test/modules/qqbot/plugins/plugin-registry-compat.spec.ts test/modules/qqbot/plugins/plugin-platform-migration.spec.ts test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts test/qqbot/plugins/ff14-market/qqbot-ff14-worlds.spec.ts test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts test/qqbot/plugins/bangdream/manifest/operation-manifest.spec.ts test/qqbot/plugins/bangdream/manifest/command-sql.spec.ts pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api run typecheck ``` diff --git a/docs/superpowers/plans/2026-06-15-api-admin-full-refactor-v3.md b/docs/superpowers/plans/2026-06-15-api-admin-full-refactor-v3.md index 039346e..5086281 100644 --- a/docs/superpowers/plans/2026-06-15-api-admin-full-refactor-v3.md +++ b/docs/superpowers/plans/2026-06-15-api-admin-full-refactor-v3.md @@ -1191,13 +1191,13 @@ git -C D:\MyFiles\KT\Vue\kt-template-admin commit -m "feat: 增加QQBot插件管 **Files:** -- Create: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\bangDream\plugin.json` -- Create/modify: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\bangDream\**` -- Test: `D:\MyFiles\KT\Node\kt-template-online-api\test\modules\qqbot\plugins\bangDream\**` +- Create: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\bangdream\plugin.json` +- Create/modify: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\bangdream\**` +- Test: `D:\MyFiles\KT\Node\kt-template-online-api\test\modules\qqbot\plugins\bangdream\**` - [x] **Step 1: Preserve business directories** -Keep business capabilities in `song`, `card`, `character`, `event`, `gacha`, `player`, `cutoff`, `catalog`, with cross-cutting code only in `application`, `registry`, `hook`, `provider`, `policy`, `theme`, `config`, `dictionary`, `search`, `shared`. +Keep business capabilities in `domain/song`, `domain/card`, `domain/character`, `domain/event`, `domain/gacha`, `domain/player`, `domain/cutoff`, and `domain/catalog`; keep orchestration in `application`, operations in `operations`, external APIs in `infrastructure/integration`, cache/static patch adapters in `infrastructure/storage`, dictionary/static config in `config`, and visual helpers in `theme`. - [x] **Step 2: Convert operation registry to manifest metadata** @@ -1208,22 +1208,22 @@ Keep operation keys and aliases stable. `handlerName` becomes worker-internal. Run: ```powershell -pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts test/qqbot/plugins/bangDream/registry/command-sql.spec.ts test/modules/qqbot/plugins/bangDream +pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangdream/manifest/operation-manifest.spec.ts test/qqbot/plugins/bangdream/manifest/command-sql.spec.ts test/modules/qqbot/plugins/bangdream-rewrite/bangdream-operation-parity.spec.ts pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api run typecheck ``` Expected: PASS. Event stage smoke keeps `imageCount=5`. -Actual note: BangDream moved to `src/modules/qqbot/plugins/bangDream` with the approved business and cross-cutting directory layout preserved. Added `plugin.json` with platform key `bangdream`; manifest operations are asserted against `BANGDREAM_OPERATION_REGISTRY` for stable operation keys, aliases, names, and worker-internal `handlerName`. Runtime registry now uses platform key `bangdream` as primary while resolving legacy `bangDream`; `/qqbot/plugin/*` local HTTP smoke now proves the controller returns platform keys and resolves legacy `bangDream` to `bangdream`. Verification passed via focused registry/command SQL Jest, plugin migration contract, QQBot core module contract path guard, plugin controller HTTP smoke, `pnpm run typecheck`, and `scripts/bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text 310`, which produced `imageCount=5`. +Actual note: BangDream moved to `src/modules/qqbot/plugins/bangdream/src` with third-phase plugin ownership: business code in `domain/*`, orchestration in `application`, operations in `operations`, external APIs in `infrastructure/integration`, cache/static patch adapters in `infrastructure/storage`, dictionary/static config in `config`, and visual helpers in `theme`. Added `plugin.json` with platform key `bangdream`; manifest operations are the single metadata source for stable operation keys, aliases, names, and worker-internal `handlerName`. Runtime registry now uses platform key `bangdream` as primary while resolving legacy `bangDream`; `/qqbot/plugin/*` local HTTP smoke now proves the controller returns platform keys and resolves legacy `bangDream` to `bangdream`. Verification passed via focused manifest/command SQL Jest, plugin migration contract, QQBot core module contract path guard, plugin controller HTTP smoke, `pnpm run typecheck`, and `scripts/bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text 310`, which produced `imageCount=5`. ### Task 6.2: Rewrite FF14 Market, FFLogs, and Repeater **Files:** -- Create: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\ff14Market\**` +- Create: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\ff14-market\**` - Create: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\fflogs\**` - Create: `D:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugins\repeater\**` -- Test: `D:\MyFiles\KT\Node\kt-template-online-api\test\modules\qqbot\plugins\ff14Market\**` +- Test: `D:\MyFiles\KT\Node\kt-template-online-api\test\modules\qqbot\plugins\ff14-market\**` - Test: `D:\MyFiles\KT\Node\kt-template-online-api\test\modules\qqbot\plugins\fflogs\**` - Test: `D:\MyFiles\KT\Node\kt-template-online-api\test\modules\qqbot\plugins\repeater\**` @@ -1244,13 +1244,13 @@ Use host send queue and account binding. Do not bypass rate limit. Run: ```powershell -pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --runTestsByPath test/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts test/modules/qqbot/plugins/ff14Market test/modules/qqbot/plugins/fflogs test/modules/qqbot/plugins/repeater +pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --runTestsByPath test/qqbot/plugins/ff14-market/qqbot-ff14-worlds.spec.ts test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts test/modules/qqbot/plugins/ff14-market test/modules/qqbot/plugins/fflogs test/modules/qqbot/plugins/repeater pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api run typecheck ``` Expected: PASS. -Actual note: FF14 Market, FFLogs, and Repeater moved to `src/modules/qqbot/plugins/*` and each now has a platform `plugin.json`. FF14 Market uses platform key `ff14-market` with legacy `ff14Market` compatibility and delegates outbound requests through `QqbotPluginHttpClientService`; FFLogs delegates outbound requests through the same host SDK and keeps credential references in manifest config schema without real secrets; Repeater remains event-plugin driven through host account binding and send queue services. Verification passed via focused plugin Jest, migration direct-HTTP scan for FF14/FFLogs, `/qqbot/plugin/list` and `/qqbot/plugin/operation/list` local HTTP smoke, and `pnpm run typecheck`; final focused Jest covered 9 suites / 27 tests. +Actual note: FF14 Market, FFLogs, and Repeater moved to `src/modules/qqbot/plugins/*` and each now has a platform `plugin.json` plus real third-phase package structure. FF14 Market splits operation binding into `operations`, use cases into `application`, world/market types into `domain`, external clients into `infrastructure/integration`, and config into `config`; FFLogs splits character-summary operation, application use case, GraphQL/OAuth client, config, and token cache; Repeater splits event handler, repeat policy/domain state, runtime config, application state, and host integration. Verification passed via focused plugin Jest, migration direct-HTTP scan for FF14/FFLogs, `/qqbot/plugin/list` and `/qqbot/plugin/operation/list` local HTTP smoke, and `pnpm run typecheck`; final focused Jest covered 9 suites / 27 tests. ### Task 6.3: Commit Batch 6 diff --git a/docs/superpowers/plans/2026-06-15-qqbot-architecture-convergence-bangdream-rewrite.md b/docs/superpowers/plans/2026-06-15-qqbot-architecture-convergence-bangdream-rewrite.md index 948a7e3..6790d37 100644 --- a/docs/superpowers/plans/2026-06-15-qqbot-architecture-convergence-bangdream-rewrite.md +++ b/docs/superpowers/plans/2026-06-15-qqbot-architecture-convergence-bangdream-rewrite.md @@ -34,7 +34,7 @@ This plan is complete only when all of these are true: - `pnpm-lock.yaml` - `src/modules/qqbot/napcat/integration/napcat-login-api.client.ts` - `src/modules/qqbot/plugin-platform/qqbot-plugin.controller.ts` - - `src/modules/qqbot/plugins/bangDream/shared/main-data-store.ts` + - `src/modules/qqbot/plugins/bangdream/src/application/main-data-store.ts` - `test/modules/qqbot/napcat/new-device-flow.spec.ts` - `test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts` - Existing Admin dirty work contains the matching plugin pagination wrapper/page change: @@ -93,7 +93,7 @@ pnpm --dir D:\MyFiles\KT\Vue\kt-template-admin -F @vben/web-antdv-next run typec - [ ] Commit the verified baseline in API with: ```powershell -git -C D:\MyFiles\KT\Node\kt-template-online-api add package.json pnpm-lock.yaml src/modules/qqbot/napcat/integration/napcat-login-api.client.ts src/modules/qqbot/plugin-platform/qqbot-plugin.controller.ts src/modules/qqbot/plugins/bangDream/shared/main-data-store.ts test/modules/qqbot/napcat/new-device-flow.spec.ts test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts +git -C D:\MyFiles\KT\Node\kt-template-online-api add package.json pnpm-lock.yaml src/modules/qqbot/napcat/integration/napcat-login-api.client.ts src/modules/qqbot/plugin-platform/qqbot-plugin.controller.ts src/modules/qqbot/plugins/bangdream/src/application/main-data-store.ts test/modules/qqbot/napcat/new-device-flow.spec.ts test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts git -C D:\MyFiles\KT\Node\kt-template-online-api commit -m "fix: 补齐QQBot插件分页与新设备二维码" ``` @@ -472,7 +472,7 @@ powershell -ExecutionPolicy Bypass -File D:\MyFiles\KT\Node\kt-template-online-a ```powershell git -C D:\MyFiles\KT\Node\kt-template-online-api add src/modules/qqbot/plugins/bangdream test/modules/qqbot/plugins/bangdream-rewrite test/modules/qqbot/architecture sql/refactor-v3 docs/refactor-v3 API.md -git -C D:\MyFiles\KT\Node\kt-template-online-api add -u src/modules/qqbot/plugins/bangDream +git -C D:\MyFiles\KT\Node\kt-template-online-api add -u src/modules/qqbot/plugins/bangdream/src git -C D:\MyFiles\KT\Node\kt-template-online-api commit -m "refactor: 重写BangDream插件" ``` @@ -533,7 +533,7 @@ pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api run typecheck ```powershell git -C D:\MyFiles\KT\Node\kt-template-online-api add src/modules/qqbot/plugins/ff14-market src/modules/qqbot/plugins/fflogs src/modules/qqbot/plugins/repeater test/modules/qqbot/plugins test/modules/qqbot/architecture sql/refactor-v3 docs/refactor-v3 API.md -git -C D:\MyFiles\KT\Node\kt-template-online-api add -u src/modules/qqbot/plugins/ff14Market +git -C D:\MyFiles\KT\Node\kt-template-online-api add -u src/modules/qqbot/plugins/ff14-market git -C D:\MyFiles\KT\Node\kt-template-online-api commit -m "refactor: 规范化QQBot内置插件包" ``` diff --git a/nest-cli.json b/nest-cli.json index 6e24a0d..9f66766 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -5,11 +5,19 @@ "compilerOptions": { "assets": [ { - "include": "modules/qqbot/plugins/bangDream/assets/**/*", + "include": "modules/qqbot/plugins/*/plugin.json", "outDir": "dist" }, { - "include": "modules/qqbot/plugins/bangDream/static-config/**/*", + "include": "modules/qqbot/plugins/bangdream/TSUGU-LICENSE", + "outDir": "dist" + }, + { + "include": "modules/qqbot/plugins/bangdream/src/assets/**/*", + "outDir": "dist" + }, + { + "include": "modules/qqbot/plugins/bangdream/src/config/static/**/*", "outDir": "dist" } ], diff --git a/scripts/bangdream-render-smoke.ps1 b/scripts/bangdream-render-smoke.ps1 index 75867a3..5bcc2e8 100644 --- a/scripts/bangdream-render-smoke.ps1 +++ b/scripts/bangdream-render-smoke.ps1 @@ -4,6 +4,7 @@ param( [string]$DisplayedServerList = "cn jp", [string]$OutFile = ".kt-workspace/bangdream-smoke/bangdream-smoke.jpg", [int]$TimeoutSeconds = 45, + [int]$ExpectedImageCount = 0, [switch]$UseEasyBg ) @@ -38,6 +39,7 @@ $Payload = @{ } operationKey = $OperationKey outFile = $ResolvedOutFile + expectedImageCount = $ExpectedImageCount } $PayloadJson = $Payload | ConvertTo-Json -Compress -Depth 5 $PayloadBase64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($PayloadJson)) @@ -45,17 +47,96 @@ $PayloadBase64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Paylo $NodeCode = @" const fs = require("fs"); const path = require("path"); -const { ConfigService } = require("@nestjs/config"); +const XLSX = require("xlsx"); const payload = JSON.parse(Buffer.from("$PayloadBase64", "base64").toString("utf8")); -const { ToolsService } = require("./src/common/services/tool.service"); -const { QqbotBangDreamRendererService } = require("./src/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade"); -const { TsuguApplicationService } = require("./src/modules/qqbot/plugins/bangDream/application/bangdream-application.service"); +const { createPlugin } = require("./src/modules/qqbot/plugins/bangdream/src"); + +function createHttpError(message, statusCode) { + const error = new Error(message); + error.statusCode = statusCode; + error.response = { status: statusCode }; + return error; +} + +async function fetchWithTimeout(url, options = {}) { + const controller = new AbortController(); + const timeoutMs = options.timeoutMs || 30000; + const timer = setTimeout(() => controller.abort(), timeoutMs); + try { + return await fetch(url, { + headers: options.headers, + signal: controller.signal, + }); + } finally { + clearTimeout(timer); + } +} + +function readJsonFile(filePath) { + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +function readExcelRows(filePath) { + const workbook = XLSX.readFile(filePath); + const sheetName = workbook.SheetNames[0]; + return XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]); +} + +async function requestBuffer(url, options) { + const response = await fetchWithTimeout(url, options); + const body = Buffer.from(await response.arrayBuffer()); + if (!response.ok) { + throw createHttpError("BangDream resource request failed: " + response.status, response.status); + } + return { + body, + statusCode: response.status, + }; +} + +async function requestJson(url, options) { + const response = await fetchWithTimeout(url, options); + const text = await response.text(); + if (!response.ok) { + throw createHttpError("BangDream JSON request failed: " + response.status, response.status); + } + return { + body: JSON.parse(text), + statusCode: response.status, + }; +} (async () => { - const renderer = new QqbotBangDreamRendererService(new ConfigService({}), undefined); - const service = new TsuguApplicationService(renderer, new ToolsService()); - await service.onApplicationBootstrap(); - const result = await service.execute(payload.operationKey, payload.input); + const manifest = readJsonFile(path.join(process.cwd(), "src/modules/qqbot/plugins/bangdream/plugin.json")); + const operations = manifest.operations.map((operation) => ({ + handlerName: operation.handlerName, + key: operation.key, + })); + const plugin = createPlugin({ + io: { + getConfig: (key) => process.env[key], + readAssetFile: async (filePath) => fs.promises.readFile(filePath), + readExcelRows: async (filePath) => readExcelRows(filePath), + readJsonFile: async (filePath) => readJsonFile(filePath), + readJsonFileSync: (filePath) => readJsonFile(filePath), + requestArrayBuffer: requestBuffer, + requestJson, + sleep: async (ms) => new Promise((resolve) => setTimeout(resolve, ms)), + writeJsonFile: async (filePath, data) => { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, JSON.stringify(data)); + }, + }, + normalizeError: (error) => String(error?.message || error || "BangDream command failed"), + operations, + }); + await plugin.activate(); + const result = await plugin.executeOperation(payload.operationKey, payload.input); + if (payload.expectedImageCount > 0 && result.imageCount !== payload.expectedImageCount) { + throw new Error( + "Expected imageCount=" + payload.expectedImageCount + ", got " + result.imageCount, + ); + } const matches = [...result.replyText.matchAll(/base64:\/\/([A-Za-z0-9+/=]+)/g)]; if (matches.length === 0) throw new Error("No image CQ payload"); fs.mkdirSync(path.dirname(payload.outFile), { recursive: true }); diff --git a/sql/qqbot-init.sql b/sql/qqbot-init.sql index 591162e..b6ad2ac 100644 --- a/sql/qqbot-init.sql +++ b/sql/qqbot-init.sql @@ -756,23 +756,23 @@ ON DUPLICATE KEY UPDATE INSERT INTO `qqbot_command` (`id`, `code`, `name`, `aliases`, `prefixes`, `plugin_key`, `operation_key`, `parser_key`, `target_type`, `default_params`, `reply_template`, `error_template`, `enabled`, `priority`, `cooldown_ms`, `remark`) VALUES - (2041700000000300501, 'ff14_price', 'FF14 查价', '["查价","price","ff14price"]', '["/","!","!"]', 'ff14Market', 'ff14.market.price', 'ff14Price', 'all', '{"language":"chs","world":"中国"}', '', 'FF14 查价失败:{{error}}', 1, 0, 1500, '默认示例命令;请在账号配置中绑定后启用'), + (2041700000000300501, 'ff14_price', 'FF14 查价', '["查价","price","ff14price"]', '["/","!","!"]', 'ff14-market', 'ff14.market.price', 'ff14Price', 'all', '{"language":"chs","world":"中国"}', '', 'FF14 查价失败:{{error}}', 1, 0, 1500, '默认示例命令;请在账号配置中绑定后启用'), (2041700000000300502, 'fflogs_character', 'FFLogs 查询', '["fflogs","logs","查logs","查log"]', '["/","!","!"]', 'fflogs', 'fflogs.character.summary', 'fflogsCharacter', 'all', '{"serverRegion":"CN"}', '', 'FFLogs 查询失败:{{error}}', 1, 0, 3000, '查询 FFLogs 角色公开排名;带高难任务时返回最近10次记录;格式:/fflogs 角色名 服务器 [高难任务]'), - (2041700000000300503, 'bangdream_song', 'BangDream 查曲', '["查曲","bd","bangdream","bandori","邦邦","邦邦查歌"]', '["/","!","!"]', 'bangDream', 'bangdream.song.search', 'plain', 'all', '{}', '', 'BangDream 查曲失败:{{error}}', 1, 0, 1500, '查询 BanG Dream 歌曲信息;格式:/查曲 歌曲名 或 /查曲 歌曲ID'), - (2041700000000300504, 'bangdream_song_chart', 'BangDream 查谱面', '["查谱面","谱面","bd谱面"]', '["/","!","!"]', 'bangDream', 'bangdream.song.chart', 'plain', 'all', '{}', '', 'BangDream 查谱面失败:{{error}}', 1, 0, 1500, '查询歌曲谱面;格式:/查谱面 歌曲ID [难度]'), - (2041700000000300505, 'bangdream_song_random', 'BangDream 随机曲', '["随机曲","随机","bd随机"]', '["/","!","!"]', 'bangDream', 'bangdream.song.random', 'plain', 'all', '{}', '', 'BangDream 随机曲失败:{{error}}', 1, 0, 1500, '按关键词随机歌曲;格式:/随机曲 [关键词]'), - (2041700000000300506, 'bangdream_song_meta', 'BangDream 分数表', '["查询分数表","查分数表","查询分数榜","查分数榜","bd分数表"]', '["/","!","!"]', 'bangDream', 'bangdream.song.meta', 'plain', 'all', '{}', '', 'BangDream 分数表失败:{{error}}', 1, 0, 1500, '查询歌曲分数榜;格式:/查询分数表 [服务器]'), - (2041700000000300507, 'bangdream_card', 'BangDream 查卡', '["查卡","查卡牌","bd查卡"]', '["/","!","!"]', 'bangDream', 'bangdream.card.search', 'plain', 'all', '{}', '', 'BangDream 查卡失败:{{error}}', 1, 0, 1500, '查询卡牌信息;格式:/查卡 卡牌关键词 或 /查卡 卡牌ID'), - (2041700000000300508, 'bangdream_card_illustration', 'BangDream 查卡面', '["查卡面","查卡插画","查插画","bd卡面"]', '["/","!","!"]', 'bangDream', 'bangdream.card.illustration', 'plain', 'all', '{}', '', 'BangDream 查卡面失败:{{error}}', 1, 0, 1500, '查询卡牌插画;格式:/查卡面 卡牌ID'), - (2041700000000300509, 'bangdream_character', 'BangDream 查角色', '["查角色","bd角色"]', '["/","!","!"]', 'bangDream', 'bangdream.character.search', 'plain', 'all', '{}', '', 'BangDream 查角色失败:{{error}}', 1, 0, 1500, '查询角色信息;格式:/查角色 角色关键词 或 /查角色 角色ID'), - (2041700000000300510, 'bangdream_event', 'BangDream 查活动', '["查活动","bd活动"]', '["/","!","!"]', 'bangDream', 'bangdream.event.search', 'plain', 'all', '{}', '', 'BangDream 查活动失败:{{error}}', 1, 0, 1500, '查询活动信息;格式:/查活动 活动关键词 或 /查活动 活动ID'), - (2041700000000300511, 'bangdream_event_stage', 'BangDream 查试炼', '["查试炼","查stage","查舞台","查festival","查5v5"]', '["/","!","!"]', 'bangDream', 'bangdream.event.stage', 'plain', 'all', '{}', '', 'BangDream 查试炼失败:{{error}}', 1, 0, 1500, '查询活动试炼;格式:/查试炼 [活动ID] [-m]'), - (2041700000000300512, 'bangdream_player', 'BangDream 查玩家', '["查玩家","查询玩家","bd玩家"]', '["/","!","!"]', 'bangDream', 'bangdream.player.search', 'plain', 'all', '{}', '', 'BangDream 查玩家失败:{{error}}', 1, 0, 1500, '查询玩家信息;格式:/查玩家 玩家ID [服务器]'), - (2041700000000300513, 'bangdream_gacha', 'BangDream 查卡池', '["查卡池","bd卡池"]', '["/","!","!"]', 'bangDream', 'bangdream.gacha.search', 'plain', 'all', '{}', '', 'BangDream 查卡池失败:{{error}}', 1, 0, 1500, '查询卡池信息;格式:/查卡池 卡池ID'), - (2041700000000300514, 'bangdream_gacha_simulate', 'BangDream 抽卡模拟', '["抽卡模拟","bd抽卡"]', '["/","!","!"]', 'bangDream', 'bangdream.gacha.simulate', 'plain', 'all', '{}', '', 'BangDream 抽卡模拟失败:{{error}}', 1, 0, 3000, '模拟抽卡;格式:/抽卡模拟 [次数] [卡池ID]'), - (2041700000000300515, 'bangdream_cutoff_detail', 'BangDream ycx', '["ycx","预测线","查档线","bd档线"]', '["/","!","!"]', 'bangDream', 'bangdream.cutoff.detail', 'plain', 'all', '{}', '', 'BangDream ycx 失败:{{error}}', 1, 0, 3000, '查询指定档位预测线;格式:/ycx 档位 [活动ID] [服务器]'), - (2041700000000300516, 'bangdream_cutoff_all', 'BangDream ycxall', '["ycxall","myycx","全部档线"]', '["/","!","!"]', 'bangDream', 'bangdream.cutoff.all', 'plain', 'all', '{}', '', 'BangDream ycxall 失败:{{error}}', 1, 0, 3000, '查询所有档位预测线;格式:/ycxall [活动ID] [服务器]'), - (2041700000000300517, 'bangdream_cutoff_recent', 'BangDream lsycx', '["lsycx","历史档线","近期档线"]', '["/","!","!"]', 'bangDream', 'bangdream.cutoff.recent', 'plain', 'all', '{}', '', 'BangDream lsycx 失败:{{error}}', 1, 0, 3000, '查询同类型活动档线;格式:/lsycx 档位 [活动ID] [服务器]') + (2041700000000300503, 'bangdream_song', 'BangDream 查曲', '["查曲","bd","bangdream","bandori","邦邦","邦邦查歌"]', '["/","!","!"]', 'bangdream', 'bangdream.song.search', 'plain', 'all', '{}', '', 'BangDream 查曲失败:{{error}}', 1, 0, 1500, '查询 BanG Dream 歌曲信息;格式:/查曲 歌曲名 或 /查曲 歌曲ID'), + (2041700000000300504, 'bangdream_song_chart', 'BangDream 查谱面', '["查谱面","谱面","bd谱面"]', '["/","!","!"]', 'bangdream', 'bangdream.song.chart', 'plain', 'all', '{}', '', 'BangDream 查谱面失败:{{error}}', 1, 0, 1500, '查询歌曲谱面;格式:/查谱面 歌曲ID [难度]'), + (2041700000000300505, 'bangdream_song_random', 'BangDream 随机曲', '["随机曲","随机","bd随机"]', '["/","!","!"]', 'bangdream', 'bangdream.song.random', 'plain', 'all', '{}', '', 'BangDream 随机曲失败:{{error}}', 1, 0, 1500, '按关键词随机歌曲;格式:/随机曲 [关键词]'), + (2041700000000300506, 'bangdream_song_meta', 'BangDream 分数表', '["查询分数表","查分数表","查询分数榜","查分数榜","bd分数表"]', '["/","!","!"]', 'bangdream', 'bangdream.song.meta', 'plain', 'all', '{}', '', 'BangDream 分数表失败:{{error}}', 1, 0, 1500, '查询歌曲分数榜;格式:/查询分数表 [服务器]'), + (2041700000000300507, 'bangdream_card', 'BangDream 查卡', '["查卡","查卡牌","bd查卡"]', '["/","!","!"]', 'bangdream', 'bangdream.card.search', 'plain', 'all', '{}', '', 'BangDream 查卡失败:{{error}}', 1, 0, 1500, '查询卡牌信息;格式:/查卡 卡牌关键词 或 /查卡 卡牌ID'), + (2041700000000300508, 'bangdream_card_illustration', 'BangDream 查卡面', '["查卡面","查卡插画","查插画","bd卡面"]', '["/","!","!"]', 'bangdream', 'bangdream.card.illustration', 'plain', 'all', '{}', '', 'BangDream 查卡面失败:{{error}}', 1, 0, 1500, '查询卡牌插画;格式:/查卡面 卡牌ID'), + (2041700000000300509, 'bangdream_character', 'BangDream 查角色', '["查角色","bd角色"]', '["/","!","!"]', 'bangdream', 'bangdream.character.search', 'plain', 'all', '{}', '', 'BangDream 查角色失败:{{error}}', 1, 0, 1500, '查询角色信息;格式:/查角色 角色关键词 或 /查角色 角色ID'), + (2041700000000300510, 'bangdream_event', 'BangDream 查活动', '["查活动","bd活动"]', '["/","!","!"]', 'bangdream', 'bangdream.event.search', 'plain', 'all', '{}', '', 'BangDream 查活动失败:{{error}}', 1, 0, 1500, '查询活动信息;格式:/查活动 活动关键词 或 /查活动 活动ID'), + (2041700000000300511, 'bangdream_event_stage', 'BangDream 查试炼', '["查试炼","查stage","查舞台","查festival","查5v5"]', '["/","!","!"]', 'bangdream', 'bangdream.event.stage', 'plain', 'all', '{}', '', 'BangDream 查试炼失败:{{error}}', 1, 0, 1500, '查询活动试炼;格式:/查试炼 [活动ID] [-m]'), + (2041700000000300512, 'bangdream_player', 'BangDream 查玩家', '["查玩家","查询玩家","bd玩家"]', '["/","!","!"]', 'bangdream', 'bangdream.player.search', 'plain', 'all', '{}', '', 'BangDream 查玩家失败:{{error}}', 1, 0, 1500, '查询玩家信息;格式:/查玩家 玩家ID [服务器]'), + (2041700000000300513, 'bangdream_gacha', 'BangDream 查卡池', '["查卡池","bd卡池"]', '["/","!","!"]', 'bangdream', 'bangdream.gacha.search', 'plain', 'all', '{}', '', 'BangDream 查卡池失败:{{error}}', 1, 0, 1500, '查询卡池信息;格式:/查卡池 卡池ID'), + (2041700000000300514, 'bangdream_gacha_simulate', 'BangDream 抽卡模拟', '["抽卡模拟","bd抽卡"]', '["/","!","!"]', 'bangdream', 'bangdream.gacha.simulate', 'plain', 'all', '{}', '', 'BangDream 抽卡模拟失败:{{error}}', 1, 0, 3000, '模拟抽卡;格式:/抽卡模拟 [次数] [卡池ID]'), + (2041700000000300515, 'bangdream_cutoff_detail', 'BangDream ycx', '["ycx","预测线","查档线","bd档线"]', '["/","!","!"]', 'bangdream', 'bangdream.cutoff.detail', 'plain', 'all', '{}', '', 'BangDream ycx 失败:{{error}}', 1, 0, 3000, '查询指定档位预测线;格式:/ycx 档位 [活动ID] [服务器]'), + (2041700000000300516, 'bangdream_cutoff_all', 'BangDream ycxall', '["ycxall","myycx","全部档线"]', '["/","!","!"]', 'bangdream', 'bangdream.cutoff.all', 'plain', 'all', '{}', '', 'BangDream ycxall 失败:{{error}}', 1, 0, 3000, '查询所有档位预测线;格式:/ycxall [活动ID] [服务器]'), + (2041700000000300517, 'bangdream_cutoff_recent', 'BangDream lsycx', '["lsycx","历史档线","近期档线"]', '["/","!","!"]', 'bangdream', 'bangdream.cutoff.recent', 'plain', 'all', '{}', '', 'BangDream lsycx 失败:{{error}}', 1, 0, 3000, '查询同类型活动档线;格式:/lsycx 档位 [活动ID] [服务器]') ON DUPLICATE KEY UPDATE `code` = VALUES(`code`), `name` = VALUES(`name`), diff --git a/sql/refactor-v3/01-seed-core.sql b/sql/refactor-v3/01-seed-core.sql index cf9cc7e..abdcf83 100644 --- a/sql/refactor-v3/01-seed-core.sql +++ b/sql/refactor-v3/01-seed-core.sql @@ -274,21 +274,178 @@ INSERT INTO qqbot_command ( plugin_key, enabled, cooldown_seconds -) VALUES ( - 1000000000000000201, - 'bangdream.song.search', - 'bangdream_song', - 'bd', - 'BangDream 查歌', - '["查曲","bd","bangdream","bandori","邦邦","邦邦查歌"]', - 'bangdream', - 1, - 2 -) ON DUPLICATE KEY UPDATE +) VALUES + ( + 1000000000000000201, + 'bangdream.song.search', + 'bangdream_song', + 'bd', + 'BangDream 查歌', + '["查曲","bd","bangdream","bandori","邦邦","邦邦查歌"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000202, + 'bangdream.song.chart', + 'bangdream_song_chart', + 'bangdream_song_chart', + 'BangDream 查谱面', + '["查谱面","谱面","bd谱面"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000203, + 'bangdream.song.random', + 'bangdream_song_random', + 'bangdream_song_random', + 'BangDream 随机曲', + '["随机曲","随机","bd随机"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000204, + 'bangdream.song.meta', + 'bangdream_song_meta', + 'bangdream_song_meta', + 'BangDream 分数表', + '["查询分数表","查分数表","查询分数榜","查分数榜","bd分数表"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000205, + 'bangdream.card.search', + 'bangdream_card', + 'bangdream_card', + 'BangDream 查卡', + '["查卡","查卡牌","bd查卡"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000206, + 'bangdream.card.illustration', + 'bangdream_card_illustration', + 'bangdream_card_illustration', + 'BangDream 查卡面', + '["查卡面","查卡插画","查插画","bd卡面"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000207, + 'bangdream.character.search', + 'bangdream_character', + 'bangdream_character', + 'BangDream 查角色', + '["查角色","bd角色"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000208, + 'bangdream.event.search', + 'bangdream_event', + 'bangdream_event', + 'BangDream 查活动', + '["查活动","bd活动"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000209, + 'bangdream.event.stage', + 'bangdream_event_stage', + 'bangdream_event_stage', + 'BangDream 查试炼', + '["查试炼","查stage","查舞台","查festival","查5v5"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000210, + 'bangdream.player.search', + 'bangdream_player', + 'bangdream_player', + 'BangDream 查玩家', + '["查玩家","查询玩家","bd玩家"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000211, + 'bangdream.gacha.search', + 'bangdream_gacha', + 'bangdream_gacha', + 'BangDream 查卡池', + '["查卡池","bd卡池"]', + 'bangdream', + 1, + 2 + ), + ( + 1000000000000000212, + 'bangdream.gacha.simulate', + 'bangdream_gacha_simulate', + 'bangdream_gacha_simulate', + 'BangDream 抽卡模拟', + '["抽卡模拟","bd抽卡"]', + 'bangdream', + 1, + 3 + ), + ( + 1000000000000000213, + 'bangdream.cutoff.detail', + 'bangdream_cutoff_detail', + 'bangdream_cutoff_detail', + 'BangDream ycx', + '["ycx","预测线","查档线","bd档线"]', + 'bangdream', + 1, + 3 + ), + ( + 1000000000000000214, + 'bangdream.cutoff.all', + 'bangdream_cutoff_all', + 'bangdream_cutoff_all', + 'BangDream ycxall', + '["ycxall","myycx","全部档线"]', + 'bangdream', + 1, + 3 + ), + ( + 1000000000000000215, + 'bangdream.cutoff.recent', + 'bangdream_cutoff_recent', + 'bangdream_cutoff_recent', + 'BangDream lsycx', + '["lsycx","历史档线","近期档线"]', + 'bangdream', + 1, + 3 + ) +ON DUPLICATE KEY UPDATE operation_key = VALUES(operation_key), code = VALUES(code), name = VALUES(name), aliases = VALUES(aliases), plugin_key = VALUES(plugin_key), enabled = VALUES(enabled), - cooldown_seconds = VALUES(cooldown_seconds); + cooldown_seconds = VALUES(cooldown_seconds), + is_deleted = 0; diff --git a/sql/refactor-v3/99-verify.sql b/sql/refactor-v3/99-verify.sql index d76eab5..858a5b8 100644 --- a/sql/refactor-v3/99-verify.sql +++ b/sql/refactor-v3/99-verify.sql @@ -31,6 +31,13 @@ WHERE command_key = 'bangdream_song' AND plugin_key = 'bangdream' AND enabled = 1; +SELECT 'seed_qqbot_command_bangdream_all' AS check_name, COUNT(*) AS matched_rows +FROM qqbot_command +WHERE plugin_key = 'bangdream' + AND operation_key LIKE 'bangdream.%' + AND enabled = 1 + AND is_deleted = 0; + SELECT 'index_admin_user_username' AS check_name, COUNT(*) AS matched_rows FROM information_schema.statistics WHERE table_schema = DATABASE() diff --git a/src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts b/src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts index c225596..9016d9c 100644 --- a/src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts +++ b/src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts @@ -9,8 +9,8 @@ import { isQqbotFf14WorldName, QQBOT_FF14_MARKET_DICT_CODES, splitQqbotFf14WorldPath, -} from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds'; -import type { QqbotFf14MarketCatalog } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.types'; +} from '@/modules/qqbot/plugins/ff14-market/src'; +import type { QqbotFf14MarketCatalog } from '@/modules/qqbot/plugins/ff14-market/src'; import type { QqbotPluginExecutionInput } from '@/modules/qqbot/core/domain/plugin-execution.port'; @Injectable() diff --git a/src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts b/src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts index 69f6012..87ae6b7 100644 --- a/src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts +++ b/src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts @@ -1,20 +1,52 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { existsSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { formatKtDateTime, throwVbenError } from '@/common'; import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; -import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; +import { QqbotSendService } from '@/modules/qqbot/core/application/send/qqbot-send.service'; import type { QqbotEventPluginDefinition, QqbotNormalizedMessage, QqbotPluginHealth, QqbotPluginOperationSummary, } from '@/modules/qqbot/core/contract/qqbot.types'; +import { + parseQqbotPluginManifest, + type QqbotPluginManifest, +} from '@/modules/qqbot/plugin-platform/domain/manifest'; +import { createPlugin as createRepeaterPlugin } from '@/modules/qqbot/plugins/repeater/src'; @Injectable() export class QqbotEventPluginRegistryService { + private readonly logger = new Logger(QqbotEventPluginRegistryService.name); + private readonly repeaterPlugin: ReturnType; + constructor( private readonly accountService: QqbotAccountService, - private readonly repeaterPlugin: QqbotRepeaterPluginService, - ) {} + private readonly configService: ConfigService, + private readonly sendService: QqbotSendService, + ) { + const manifest = this.loadManifest('repeater'); + this.repeaterPlugin = createRepeaterPlugin({ + host: { + bindEventPlugin: (selfId, pluginKey) => + this.accountService + .bindEventPlugin(selfId, pluginKey) + .then(() => undefined), + getBoundEventPluginKeys: (selfId) => + this.accountService.getBoundEventPluginKeys(selfId), + getConfig: (key) => this.configService.get(key), + sendText: (input) => this.sendService.sendText(input as any), + unbindEventPlugin: (selfId, pluginKey) => + this.accountService + .unbindEventPlugin(selfId, pluginKey) + .then(() => undefined), + warn: (message) => this.logger.warn(message), + }, + manifest, + }); + } listDefinitions(pluginKey?: string): QqbotEventPluginDefinition[] { return this.getDefinitions(pluginKey); @@ -95,4 +127,25 @@ export class QqbotEventPluginRegistryService { ? definitions.filter((definition) => definition.key === pluginKey) : definitions; } + + private loadManifest(pluginKey: string): QqbotPluginManifest { + const pluginRoot = this.resolvePluginRoot(pluginKey); + return parseQqbotPluginManifest( + readJsonFile(join(pluginRoot, 'plugin.json')), + { pluginRoot }, + ); + } + + private resolvePluginRoot(pluginKey: string) { + const sourceRoot = join( + process.cwd(), + `src/modules/qqbot/plugins/${pluginKey}`, + ); + if (existsSync(join(sourceRoot, 'plugin.json'))) return sourceRoot; + return join(__dirname, `../../../plugins/${pluginKey}`); + } +} + +function readJsonFile(filePath: string) { + return JSON.parse(readFileSync(filePath, 'utf8')) as T; } diff --git a/src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service.ts b/src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service.ts index 8fb2a60..ccf145d 100644 --- a/src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service.ts +++ b/src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service.ts @@ -1,8 +1,10 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; -import { formatKtDateTime, throwVbenError } from '@/common'; -import { QqbotBangDreamPluginService } from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin'; -import { QqbotFf14MarketPluginService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin'; -import { QqbotFflogsPluginService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin'; +import { ConfigService } from '@nestjs/config'; +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import * as XLSX from 'xlsx'; +import { formatKtDateTime, throwVbenError, ToolsService } from '@/common'; +import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; import type { QqbotIntegrationPlugin, QqbotPluginHealth, @@ -10,6 +12,16 @@ import type { QqbotPluginOperationSummary, QqbotPluginSummary, } from '@/modules/qqbot/core/contract/qqbot.types'; +import { + parseQqbotPluginManifest, + type QqbotPluginManifest, +} from '@/modules/qqbot/plugin-platform/domain/manifest'; +import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; +import { createPlugin as createBangDreamPlugin } from '@/modules/qqbot/plugins/bangdream/src'; +import type { BangDreamManifestOperation } from '@/modules/qqbot/plugins/bangdream/src'; +import type { BangDreamRuntimeIo } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; +import { createPlugin as createFf14MarketPlugin } from '@/modules/qqbot/plugins/ff14-market/src'; +import { createPlugin as createFflogsPlugin } from '@/modules/qqbot/plugins/fflogs/src'; @Injectable() export class QqbotPluginRegistryService implements OnModuleInit { @@ -17,15 +29,16 @@ export class QqbotPluginRegistryService implements OnModuleInit { private readonly plugins = new Map(); constructor( - private readonly bangDreamPlugin: QqbotBangDreamPluginService, - private readonly ff14MarketPlugin: QqbotFf14MarketPluginService, - private readonly fflogsPlugin: QqbotFflogsPluginService, + private readonly configService: ConfigService, + private readonly dictService: DictService, + private readonly httpClient: QqbotPluginHttpClientService, + private readonly toolsService: ToolsService, ) {} onModuleInit() { - this.register(this.bangDreamPlugin.getPlugin()); - this.register(this.ff14MarketPlugin.getPlugin()); - this.register(this.fflogsPlugin.getPlugin()); + this.register(this.createBangDreamPlugin()); + this.register(this.createFf14MarketPlugin()); + this.register(this.createFflogsPlugin()); } register(plugin: QqbotIntegrationPlugin) { @@ -134,4 +147,126 @@ export class QqbotPluginRegistryService implements OnModuleInit { this.plugins.get(this.pluginAliases.get(pluginKey) || '') ); } + + private createBangDreamPlugin(): QqbotIntegrationPlugin { + const manifest = this.loadManifest('bangdream'); + return createBangDreamPlugin({ + configReader: { + get: (key) => this.configService.get(key), + }, + description: manifest.description, + dictionaryReader: { + getDictItemsByKey: (dictCode) => + this.dictService.getDictItemsByKey(dictCode), + }, + io: this.createBangDreamRuntimeIo(), + legacyAliases: manifest.legacyAliases, + name: manifest.name, + normalizeError: (error) => + this.toolsService.getErrorMessage(error, 'BangDream 命令执行失败'), + operations: manifest.operations.map((operation) => ({ + description: operation.description, + handlerName: operation.handlerName, + inputSchema: operation.inputSchema, + key: operation.key, + name: operation.name, + outputSchema: operation.outputSchema, + })) as BangDreamManifestOperation[], + pluginKey: manifest.pluginKey, + version: manifest.version, + }) as QqbotIntegrationPlugin; + } + + private createFf14MarketPlugin(): QqbotIntegrationPlugin { + const manifest = this.loadManifest('ff14-market'); + return createFf14MarketPlugin({ + host: { + getConfig: (key) => this.configService.get(key), + getDictItemsByKey: (dictCode) => + this.dictService.getDictItemsByKey(dictCode), + relationTree: (input) => this.dictService.relationTree(input), + requestJson: (options) => this.httpClient.requestJson(options), + }, + manifest, + normalizeError: (error, fallback) => + this.toolsService.getErrorMessage(error, fallback), + }) as QqbotIntegrationPlugin; + } + + private createFflogsPlugin(): QqbotIntegrationPlugin { + const manifest = this.loadManifest('fflogs'); + return createFflogsPlugin({ + host: { + getConfig: (key) => this.configService.get(key), + getDictByKey: (dictCode) => this.dictService.getDictByKey(dictCode), + requestJson: (options) => this.httpClient.requestJson(options), + }, + manifest, + normalizeError: (error, fallback) => + this.toolsService.getErrorMessage(error, fallback), + }) as QqbotIntegrationPlugin; + } + + private createBangDreamRuntimeIo(): BangDreamRuntimeIo { + return { + getConfig: (key) => this.configService.get(key), + readAssetFile: async (filePath) => readFileSync(filePath), + readExcelRows: async (filePath) => readExcelRows(filePath), + readJsonFile: async (filePath) => readJsonFile(filePath), + readJsonFileSync: (filePath) => readJsonFile(filePath), + requestArrayBuffer: async (url, options) => ({ + body: await this.httpClient.requestBuffer({ + context: 'BangDream 资源下载', + failureMessage: (statusCode) => + `BangDream 资源下载失败:${statusCode}`, + timeoutMessage: 'BangDream 资源下载超时', + timeoutMs: options?.timeoutMs, + url, + }), + }), + requestJson: async (url, options) => ({ + body: await this.httpClient.requestJson({ + context: 'BangDream 数据接口', + failureMessage: (statusCode) => + `BangDream 数据接口失败:${statusCode}`, + invalidJsonMessage: 'BangDream 数据接口返回不是合法 JSON', + timeoutMessage: 'BangDream 数据接口请求超时', + timeoutMs: options?.timeoutMs, + url, + }), + }), + sleep: async (ms) => + await new Promise((resolve) => setTimeout(resolve, ms)), + writeJsonFile: async (filePath, data) => + writeFileSync(filePath, JSON.stringify(data)), + }; + } + + private loadManifest(pluginKey: string): QqbotPluginManifest { + const pluginRoot = this.resolvePluginRoot(pluginKey); + return parseQqbotPluginManifest( + readJsonFile(join(pluginRoot, 'plugin.json')), + { pluginRoot }, + ); + } + + private resolvePluginRoot(pluginKey: string) { + const sourceRoot = join( + process.cwd(), + `src/modules/qqbot/plugins/${pluginKey}`, + ); + if (existsSync(join(sourceRoot, 'plugin.json'))) return sourceRoot; + return join(__dirname, `../../../plugins/${pluginKey}`); + } +} + +function readJsonFile(filePath: string) { + return JSON.parse(readFileSync(filePath, 'utf8')) as T; +} + +function readExcelRows>(filePath: string) { + const workbook = XLSX.readFile(filePath); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + return XLSX.utils.sheet_to_json(worksheet); } diff --git a/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts index 256c769..3db973b 100644 --- a/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts +++ b/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts @@ -28,6 +28,57 @@ export class QqbotPluginHttpClientService { } } + requestBuffer(input: QqbotPluginHttpClientRequest): Promise { + const url = input.url instanceof URL ? input.url : new URL(input.url); + const method = input.method || 'GET'; + const timeoutMs = input.timeoutMs || 8000; + const context = input.context || '插件 HTTP 接口'; + + return new Promise((resolve, reject) => { + const client = url.protocol === 'http:' ? http : https; + const request = client.request( + url, + { + headers: { + Accept: '*/*', + 'User-Agent': 'kt-template-online-api/qqbot-plugin', + ...(input.headers || {}), + }, + method, + timeout: timeoutMs, + }, + (response) => { + const chunks: Buffer[] = []; + response.on('data', (chunk) => { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + }); + response.on('end', () => { + const statusCode = response.statusCode || 500; + if (statusCode >= 400) { + reject( + createPluginHttpError( + input.failureMessage?.(statusCode) || + `${context}请求失败:${statusCode}`, + statusCode, + ), + ); + return; + } + resolve(Buffer.concat(chunks)); + }); + }, + ); + request.on('timeout', () => { + request.destroy( + new Error(input.timeoutMessage || `${context}请求超时`), + ); + }); + request.on('error', reject); + if (input.body) request.write(input.body); + request.end(); + }); + } + requestText(input: QqbotPluginHttpClientRequest): Promise { const url = input.url instanceof URL ? input.url : new URL(input.url); const method = input.method || 'GET'; @@ -57,9 +108,10 @@ export class QqbotPluginHttpClientService { const statusCode = response.statusCode || 500; if (statusCode >= 400) { reject( - new Error( + createPluginHttpError( input.failureMessage?.(statusCode) || `${context}请求失败:${statusCode}`, + statusCode, ), ); return; @@ -79,3 +131,12 @@ export class QqbotPluginHttpClientService { }); } } + +function createPluginHttpError(message: string, statusCode: number) { + return Object.assign(new Error(message), { + response: { + status: statusCode, + }, + statusCode, + }); +} diff --git a/src/modules/qqbot/plugin-platform/plugin-platform.module.ts b/src/modules/qqbot/plugin-platform/plugin-platform.module.ts index 996a038..ac99d54 100644 --- a/src/modules/qqbot/plugin-platform/plugin-platform.module.ts +++ b/src/modules/qqbot/plugin-platform/plugin-platform.module.ts @@ -5,15 +5,6 @@ import { AdminAuthGuardModule } from '@/modules/admin/identity/auth/admin-auth-g import { DictModule } from '@/modules/admin/platform-config/dict/dict.module'; import { QQBOT_PLUGIN_EXECUTION_PORT } from '@/modules/qqbot/core/domain/plugin-execution.port'; import { QqbotCoreModule } from '@/modules/qqbot/core/qqbot-core.module'; -import { QqbotBangDreamClientService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-client.service'; -import { TsuguApplicationService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service'; -import { QqbotBangDreamRendererService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade'; -import { QqbotBangDreamPluginService } from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin'; -import { QqbotFf14ClientService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service'; -import { QqbotFf14MarketPluginService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin'; -import { QqbotFflogsClientService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service'; -import { QqbotFflogsPluginService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin'; -import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; import { QqbotPluginArgumentParserService } from './application/argument/qqbot-plugin-argument-parser.service'; import { QqbotEventPluginRegistryService } from './application/registry/qqbot-event-plugin-registry.service'; import { QqbotPluginRegistryService } from './application/registry/qqbot-plugin-registry.service'; @@ -39,14 +30,7 @@ import { QqbotPluginHttpClientService } from './infrastructure/integration/sdk'; TypeOrmModule.forFeature([...QQBOT_PLUGIN_PLATFORM_ENTITIES]), ], providers: [ - QqbotBangDreamClientService, - QqbotBangDreamPluginService, - QqbotBangDreamRendererService, QqbotEventPluginRegistryService, - QqbotFf14ClientService, - QqbotFf14MarketPluginService, - QqbotFflogsClientService, - QqbotFflogsPluginService, QqbotPluginArgumentParserService, QqbotPluginExecutionAdapter, { @@ -56,8 +40,6 @@ import { QqbotPluginHttpClientService } from './infrastructure/integration/sdk'; QqbotPluginHttpClientService, QqbotPluginPlatformService, QqbotPluginRegistryService, - QqbotRepeaterPluginService, - TsuguApplicationService, ], }) export class QqbotPluginPlatformModule {} diff --git a/src/modules/qqbot/plugins/bangDream/application/bangdream-application.service.ts b/src/modules/qqbot/plugins/bangDream/application/bangdream-application.service.ts deleted file mode 100644 index 909b697..0000000 --- a/src/modules/qqbot/plugins/bangDream/application/bangdream-application.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Injectable, OnApplicationBootstrap } from '@nestjs/common'; -import { ToolsService } from '@/common'; -import type { - QqbotBangDreamCommandInput, - QqbotBangDreamCommandOutput, - QqbotBangDreamOperationKey, -} from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.types'; -import { waitForMainDataReady } from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { - createTsuguLogHook, - TsuguHookRegistry, -} from '@/modules/qqbot/plugins/bangDream/hook/hook-registry'; -import { getBangDreamOperationDefinition } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; -import { TsuguOperationPipeline } from '@/modules/qqbot/plugins/bangDream/application/operation-pipeline'; -import { QqbotBangDreamRendererService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade'; - -@Injectable() -export class TsuguApplicationService implements OnApplicationBootstrap { - private readonly hookRegistry = new TsuguHookRegistry([createTsuguLogHook()]); - private readonly operationPipeline: TsuguOperationPipeline; - - constructor( - private readonly rendererService: QqbotBangDreamRendererService, - private readonly toolsService: ToolsService, - ) { - this.operationPipeline = new TsuguOperationPipeline({ - executeHandler: async (handlerName, input) => - await this.rendererService.executeOperationHandler(handlerName, input), - hookRegistry: this.hookRegistry, - normalizeError: (error) => - this.toolsService.getErrorMessage(error, 'BangDream 命令执行失败'), - resolveOperation: (operationKey) => - getBangDreamOperationDefinition(operationKey), - waitForReady: async () => await waitForMainDataReady(), - }); - } - - async onApplicationBootstrap() { - await this.rendererService.refreshDictionaryCache(); - } - - async checkHealth() { - return await this.rendererService.checkHealth(); - } - - async execute( - operationKey: QqbotBangDreamOperationKey, - input: QqbotBangDreamCommandInput, - ): Promise { - return await this.operationPipeline.run(operationKey, input); - } -} diff --git a/src/modules/qqbot/plugins/bangDream/application/bangdream-client.service.ts b/src/modules/qqbot/plugins/bangDream/application/bangdream-client.service.ts deleted file mode 100644 index c547b68..0000000 --- a/src/modules/qqbot/plugins/bangDream/application/bangdream-client.service.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import type { - QqbotBangDreamCommandInput, - QqbotBangDreamOperationKey, - QqbotBangDreamSongSearchInput, - QqbotBangDreamSongSummary, -} from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.types'; -import { TsuguApplicationService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service'; - -@Injectable() -export class QqbotBangDreamClientService { - constructor( - private readonly tsuguApplicationService: TsuguApplicationService, - ) {} - - async checkHealth() { - return this.tsuguApplicationService.checkHealth(); - } - - async execute( - operationKey: QqbotBangDreamOperationKey, - input: QqbotBangDreamCommandInput, - ) { - return await this.tsuguApplicationService.execute(operationKey, input); - } - - async searchSong( - params: QqbotBangDreamSongSearchInput, - ): Promise { - return await this.tsuguApplicationService.execute( - 'bangdream.song.search', - params, - ); - } -} diff --git a/src/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade.ts b/src/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade.ts deleted file mode 100644 index fcadf00..0000000 --- a/src/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade.ts +++ /dev/null @@ -1,555 +0,0 @@ -import { Injectable, Optional } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { getPresentEvent } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { - Gacha, - getPresentGachaList, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { - BANGDREAM_GACHA_DEFAULT_SPIN_COUNT, - isBirthdayGachaType, -} from '@/modules/qqbot/plugins/bangDream/policy/gacha.policy'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import mainAPI, { - waitForMainDataReady, -} from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { - fuzzySearch, - type FuzzySearchResult, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; -import { drawCardDetail } from '@/modules/qqbot/plugins/bangDream/card/card-detail.renderer'; -import { drawCardList } from '@/modules/qqbot/plugins/bangDream/card/card-search.renderer'; -import { drawCharacterDetail } from '@/modules/qqbot/plugins/bangDream/character/character-detail.renderer'; -import { drawCharacterList } from '@/modules/qqbot/plugins/bangDream/character/character-search.renderer'; -import { drawCutoffAll } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-all.renderer'; -import { drawCutoffDetail } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-detail.renderer'; -import { drawCutoffEventTop } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.renderer'; -import { drawCutoffListOfRecentEvent } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-recent.renderer'; -import { drawEventDetail } from '@/modules/qqbot/plugins/bangDream/event/event-detail.renderer'; -import { drawEventList } from '@/modules/qqbot/plugins/bangDream/event/event-search.renderer'; -import { drawEventStage } from '@/modules/qqbot/plugins/bangDream/event/event-stage.renderer'; -import { drawGachaDetail } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-detail.renderer'; -import { drawRandomGacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.renderer'; -import { drawPlayerDetail } from '@/modules/qqbot/plugins/bangDream/player/player-detail.renderer'; -import { drawSongChart } from '@/modules/qqbot/plugins/bangDream/song/song-chart.renderer'; -import { drawSongDetail } from '@/modules/qqbot/plugins/bangDream/song/song-detail.renderer'; -import { drawSongList } from '@/modules/qqbot/plugins/bangDream/song/song-search.renderer'; -import { drawSongMetaList } from '@/modules/qqbot/plugins/bangDream/song/song-meta.renderer'; -import { drawSongRandom } from '@/modules/qqbot/plugins/bangDream/song/song-random.renderer'; -import { - BangDreamDictionaryLoader, - type BangDreamDictionaryItem, -} from '@/modules/qqbot/plugins/bangDream/dictionary/dictionary-loader'; -import type { QqbotBangDreamOperationHandlerName } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; -import { - BANGDREAM_TSUGU_ENV_KEYS, - normalizeBangDreamBoolean, - splitBangDreamOptionList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; -import type { - QqbotBangDreamCommandInput, - QqbotBangDreamCommandOutput, - QqbotBangDreamOperationKey, -} from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.types'; - -const SOURCE_NAME = 'Tsugu BangDream Bot 内置源码'; - -@Injectable() -export class QqbotBangDreamRendererService { - private readonly dictionaryLoader = new BangDreamDictionaryLoader(); - - constructor( - private readonly configService: ConfigService, - @Optional() - private readonly dictService?: DictService, - ) {} - - async refreshDictionaryCache() { - await this.dictionaryLoader.refresh((dictCode) => - this.fetchDictionaryItems(dictCode), - ); - } - - async checkHealth() { - await waitForMainDataReady(); - const data = mainAPI as { cards?: unknown; songs?: unknown }; - if (!data.songs || !data.cards) { - throw new Error('Tsugu 数据配置未加载'); - } - fuzzySearch('夏祭り'); - return true; - } - - async executeOperationHandler( - handlerName: QqbotBangDreamOperationHandlerName, - input: QqbotBangDreamCommandInput, - ) { - const handler = this[handlerName]; - if (typeof handler !== 'function') { - throw new Error(`BangDream 插件能力未绑定执行器:${handlerName}`); - } - return await ( - handler as ( - input: QqbotBangDreamCommandInput, - ) => Promise - ).call(this, input); - } - - async searchSong(input: QqbotBangDreamCommandInput) { - const query = this.requireText(input, '请提供歌曲名或歌曲 ID'); - const options = this.getRenderOptions(input); - const images = this.isInteger(query) - ? await drawSongDetail( - new Song(Number(query)), - options.displayedServerList, - options.compress, - ) - : await this.drawFuzzyResult(query, (matches) => - drawSongList(matches, options.displayedServerList, options.compress), - ); - return this.toImageReply('bangdream.song.search', query, images); - } - - async searchCard(input: QqbotBangDreamCommandInput) { - const query = this.requireText(input, '请提供卡牌关键词或卡牌 ID'); - const options = this.getRenderOptions(input); - const images = this.isInteger(query) - ? await drawCardDetail( - Number(query), - options.displayedServerList, - options.useEasyBG, - options.compress, - ) - : await this.drawFuzzyResult(query, (matches) => - drawCardList(matches, options.displayedServerList, options.compress), - ); - return this.toImageReply('bangdream.card.search', query, images); - } - - async getCardIllustration(input: QqbotBangDreamCommandInput) { - const cardId = this.requireNumber( - input.cardId, - this.firstToken(input), - '请提供卡牌 ID', - ); - const card = new Card(cardId); - if (!card.isExist) { - return this.toImageReply('bangdream.card.illustration', `${cardId}`, [ - '错误: 该卡不存在', - ]); - } - - const images: Array = []; - for (const trainingStatus of card.getTrainingStatusList()) { - images.push(await card.getCardIllustrationImageBuffer(trainingStatus)); - } - return this.toImageReply( - 'bangdream.card.illustration', - `${cardId}`, - images, - ); - } - - async searchCharacter(input: QqbotBangDreamCommandInput) { - const query = this.requireText(input, '请提供角色关键词或角色 ID'); - const options = this.getRenderOptions(input); - const images = this.isInteger(query) - ? await drawCharacterDetail( - Number(query), - options.displayedServerList, - options.compress, - ) - : await this.drawFuzzyResult(query, (matches) => - drawCharacterList( - matches, - options.displayedServerList, - options.compress, - ), - ); - return this.toImageReply('bangdream.character.search', query, images); - } - - async searchEvent(input: QqbotBangDreamCommandInput) { - const query = this.requireText(input, '请提供活动关键词或活动 ID'); - const options = this.getRenderOptions(input); - const images = this.isInteger(query) - ? await drawEventDetail( - Number(query), - options.displayedServerList, - options.useEasyBG, - options.compress, - ) - : await this.drawFuzzyResult(query, (matches) => - drawEventList(matches, options.displayedServerList, options.compress), - ); - return this.toImageReply('bangdream.event.search', query, images); - } - - async searchPlayer(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input); - const playerId = this.requireNumber( - input.playerId, - tokens[0], - '请提供玩家 ID', - ); - const server = this.pickMainServer(input, tokens.slice(1)); - const options = this.getRenderOptions(input); - return this.toImageReply( - 'bangdream.player.search', - `${playerId}`, - await drawPlayerDetail( - playerId, - server, - options.useEasyBG, - options.compress, - ), - ); - } - - async getSongChart(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input); - const songId = this.requireNumber(input.songId, tokens[0], '请提供歌曲 ID'); - const difficulty = this.pickDifficulty( - input.difficulty ?? input.difficultyText ?? tokens.slice(1).join(' '), - ); - const options = this.getRenderOptions(input); - return this.toImageReply( - 'bangdream.song.chart', - `${songId}${difficulty === undefined ? '' : ` ${difficulty}`}`, - await drawSongChart( - songId, - difficulty ?? 3, - options.displayedServerList, - options.compress, - ), - ); - } - - async randomSong(input: QqbotBangDreamCommandInput) { - const query = this.pickText(input); - const options = this.getRenderOptions(input); - const matches = query ? fuzzySearch(query) : {}; - return this.toImageReply( - 'bangdream.song.random', - query || '随机曲', - await drawSongRandom( - matches, - [options.mainServer], - true, - options.compress, - ), - ); - } - - async getSongMeta(input: QqbotBangDreamCommandInput) { - const mainServer = this.pickMainServer(input, this.getTokens(input)); - const options = this.getRenderOptions({ ...input, mainServer }); - return this.toImageReply( - 'bangdream.song.meta', - Server[mainServer], - await drawSongMetaList(mainServer, options.compress), - ); - } - - async getEventStage(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input).filter((item) => item !== '-m'); - const mainServer = this.pickMainServer(input, tokens); - const eventId = - this.optionalNumber(input.eventId) ?? - this.firstNumber(tokens) ?? - getPresentEvent(mainServer).eventId; - const meta = this.normalizeBoolean( - input.meta, - this.getTokens(input).includes('-m'), - ); - const options = this.getRenderOptions({ ...input, mainServer }); - return this.toImageReply( - 'bangdream.event.stage', - `${eventId}`, - await drawEventStage(eventId, mainServer, meta, options.compress), - ); - } - - async searchGacha(input: QqbotBangDreamCommandInput) { - const gachaId = this.requireNumber( - input.gachaId, - this.firstToken(input), - '请提供卡池 ID', - ); - const options = this.getRenderOptions(input); - return this.toImageReply( - 'bangdream.gacha.search', - `${gachaId}`, - await drawGachaDetail( - gachaId, - options.displayedServerList, - options.useEasyBG, - options.compress, - ), - ); - } - - async getCutoffDetail(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input); - const tier = this.requireNumber(input.tier, tokens[0], '请提供档位'); - const mainServer = this.pickMainServer(input, tokens.slice(1)); - const eventId = - this.optionalNumber(input.eventId) ?? - this.firstNumber(tokens.slice(1)) ?? - getPresentEvent(mainServer).eventId; - const options = this.getRenderOptions({ ...input, mainServer }); - const images = - tier === 10 - ? await drawCutoffEventTop(eventId, mainServer, options.compress) - : await drawCutoffDetail(eventId, tier, mainServer, options.compress); - return this.toImageReply( - 'bangdream.cutoff.detail', - `${tier} ${eventId}`, - images, - ); - } - - async getCutoffAll(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input); - const mainServer = this.pickMainServer(input, tokens); - const eventId = - this.optionalNumber(input.eventId) ?? - this.firstNumber(tokens) ?? - getPresentEvent(mainServer).eventId; - const options = this.getRenderOptions({ ...input, mainServer }); - return this.toImageReply( - 'bangdream.cutoff.all', - `${eventId}`, - await drawCutoffAll(eventId, mainServer, options.compress), - ); - } - - async getCutoffRecent(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input); - const tier = this.requireNumber(input.tier, tokens[0], '请提供档位'); - const mainServer = this.pickMainServer(input, tokens.slice(1)); - const eventId = - this.optionalNumber(input.eventId) ?? - this.firstNumber(tokens.slice(1)) ?? - getPresentEvent(mainServer).eventId; - const options = this.getRenderOptions({ ...input, mainServer }); - return this.toImageReply( - 'bangdream.cutoff.recent', - `${tier} ${eventId}`, - await drawCutoffListOfRecentEvent( - eventId, - tier, - mainServer, - options.compress, - ), - ); - } - - async simulateGacha(input: QqbotBangDreamCommandInput) { - const tokens = this.getTokens(input); - const mainServer = this.pickMainServer(input, tokens); - const times = - this.optionalNumber(input.times) ?? - this.firstNumber(tokens) ?? - BANGDREAM_GACHA_DEFAULT_SPIN_COUNT; - const gachaId = - this.optionalNumber(input.gachaId) ?? this.secondNumber(tokens); - const options = this.getRenderOptions({ ...input, mainServer }); - const gacha = gachaId - ? new Gacha(gachaId) - : await this.pickPresentGacha(mainServer); - return this.toImageReply( - 'bangdream.gacha.simulate', - `${times}${gachaId ? ` ${gachaId}` : ''}`, - await drawRandomGacha(gacha, times, options.compress), - ); - } - - private async pickPresentGacha(mainServer: Server) { - const gachaList = await getPresentGachaList(mainServer); - const gacha = gachaList.find((item) => !isBirthdayGachaType(item.type)); - if (!gacha) throw new Error('错误: 该服务器没有正在进行的卡池'); - return gacha; - } - - private async drawFuzzyResult( - query: string, - render: (matches: FuzzySearchResult) => Promise>, - ) { - const matches = fuzzySearch(query); - if (Object.keys(matches).length === 0) { - return ['错误: 没有有效的关键词']; - } - return await render(matches); - } - - private toImageReply( - operationKey: QqbotBangDreamOperationKey, - query: string, - list: Array, - ): QqbotBangDreamCommandOutput { - const images = list.filter((item): item is Buffer => Buffer.isBuffer(item)); - if (images.length === 0) { - const message = - list.find((item): item is string => typeof item === 'string') || - 'Tsugu 未返回图片'; - throw new Error(message); - } - return { - imageCount: images.length, - operationKey, - query, - replyText: images - .map((item) => `[CQ:image,file=base64://${item.toString('base64')}]`) - .join('\n'), - source: SOURCE_NAME, - }; - } - - private getRenderOptions( - input: QqbotBangDreamCommandInput, - defaults: { useEasyBG?: boolean } = {}, - ) { - return { - compress: normalizeBangDreamBoolean( - input.compress, - normalizeBangDreamBoolean( - this.configService.get(BANGDREAM_TSUGU_ENV_KEYS.compress), - true, - ), - ), - displayedServerList: this.pickDisplayedServerList(input), - mainServer: this.pickMainServer(input, []), - useEasyBG: normalizeBangDreamBoolean( - input.useEasyBG, - normalizeBangDreamBoolean( - this.configService.get(BANGDREAM_TSUGU_ENV_KEYS.useEasyBg), - defaults.useEasyBG ?? false, - ), - ), - }; - } - - private pickDisplayedServerList(input: QqbotBangDreamCommandInput) { - const source = - input.displayedServerList || - this.configService.get(BANGDREAM_TSUGU_ENV_KEYS.displayedServers); - const defaultServers = this.dictionaryLoader.getDefaultDisplayedServers(); - if (!source) return defaultServers; - const values = splitBangDreamOptionList(source); - const servers = values - .map((item) => this.normalizeServer(item)) - .filter((item) => item !== undefined) as Server[]; - return servers.length > 0 ? [...new Set(servers)] : defaultServers; - } - - private pickMainServer( - input: QqbotBangDreamCommandInput, - tokens: string[], - ): Server { - const explicit = this.firstDefined( - input.mainServer, - input.serverName, - input.server, - tokens.find((item) => this.normalizeServer(item) !== undefined) || - this.configService.get(BANGDREAM_TSUGU_ENV_KEYS.mainServer), - ); - return this.normalizeServer(explicit) ?? Server.cn; - } - - private pickDifficulty(value: unknown) { - const source = `${value || ''}`.trim(); - if (!source) return undefined; - const numeric = this.optionalNumber(source); - if (numeric !== undefined) return numeric; - const alias = this.dictionaryLoader.resolveDifficulty(source); - if (alias !== undefined) return alias; - const matched = fuzzySearch(source)?.difficulty?.[0]; - return typeof matched === 'number' ? matched : undefined; - } - - private normalizeServer(value: unknown): Server | undefined { - if (value === undefined || value === null || value === '') return undefined; - const raw = `${value}`.trim(); - const numeric = Number(raw); - if (Number.isInteger(numeric) && numeric >= 0 && numeric <= 4) { - return numeric as Server; - } - const server = this.dictionaryLoader.resolveServer(raw); - return server === undefined ? undefined : (server as Server); - } - - private requireText(input: QqbotBangDreamCommandInput, message: string) { - const text = this.pickText(input); - if (!text) throw new Error(message); - return text; - } - - private pickText(input: QqbotBangDreamCommandInput) { - return `${input.query || input.text || input.raw || ''}`.trim(); - } - - private getTokens(input: QqbotBangDreamCommandInput) { - if (Array.isArray(input.args)) { - return input.args.map((item) => `${item}`.trim()).filter(Boolean); - } - return this.pickText(input).split(/\s+/).filter(Boolean); - } - - private firstToken(input: QqbotBangDreamCommandInput) { - return this.getTokens(input)[0]; - } - - private requireNumber(explicit: unknown, fallback: unknown, message: string) { - const value = - this.optionalNumber(explicit) ?? this.optionalNumber(fallback); - if (value === undefined) throw new Error(message); - return value; - } - - private optionalNumber(value: unknown) { - if (value === undefined || value === null || value === '') return undefined; - const parsed = Number(value); - return Number.isInteger(parsed) ? parsed : undefined; - } - - private firstNumber(tokens: string[]) { - return tokens - .map((item) => this.optionalNumber(item)) - .find((item) => item !== undefined); - } - - private secondNumber(tokens: string[]) { - return tokens - .map((item) => this.optionalNumber(item)) - .filter((item) => item !== undefined)[1]; - } - - private normalizeBoolean(value: unknown, fallback: boolean) { - return normalizeBangDreamBoolean(value, fallback); - } - - private isInteger(value: string) { - return /^(0|[1-9]\d*)$/.test(value); - } - - private firstDefined(...values: unknown[]) { - return values.find( - (value) => value !== undefined && value !== null && value !== '', - ); - } - - private async fetchDictionaryItems( - dictCode: string, - ): Promise { - if (!this.dictService) return []; - const items = await this.dictService.getDictItemsByKey(dictCode); - return items.map(({ label, value }) => ({ - label, - value, - })); - } -} diff --git a/src/modules/qqbot/plugins/bangDream/application/operation-pipeline.ts b/src/modules/qqbot/plugins/bangDream/application/operation-pipeline.ts deleted file mode 100644 index 1723446..0000000 --- a/src/modules/qqbot/plugins/bangDream/application/operation-pipeline.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { - QqbotBangDreamCommandInput, - QqbotBangDreamCommandOutput, - QqbotBangDreamOperationKey, -} from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.types'; -import type { - QqbotBangDreamOperationHandlerName, - TsuguOperationDefinition, -} from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; -import { - createTsuguHookContext, - type TsuguHookRegistry, -} from '@/modules/qqbot/plugins/bangDream/hook/hook-registry'; - -export type TsuguOperationPipelineOptions = { - executeHandler: ( - handlerName: QqbotBangDreamOperationHandlerName, - input: QqbotBangDreamCommandInput, - ) => Promise; - hookRegistry: TsuguHookRegistry; - normalizeError: (error: unknown) => string; - resolveOperation: ( - operationKey: QqbotBangDreamOperationKey, - ) => TsuguOperationDefinition | undefined; - waitForReady: () => Promise; -}; - -/** - * 串联 Tsugu 命令执行阶段:准备主数据、解析 operation、调用渲染 handler、输出 hook。 - */ -export class TsuguOperationPipeline { - constructor(private readonly options: TsuguOperationPipelineOptions) {} - - async run( - operationKey: QqbotBangDreamOperationKey, - input: QqbotBangDreamCommandInput, - ) { - const context = createTsuguHookContext(operationKey, input); - await this.options.hookRegistry.beforeParse(context); - - try { - context.stage = 'mainData'; - await this.options.waitForReady(); - - context.stage = 'operation'; - const operation = this.options.resolveOperation(operationKey); - if (!operation) { - throw new Error(`BangDream 插件能力不存在:${operationKey}`); - } - context.handlerName = operation.handlerName; - await this.options.hookRegistry.afterResolve(context); - - context.stage = 'handler'; - await this.options.hookRegistry.beforeRender(context); - const output = await this.options.executeHandler( - operation.handlerName, - input, - ); - - context.stage = 'output'; - context.imageCount = output.imageCount; - context.query = output.query || context.query; - await this.options.hookRegistry.afterOutput(context); - return output; - } catch (error) { - const message = this.options.normalizeError(error); - await this.options.hookRegistry.onError(context, message); - throw new Error(message); - } - } -} diff --git a/src/modules/qqbot/plugins/bangDream/commands/qqbot-bangdream-command.definitions.ts b/src/modules/qqbot/plugins/bangDream/commands/qqbot-bangdream-command.definitions.ts deleted file mode 100644 index 04460ea..0000000 --- a/src/modules/qqbot/plugins/bangDream/commands/qqbot-bangdream-command.definitions.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - BANGDREAM_OPERATION_REGISTRY, - type QqbotBangDreamOperationKey, -} from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -export const BANGDREAM_INPUT_SCHEMA = { - properties: { - args: { description: '命令参数数组', type: 'array' }, - query: { description: '查询关键词', type: 'string' }, - raw: { description: '命令原始参数', type: 'string' }, - text: { description: '命令原始文本', type: 'string' }, - }, - type: 'object', -}; - -export const BANGDREAM_OUTPUT_SCHEMA = { - properties: { - imageCount: { type: 'number' }, - operationKey: { type: 'string' }, - query: { type: 'string' }, - replyText: { type: 'string' }, - source: { type: 'string' }, - }, - type: 'object', -}; - -export const BANGDREAM_OPERATION_DEFS: Array<{ - description: string; - key: QqbotBangDreamOperationKey; - name: string; -}> = BANGDREAM_OPERATION_REGISTRY.map((operation) => ({ - description: operation.description, - key: operation.key, - name: operation.name, -})); diff --git a/src/modules/qqbot/plugins/bangDream/provider/file-cache.client.ts b/src/modules/qqbot/plugins/bangDream/provider/file-cache.client.ts deleted file mode 100644 index 214b33d..0000000 --- a/src/modules/qqbot/plugins/bangDream/provider/file-cache.client.ts +++ /dev/null @@ -1,201 +0,0 @@ -import axios from 'axios'; -import * as path from 'path'; -import * as fs from 'fs'; -import { - BANGDREAM_TSUGU_ENV_KEYS, - normalizeBangDreamPositiveInteger, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; -import { - BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS, - getCacheClientErrorMessage, - getCacheClientResponseStatus, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-policy'; - -const errorUrlCache: { [url: string]: number } = {}; -const DEFAULT_REQUEST_TIMEOUT_MS = 8000; - -function getRequestTimeoutMs(): number { - return normalizeBangDreamPositiveInteger( - process.env[BANGDREAM_TSUGU_ENV_KEYS.requestTimeoutMs], - DEFAULT_REQUEST_TIMEOUT_MS, - ); -} - -/** - * 在数据下载与缓存层中下载当前数据。 - * - * @param url - 远端资源地址。 - * @param directory - directory参数,未传入时使用默认值。 - * @param fileName - file名称参数,未传入时使用默认值。 - * @param cacheTime - 缓存时间参数,未传入时使用默认值。 - * @returns 异步处理结果。 - */ -export async function download( - url: string, - directory?: string, - fileName?: string, - cacheTime = 0, -): Promise { - if (directory != undefined && fileName != undefined) { - ensureDirectoryExists(directory); - } - try { - if (isErrorUrlCacheActive(url)) { - throw new Error('downloadFile: errorUrlCache includes url'); - } - let eTag: string | undefined; - const cacheFilePath = path.join(directory || '', `${fileName || ''}`); - if (fileName && directory) { - const eTagFilePath = path.join(directory, `${fileName}.etag`); - eTag = fs.existsSync(eTagFilePath) - ? fs.readFileSync(eTagFilePath, 'utf-8') - : undefined; - if (fs.existsSync(cacheFilePath)) { - const stat = fs.statSync(cacheFilePath); - const now = Date.now(); - if (now - stat.mtimeMs < cacheTime * 1000) { - const cachedData = fs.readFileSync(cacheFilePath); - return cachedData; - } - } - } - const headers = eTag ? { 'If-None-Match': eTag } : {}; - let response; - try { - response = await axios.get(url, { - headers, - responseType: 'arraybuffer', - timeout: getRequestTimeoutMs(), - }); - } catch (error) { - if (error.response && error.response.status === 304) { - const cachedData = fs.readFileSync(cacheFilePath); - return cachedData; - } else { - throw error; - } - } - - const fileBuffer = Buffer.from(response.data, 'binary'); - - const newETag = response.headers.etag; - if (newETag && directory && fileName) { - fs.writeFileSync(path.join(directory, `${fileName}.etag`), newETag); - } - - if (directory && fileName) { - fs.writeFileSync(path.join(directory, fileName), fileBuffer); - } - return fileBuffer; - } catch (e) { - if (getCacheClientResponseStatus(e) === 404) { - errorUrlCache[url] = Date.now(); - } - if (url.includes('.png') || url.includes('.svg')) { - throw e; - } else { - throw new Error( - `Failed to download file from "${url}". Error: ${getCacheClientErrorMessage(e)}`, - ); - } - } -} - -function isErrorUrlCacheActive(url: string): boolean { - const cachedAt = errorUrlCache[url]; - if (cachedAt == null) { - return false; - } - if (Date.now() - cachedAt >= BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS) { - delete errorUrlCache[url]; - return false; - } - return true; -} - -/** - * 在数据下载与缓存层中确保DirectoryExists。 - * - * @param filepath - filepath参数。 - */ -function ensureDirectoryExists(filepath: string) { - if (!fs.existsSync(filepath)) { - try { - fs.mkdirSync(filepath, { recursive: true }); - } catch {} - } -} - -/** - * 在数据下载与缓存层中获取JSONAndSave。 - * - * @param url - 远端资源地址。 - * @param directory - directory参数,未传入时使用默认值。 - * @param fileName - file名称参数,未传入时使用默认值。 - * @param cacheTime - 缓存时间参数,未传入时使用默认值。 - * @returns 异步处理结果。 - */ -export async function getJsonAndSave( - url: string, - directory?: string, - fileName?: string, - cacheTime = 0, -): Promise { - try { - if (directory != undefined && fileName != undefined) { - ensureDirectoryExists(directory); - } - let eTag: string | undefined; - const cacheFilePath = path.join(directory || '', `${fileName || ''}`); - if (fileName && directory) { - const eTagFilePath = path.join(directory, `${fileName}.etag`); - eTag = fs.existsSync(eTagFilePath) - ? fs.readFileSync(eTagFilePath, 'utf-8') - : undefined; - if (fs.existsSync(cacheFilePath)) { - const stat = fs.statSync(cacheFilePath); - const now = Date.now(); - if (now - stat.mtimeMs < cacheTime * 1000) { - const cachedData = fs.readFileSync(cacheFilePath, 'utf-8'); - const cachedJson = JSON.parse(cachedData); - return cachedJson; - } - } - } - const headers = eTag ? { 'If-None-Match': eTag } : {}; - let response; - try { - response = await axios.get(url, { - headers, - responseType: 'arraybuffer', - timeout: getRequestTimeoutMs(), - }); - } catch (error) { - if (error.response && error.response.status === 304) { - const cachedData = fs.readFileSync(cacheFilePath, 'utf-8'); - const cachedJson = JSON.parse(cachedData); - return cachedJson; - } else { - throw error; - } - } - - const fileBuffer = Buffer.from(response.data, 'binary'); - const fileContent = fileBuffer.toString('utf-8'); - const jsonObject = JSON.parse(fileContent); - - const newETag = response.headers.etag; - if (newETag && directory && fileName) { - fs.writeFileSync(path.join(directory, `${fileName}.etag`), newETag); - } - - if (directory && fileName) { - fs.writeFileSync(path.join(directory, fileName), fileContent); - } - - return jsonObject; - } catch (e) { - throw e; - //throw new Error(`Failed to download JSON data from "${url}". Error: ${e.message}`); - } -} diff --git a/src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin.ts b/src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin.ts deleted file mode 100644 index 2234283..0000000 --- a/src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { formatKtDateTime, ToolsService } from '@/common'; -import type { QqbotIntegrationPlugin } from '@/modules/qqbot/core/contract/qqbot.types'; -import { - BANGDREAM_INPUT_SCHEMA, - BANGDREAM_OUTPUT_SCHEMA, -} from '@/modules/qqbot/plugins/bangDream/commands/qqbot-bangdream-command.definitions'; -import { QqbotBangDreamClientService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-client.service'; -import { BANGDREAM_OPERATION_REGISTRY } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -@Injectable() -export class QqbotBangDreamPluginService { - constructor( - private readonly bangDreamClientService: QqbotBangDreamClientService, - private readonly toolsService: ToolsService, - ) {} - - getPlugin(): QqbotIntegrationPlugin { - return { - description: - '合入 Tsugu BangDream Bot 后端开源源码,提供 BanG Dream! Girls Band Party 公开查询出图能力。', - healthCheck: async () => { - const checkedAt = formatKtDateTime(new Date()); - try { - await this.bangDreamClientService.checkHealth(); - return { - checkedAt, - message: 'BangDream 插件可用', - status: 'healthy', - }; - } catch (err) { - return { - checkedAt, - message: this.toolsService.getErrorMessage( - err, - 'BangDream 插件不可用', - ), - status: 'degraded', - }; - } - }, - key: 'bangdream', - legacyKeys: ['bangDream'], - name: 'BangDream 查询', - operations: BANGDREAM_OPERATION_REGISTRY.map((operation) => ({ - cacheTtlMs: 60_000, - description: operation.description, - inputSchema: BANGDREAM_INPUT_SCHEMA, - key: operation.key, - name: operation.name, - outputSchema: BANGDREAM_OUTPUT_SCHEMA, - execute: async (input) => - await this.bangDreamClientService.execute(operation.key, input), - })), - version: '2.0.0', - }; - } -} diff --git a/src/modules/qqbot/plugins/bangDream/qqbot-bangdream.types.ts b/src/modules/qqbot/plugins/bangDream/qqbot-bangdream.types.ts deleted file mode 100644 index 44aa603..0000000 --- a/src/modules/qqbot/plugins/bangDream/qqbot-bangdream.types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { QqbotBangDreamOperationKey } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -export type { QqbotBangDreamOperationKey } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -export type QqbotBangDreamCommandInput = { - args?: string[]; - cardId?: number | string; - compress?: boolean | string; - difficulty?: number | string; - difficultyText?: string; - displayedServerList?: Array | string; - eventId?: number | string; - gachaId?: number | string; - mainServer?: number | string; - meta?: boolean | string; - playerId?: number | string; - query?: string; - raw?: string; - server?: number | string; - serverName?: number | string; - songId?: number | string; - text?: string; - tier?: number | string; - times?: number | string; - useEasyBG?: boolean | string; -}; - -export type QqbotBangDreamCommandOutput = { - imageCount: number; - operationKey: QqbotBangDreamOperationKey; - query: string; - replyText: string; - source: string; -}; - -export type QqbotBangDreamSongSearchInput = { - query?: string; - raw?: string; - text?: string; -}; - -export type QqbotBangDreamSongSummary = QqbotBangDreamCommandOutput; diff --git a/src/modules/qqbot/plugins/bangDream/registry/operation-registry.ts b/src/modules/qqbot/plugins/bangDream/registry/operation-registry.ts deleted file mode 100644 index eeb17f5..0000000 --- a/src/modules/qqbot/plugins/bangDream/registry/operation-registry.ts +++ /dev/null @@ -1,219 +0,0 @@ -export type QqbotBangDreamOperationHandlerName = - | 'getCardIllustration' - | 'getCutoffAll' - | 'getCutoffDetail' - | 'getCutoffRecent' - | 'getEventStage' - | 'getSongChart' - | 'getSongMeta' - | 'randomSong' - | 'searchCard' - | 'searchCharacter' - | 'searchEvent' - | 'searchGacha' - | 'searchPlayer' - | 'searchSong' - | 'simulateGacha'; - -export type TsuguOperationDefinition = { - description: string; - handlerName: QqbotBangDreamOperationHandlerName; - key: string; - name: string; - onlineCommand: { - aliases: readonly string[]; - cooldownMs: number; - remark: string; - }; -}; - -export const BANGDREAM_OPERATION_REGISTRY = [ - { - description: '按歌曲名、昵称、标签或歌曲 ID 查询歌曲信息图片。', - handlerName: 'searchSong', - key: 'bangdream.song.search', - name: '查曲', - onlineCommand: { - aliases: ['查曲', 'bd', 'bangdream', 'bandori', '邦邦', '邦邦查歌'], - cooldownMs: 1500, - remark: '查询 BanG Dream 歌曲信息;格式:/查曲 歌曲名 或 /查曲 歌曲ID', - }, - }, - { - description: '按歌曲 ID 和难度查询谱面图片。', - handlerName: 'getSongChart', - key: 'bangdream.song.chart', - name: '查谱面', - onlineCommand: { - aliases: ['查谱面', '谱面', 'bd谱面'], - cooldownMs: 1500, - remark: '查询歌曲谱面;格式:/查谱面 歌曲ID [难度]', - }, - }, - { - description: '按关键词随机抽取一首歌曲并输出歌曲图片。', - handlerName: 'randomSong', - key: 'bangdream.song.random', - name: '随机曲', - onlineCommand: { - aliases: ['随机曲', '随机', 'bd随机'], - cooldownMs: 1500, - remark: '按关键词随机歌曲;格式:/随机曲 [关键词]', - }, - }, - { - description: '查询指定服务器歌曲分数榜图片。', - handlerName: 'getSongMeta', - key: 'bangdream.song.meta', - name: '查询分数表', - onlineCommand: { - aliases: ['查询分数表', '查分数表', '查询分数榜', '查分数榜', 'bd分数表'], - cooldownMs: 1500, - remark: '查询歌曲分数榜;格式:/查询分数表 [服务器]', - }, - }, - { - description: '按卡牌关键词或卡牌 ID 查询卡牌信息图片。', - handlerName: 'searchCard', - key: 'bangdream.card.search', - name: '查卡', - onlineCommand: { - aliases: ['查卡', '查卡牌', 'bd查卡'], - cooldownMs: 1500, - remark: '查询卡牌信息;格式:/查卡 卡牌关键词 或 /查卡 卡牌ID', - }, - }, - { - description: '按卡牌 ID 查询卡面插画图片。', - handlerName: 'getCardIllustration', - key: 'bangdream.card.illustration', - name: '查卡面', - onlineCommand: { - aliases: ['查卡面', '查卡插画', '查插画', 'bd卡面'], - cooldownMs: 1500, - remark: '查询卡牌插画;格式:/查卡面 卡牌ID', - }, - }, - { - description: '按角色关键词或角色 ID 查询角色信息图片。', - handlerName: 'searchCharacter', - key: 'bangdream.character.search', - name: '查角色', - onlineCommand: { - aliases: ['查角色', 'bd角色'], - cooldownMs: 1500, - remark: '查询角色信息;格式:/查角色 角色关键词 或 /查角色 角色ID', - }, - }, - { - description: '按活动关键词或活动 ID 查询活动信息图片。', - handlerName: 'searchEvent', - key: 'bangdream.event.search', - name: '查活动', - onlineCommand: { - aliases: ['查活动', 'bd活动'], - cooldownMs: 1500, - remark: '查询活动信息;格式:/查活动 活动关键词 或 /查活动 活动ID', - }, - }, - { - description: '查询当前或指定活动的试炼信息图片。', - handlerName: 'getEventStage', - key: 'bangdream.event.stage', - name: '查试炼', - onlineCommand: { - aliases: ['查试炼', '查stage', '查舞台', '查festival', '查5v5'], - cooldownMs: 1500, - remark: '查询活动试炼;格式:/查试炼 [活动ID] [-m]', - }, - }, - { - description: '按玩家 ID 和服务器查询玩家信息图片。', - handlerName: 'searchPlayer', - key: 'bangdream.player.search', - name: '查玩家', - onlineCommand: { - aliases: ['查玩家', '查询玩家', 'bd玩家'], - cooldownMs: 1500, - remark: '查询玩家信息;格式:/查玩家 玩家ID [服务器]', - }, - }, - { - description: '按卡池 ID 查询卡池信息图片。', - handlerName: 'searchGacha', - key: 'bangdream.gacha.search', - name: '查卡池', - onlineCommand: { - aliases: ['查卡池', 'bd卡池'], - cooldownMs: 1500, - remark: '查询卡池信息;格式:/查卡池 卡池ID', - }, - }, - { - description: '模拟指定次数抽卡并输出结果图片。', - handlerName: 'simulateGacha', - key: 'bangdream.gacha.simulate', - name: '抽卡模拟', - onlineCommand: { - aliases: ['抽卡模拟', 'bd抽卡'], - cooldownMs: 3000, - remark: '模拟抽卡;格式:/抽卡模拟 [次数] [卡池ID]', - }, - }, - { - description: '查询指定档位的当前活动档线与预测线图片。', - handlerName: 'getCutoffDetail', - key: 'bangdream.cutoff.detail', - name: 'ycx', - onlineCommand: { - aliases: ['ycx', '预测线', '查档线', 'bd档线'], - cooldownMs: 3000, - remark: '查询指定档位预测线;格式:/ycx 档位 [活动ID] [服务器]', - }, - }, - { - description: '查询当前活动所有档位的档线与预测线图片。', - handlerName: 'getCutoffAll', - key: 'bangdream.cutoff.all', - name: 'ycxall', - onlineCommand: { - aliases: ['ycxall', 'myycx', '全部档线'], - cooldownMs: 3000, - remark: '查询所有档位预测线;格式:/ycxall [活动ID] [服务器]', - }, - }, - { - description: '查询指定档位与最近同类型活动档线图片。', - handlerName: 'getCutoffRecent', - key: 'bangdream.cutoff.recent', - name: 'lsycx', - onlineCommand: { - aliases: ['lsycx', '历史档线', '近期档线'], - cooldownMs: 3000, - remark: '查询同类型活动档线;格式:/lsycx 档位 [活动ID] [服务器]', - }, - }, -] as const satisfies readonly TsuguOperationDefinition[]; - -export type QqbotBangDreamOperationKey = - (typeof BANGDREAM_OPERATION_REGISTRY)[number]['key']; - -export type TsuguOperationRegistryItem = - (typeof BANGDREAM_OPERATION_REGISTRY)[number]; - -export const BANGDREAM_OPERATION_KEYS = BANGDREAM_OPERATION_REGISTRY.map( - (operation) => operation.key, -); - -const BANGDREAM_OPERATION_BY_KEY = new Map( - BANGDREAM_OPERATION_REGISTRY.map((operation) => [operation.key, operation]), -); - -/** - * 按 operation key 查找 BangDream Tsugu 命令定义。 - * - * @param operationKey - BangDream operation key。 - */ -export function getBangDreamOperationDefinition(operationKey: string) { - return BANGDREAM_OPERATION_BY_KEY.get(operationKey); -} diff --git a/src/modules/qqbot/plugins/bangDream/search/search-dictionary.repository.ts b/src/modules/qqbot/plugins/bangDream/search/search-dictionary.repository.ts deleted file mode 100644 index 082c23b..0000000 --- a/src/modules/qqbot/plugins/bangDream/search/search-dictionary.repository.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as fs from 'fs'; -import { fuzzySearchPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; -import type { FuzzySearchConfig } from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search.types'; - -export class SearchDictionaryRepository { - constructor(private readonly filePath = fuzzySearchPath) {} - - /** - * 读取搜索别名配置。 - */ - loadConfig(): FuzzySearchConfig { - const fileContent = fs.readFileSync(this.filePath, 'utf-8'); - logger('fuzzySearch', 'loaded fuzzy search config'); - return JSON.parse(fileContent); - } -} - -export const searchDictionaryRepository = new SearchDictionaryRepository(); diff --git a/src/modules/qqbot/plugins/bangDream/shared/bangdream-constants.ts b/src/modules/qqbot/plugins/bangDream/shared/bangdream-constants.ts deleted file mode 100644 index 6b4ddd9..0000000 --- a/src/modules/qqbot/plugins/bangDream/shared/bangdream-constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; -export * from '@/modules/qqbot/plugins/bangDream/dictionary/default-dictionary'; -export * from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; diff --git a/src/modules/qqbot/plugins/bangDream/shared/model-utils.ts b/src/modules/qqbot/plugins/bangDream/shared/model-utils.ts deleted file mode 100644 index d6b22f8..0000000 --- a/src/modules/qqbot/plugins/bangDream/shared/model-utils.ts +++ /dev/null @@ -1,162 +0,0 @@ -import * as XLSX from 'xlsx'; -import * as fs from 'fs'; - -/** - * 在BangDream 领域模型层中读取JSON。 - * - * @param filepath - filepath参数。 - * @returns 异步处理结果。 - */ -export async function readJSON(filepath: string): Promise { - //读取json文件子程序,返回json数据 - const promise: object = new Promise(function (resolve) { - const rawdata = fs.readFileSync(filepath); - const rawstring = rawdata.toString(); - const data: object = JSON.parse(rawstring); - resolve(data); - }); - return promise; -} -/** - * 在BangDream 领域模型层中读取JSONFrom缓冲区。 - * - * @param buffer - 图片或下载结果缓冲区。 - * @returns 异步处理结果。 - */ -export async function readJSONFromBuffer(buffer: Buffer): Promise { - //读取json文件子程序,返回json数据 - const rawstring = buffer.toString(); - const data: object = JSON.parse(rawstring); - return data; -} - -/** - * 在BangDream 领域模型层中写入JSON。 - * - * @param filepath - filepath参数。 - * @param data - 业务数据对象。 - */ -export async function writeJSON(filepath: string, data: object) { - //写入json文件子程序 - const rawdata = JSON.stringify(data); - fs.writeFileSync(filepath, rawdata); -} - -/** - * 在BangDream 领域模型层中读取ExcelFile。 - * - * @param filePath - 本地文件路径。 - * @returns 异步处理结果。 - */ -export async function readExcelFile< - T extends Record = Record, ->(filePath: string): Promise { - const workbook = XLSX.readFile(filePath); - - const sheetName = workbook.SheetNames[0]; - const worksheet = workbook.Sheets[sheetName]; - return XLSX.utils.sheet_to_json(worksheet); -} - -//将string[]转变为number[] -/** - * 在BangDream 领域模型层中处理stringTo数字Array。 - * - * @param stringArray - stringArray参数。 - * @returns 计算后的数值。 - */ -export function stringToNumberArray( - stringArray: Array, -): number[] { - const numberArray: number[] = []; - for (let i = 0; i < stringArray.length; i++) { - if (stringArray[i] == null) { - numberArray.push(null); - } else { - numberArray.push(Number(stringArray[i])); - } - } - return numberArray; -} - -/** - * 在BangDream 领域模型层中格式化数字。 - * - * @param num - num参数。 - * @param length - length参数。 - * @returns 格式化后的文本。 - */ -export function formatNumber(num: number, length: number): string { - // 将数字转换为字符串 - const str = num.toString(); - - // 如果字符串长度小于3,前面补0直到长度为3 - if (str.length < length) { - return str.padStart(length, '0'); - } - - return str; -} - -//栈函数 -export class Stack { - stack: T[]; - private maxLength: number; - - /** - * 构造 Stack 实例,并初始化该模型的本地基础字段。 - * - * @param maxLength - maxLength参数。 - */ - constructor(maxLength: number) { - this.stack = []; - this.maxLength = maxLength; - } - - /** - * 在 Stack 模型中推入当前数据。 - * - * @param item - 当前列表项。 - */ - push(item: T): void { - this.stack.unshift(item); // 将新元素插入到堆栈的最前面 - - if (this.stack.length > this.maxLength) { - this.stack.pop(); // 如果堆栈长度超过指定的最大长度,自动弹出最后一个元素 - } - } - - /** - * 在 Stack 模型中处理pop。 - * - * @returns 处理结果。 - */ - pop(): T | undefined { - return this.stack.shift(); // 弹出并返回最前面的元素 - } - - /** - * 在 Stack 模型中判断Empty。 - * - * @returns 判断结果。 - */ - isEmpty(): boolean { - return this.stack.length === 0; - } - - /** - * 在 Stack 模型中处理size。 - * - * @returns 计算后的数值。 - */ - size(): number { - return this.stack.length; - } - - /** - * 在 Stack 模型中清理当前数据。 - */ - clear(): void { - this.stack = []; - } -} diff --git a/src/modules/qqbot/plugins/bangDream/TSUGU-LICENSE b/src/modules/qqbot/plugins/bangdream/TSUGU-LICENSE similarity index 100% rename from src/modules/qqbot/plugins/bangDream/TSUGU-LICENSE rename to src/modules/qqbot/plugins/bangdream/TSUGU-LICENSE diff --git a/src/modules/qqbot/plugins/bangDream/plugin.json b/src/modules/qqbot/plugins/bangdream/plugin.json similarity index 98% rename from src/modules/qqbot/plugins/bangDream/plugin.json rename to src/modules/qqbot/plugins/bangdream/plugin.json index 55d5634..872b912 100644 --- a/src/modules/qqbot/plugins/bangDream/plugin.json +++ b/src/modules/qqbot/plugins/bangdream/plugin.json @@ -6,7 +6,8 @@ "description": "BanG Dream! Girls Band Party 查询、出图和档线能力。", "author": "KT", "license": "MIT", - "entry": "qqbot-bangdream.plugin.ts", + "entry": "src/index.ts", + "legacyAliases": ["bangDream"], "permissions": [ "qqbot.send", "runtime.http", @@ -260,7 +261,7 @@ "assets": [ { "key": "bangdream-assets", - "path": "assets" + "path": "src/assets" } ], "migrations": [] diff --git a/src/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context.ts b/src/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context.ts new file mode 100644 index 0000000..ad54887 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context.ts @@ -0,0 +1,248 @@ +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { + BangDreamDictionaryLoader, + type BangDreamDictionaryItem, +} from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/dictionary-loader'; +import { + BANGDREAM_TSUGU_ENV_KEYS, + normalizeBangDreamBoolean, + splitBangDreamOptionList, +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { + fuzzySearch, + type FuzzySearchResult, +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; +import mainAPI, { + waitForMainDataReady, +} from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import type { + QqbotBangDreamCommandInput, + QqbotBangDreamCommandOutput, + QqbotBangDreamOperationKey, +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types'; + +const SOURCE_NAME = 'BangDream 内置插件'; + +export type BangDreamConfigReader = { + get(key: string): T | undefined; +}; + +export type BangDreamDictionaryReader = { + getDictItemsByKey(dictCode: string): Promise< + Array<{ + label: string; + value: string; + }> + >; +}; + +export type BangDreamCommandContextOptions = { + configReader?: BangDreamConfigReader; + dictionaryReader?: BangDreamDictionaryReader; +}; + +export class BangDreamCommandContext { + private readonly configReader?: BangDreamConfigReader; + private readonly dictionaryLoader = new BangDreamDictionaryLoader(); + private readonly dictionaryReader?: BangDreamDictionaryReader; + + constructor(options: BangDreamCommandContextOptions = {}) { + this.configReader = options.configReader; + this.dictionaryReader = options.dictionaryReader; + } + + async refreshDictionaryCache() { + await this.dictionaryLoader.refresh((dictCode) => + this.fetchDictionaryItems(dictCode), + ); + } + + async checkHealth() { + await waitForMainDataReady(); + const data = mainAPI as { cards?: unknown; songs?: unknown }; + if (!data.songs || !data.cards) { + throw new Error('BangDream 数据配置未加载'); + } + fuzzySearch('夏祭り'); + return true; + } + + async drawFuzzyResult( + query: string, + render: (matches: FuzzySearchResult) => Promise>, + ) { + const matches = fuzzySearch(query); + if (Object.keys(matches).length === 0) { + return ['错误: 没有有效的关键词']; + } + return await render(matches); + } + + toImageReply( + operationKey: QqbotBangDreamOperationKey, + query: string, + list: Array, + ): QqbotBangDreamCommandOutput { + const images = list.filter((item): item is Buffer => Buffer.isBuffer(item)); + if (images.length === 0) { + const message = + list.find((item): item is string => typeof item === 'string') || + 'BangDream 未返回图片'; + throw new Error(message); + } + return { + imageCount: images.length, + operationKey, + query, + replyText: images + .map((item) => `[CQ:image,file=base64://${item.toString('base64')}]`) + .join('\n'), + source: SOURCE_NAME, + }; + } + + getRenderOptions( + input: QqbotBangDreamCommandInput, + defaults: { useEasyBG?: boolean } = {}, + ) { + return { + compress: normalizeBangDreamBoolean( + input.compress, + normalizeBangDreamBoolean( + this.readConfig(BANGDREAM_TSUGU_ENV_KEYS.compress), + true, + ), + ), + displayedServerList: this.pickDisplayedServerList(input), + mainServer: this.pickMainServer(input, []), + useEasyBG: normalizeBangDreamBoolean( + input.useEasyBG, + normalizeBangDreamBoolean( + this.readConfig(BANGDREAM_TSUGU_ENV_KEYS.useEasyBg), + defaults.useEasyBG ?? false, + ), + ), + }; + } + + pickDisplayedServerList(input: QqbotBangDreamCommandInput) { + const source = + input.displayedServerList || + this.readConfig(BANGDREAM_TSUGU_ENV_KEYS.displayedServers); + const defaultServers = this.dictionaryLoader.getDefaultDisplayedServers(); + if (!source) return defaultServers; + const values = splitBangDreamOptionList(source); + const servers = values + .map((item) => this.normalizeServer(item)) + .filter((item) => item !== undefined) as Server[]; + return servers.length > 0 ? [...new Set(servers)] : defaultServers; + } + + pickMainServer(input: QqbotBangDreamCommandInput, tokens: string[]): Server { + const explicit = this.firstDefined( + input.mainServer, + input.serverName, + input.server, + tokens.find((item) => this.normalizeServer(item) !== undefined) || + this.readConfig(BANGDREAM_TSUGU_ENV_KEYS.mainServer), + ); + return this.normalizeServer(explicit) ?? Server.cn; + } + + pickDifficulty(value: unknown) { + const source = `${value || ''}`.trim(); + if (!source) return undefined; + const numeric = this.optionalNumber(source); + if (numeric !== undefined) return numeric; + const alias = this.dictionaryLoader.resolveDifficulty(source); + if (alias !== undefined) return alias; + const matched = fuzzySearch(source)?.difficulty?.[0]; + return typeof matched === 'number' ? matched : undefined; + } + + normalizeServer(value: unknown): Server | undefined { + if (value === undefined || value === null || value === '') return undefined; + const raw = `${value}`.trim(); + const numeric = Number(raw); + if (Number.isInteger(numeric) && numeric >= 0 && numeric <= 4) { + return numeric as Server; + } + const server = this.dictionaryLoader.resolveServer(raw); + return server === undefined ? undefined : (server as Server); + } + + requireText(input: QqbotBangDreamCommandInput, message: string) { + const text = this.pickText(input); + if (!text) throw new Error(message); + return text; + } + + pickText(input: QqbotBangDreamCommandInput) { + return `${input.query || input.text || input.raw || ''}`.trim(); + } + + getTokens(input: QqbotBangDreamCommandInput) { + if (Array.isArray(input.args)) { + return input.args.map((item) => `${item}`.trim()).filter(Boolean); + } + return this.pickText(input).split(/\s+/).filter(Boolean); + } + + firstToken(input: QqbotBangDreamCommandInput) { + return this.getTokens(input)[0]; + } + + requireNumber(explicit: unknown, fallback: unknown, message: string) { + const value = + this.optionalNumber(explicit) ?? this.optionalNumber(fallback); + if (value === undefined) throw new Error(message); + return value; + } + + optionalNumber(value: unknown) { + if (value === undefined || value === null || value === '') return undefined; + const parsed = Number(value); + return Number.isInteger(parsed) ? parsed : undefined; + } + + firstNumber(tokens: string[]) { + return tokens + .map((item) => this.optionalNumber(item)) + .find((item) => item !== undefined); + } + + secondNumber(tokens: string[]) { + return tokens + .map((item) => this.optionalNumber(item)) + .filter((item) => item !== undefined)[1]; + } + + normalizeBoolean(value: unknown, fallback: boolean) { + return normalizeBangDreamBoolean(value, fallback); + } + + isInteger(value: string) { + return /^(0|[1-9]\d*)$/.test(value); + } + + private firstDefined(...values: unknown[]) { + return values.find( + (value) => value !== undefined && value !== null && value !== '', + ); + } + + private async fetchDictionaryItems( + dictCode: string, + ): Promise { + if (!this.dictionaryReader) return []; + const items = await this.dictionaryReader.getDictItemsByKey(dictCode); + return items.map(({ label, value }) => ({ + label, + value, + })); + } + + private readConfig(key: string) { + return this.configReader?.get(key); + } +} diff --git a/src/modules/qqbot/plugins/bangDream/shared/bangdream-logger.ts b/src/modules/qqbot/plugins/bangdream/src/application/bangdream-logger.ts similarity index 57% rename from src/modules/qqbot/plugins/bangDream/shared/bangdream-logger.ts rename to src/modules/qqbot/plugins/bangdream/src/application/bangdream-logger.ts index 69b01ac..5d5b696 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/bangdream-logger.ts +++ b/src/modules/qqbot/plugins/bangdream/src/application/bangdream-logger.ts @@ -1,7 +1,3 @@ -import { Logger } from '@nestjs/common'; - -const tsuguLogger = new Logger('BangDreamTsugu'); - /** * 在运行时配置层中处理logger。 * @@ -9,5 +5,5 @@ const tsuguLogger = new Logger('BangDreamTsugu'); * @param message - message参数。 */ export function logger(type: string, message: unknown) { - tsuguLogger.log(`[${type}] ${String(message)}`); + console.info(`[BangDream][${type}] ${String(message)}`); } diff --git a/src/modules/qqbot/plugins/bangDream/hook/hook-registry.ts b/src/modules/qqbot/plugins/bangdream/src/application/hook/hook-registry.ts similarity index 54% rename from src/modules/qqbot/plugins/bangDream/hook/hook-registry.ts rename to src/modules/qqbot/plugins/bangdream/src/application/hook/hook-registry.ts index fb5818f..a99d039 100644 --- a/src/modules/qqbot/plugins/bangDream/hook/hook-registry.ts +++ b/src/modules/qqbot/plugins/bangdream/src/application/hook/hook-registry.ts @@ -1,75 +1,78 @@ import type { QqbotBangDreamCommandInput, QqbotBangDreamOperationKey, -} from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.types'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; -export type TsuguExecutionStage = +export type BangDreamExecutionStage = | 'handler' | 'mainData' | 'operation' | 'output' | 'start'; -export type TsuguHookContext = { +export type BangDreamHookContext = { handlerName?: string; imageCount?: number; input: QqbotBangDreamCommandInput; operationKey: QqbotBangDreamOperationKey; query?: string; - stage: TsuguExecutionStage; + stage: BangDreamExecutionStage; startedAt: number; }; -export type TsuguHook = { - afterOutput?: (context: TsuguHookContext) => Promise | void; - afterResolve?: (context: TsuguHookContext) => Promise | void; - beforeParse?: (context: TsuguHookContext) => Promise | void; - beforeRender?: (context: TsuguHookContext) => Promise | void; +export type BangDreamHook = { + afterOutput?: (context: BangDreamHookContext) => Promise | void; + afterResolve?: (context: BangDreamHookContext) => Promise | void; + beforeParse?: (context: BangDreamHookContext) => Promise | void; + beforeRender?: (context: BangDreamHookContext) => Promise | void; name: string; - onError?: (context: TsuguHookContext, error: unknown) => Promise | void; + onError?: ( + context: BangDreamHookContext, + error: unknown, + ) => Promise | void; order?: number; }; -type TsuguSimpleHookMethod = +type BangDreamSimpleHookMethod = | 'afterOutput' | 'afterResolve' | 'beforeParse' | 'beforeRender'; -/** - * 维护 Tsugu 命令执行生命周期 hook。 - */ -export class TsuguHookRegistry { - private readonly hooks: TsuguHook[]; +export class BangDreamHookRegistry { + private readonly hooks: BangDreamHook[]; - constructor(hooks: readonly TsuguHook[] = []) { + constructor(hooks: readonly BangDreamHook[] = []) { this.hooks = [...hooks].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); } - async beforeParse(context: TsuguHookContext) { + async beforeParse(context: BangDreamHookContext) { await this.emit('beforeParse', context); } - async afterResolve(context: TsuguHookContext) { + async afterResolve(context: BangDreamHookContext) { await this.emit('afterResolve', context); } - async beforeRender(context: TsuguHookContext) { + async beforeRender(context: BangDreamHookContext) { await this.emit('beforeRender', context); } - async afterOutput(context: TsuguHookContext) { + async afterOutput(context: BangDreamHookContext) { await this.emit('afterOutput', context); } - async onError(context: TsuguHookContext, error: unknown) { + async onError(context: BangDreamHookContext, error: unknown) { for (const hook of this.hooks) { await hook.onError?.(context, error); } } - private async emit(method: TsuguSimpleHookMethod, context: TsuguHookContext) { + private async emit( + method: BangDreamSimpleHookMethod, + context: BangDreamHookContext, + ) { for (const hook of this.hooks) { const handler = hook[method]; if (typeof handler === 'function') { @@ -79,46 +82,40 @@ export class TsuguHookRegistry { } } -/** - * 创建命令执行上下文,供 hook 在全链路中共享。 - */ -export function createTsuguHookContext( +export function createBangDreamHookContext( operationKey: QqbotBangDreamOperationKey, input: QqbotBangDreamCommandInput, -): TsuguHookContext { +): BangDreamHookContext { return { input, operationKey, - query: extractTsuguInputText(input), + query: extractBangDreamInputText(input), stage: 'start', startedAt: Date.now(), }; } -/** - * 创建默认日志 hook。 - */ -export function createTsuguLogHook(): TsuguHook { +export function createBangDreamLogHook(): BangDreamHook { return { afterOutput: (context) => { - logger('operation', formatTsuguHookMessage('success', context)); + logger('operation', formatBangDreamHookMessage('success', context)); }, beforeParse: (context) => { - logger('operation', formatTsuguHookMessage('start', context)); + logger('operation', formatBangDreamHookMessage('start', context)); }, - name: 'TsuguLogHook', + name: 'BangDreamLogHook', onError: (context, error) => { logger( 'operation', - `${formatTsuguHookMessage('error', context)} error=${getHookErrorMessage(error)}`, + `${formatBangDreamHookMessage('error', context)} error=${getHookErrorMessage(error)}`, ); }, }; } -function formatTsuguHookMessage( +function formatBangDreamHookMessage( status: 'error' | 'start' | 'success', - context: TsuguHookContext, + context: BangDreamHookContext, ) { const durationMs = Date.now() - context.startedAt; return [ @@ -134,7 +131,7 @@ function formatTsuguHookMessage( .join(' '); } -function extractTsuguInputText(input: QqbotBangDreamCommandInput) { +function extractBangDreamInputText(input: QqbotBangDreamCommandInput) { const direct = `${input.query || input.text || input.raw || ''}`.trim(); if (direct) return direct; return Array.isArray(input.args) ? input.args.join(' ').trim() : ''; diff --git a/src/modules/qqbot/plugins/bangDream/shared/main-data-store.ts b/src/modules/qqbot/plugins/bangdream/src/application/main-data-store.ts similarity index 76% rename from src/modules/qqbot/plugins/bangDream/shared/main-data-store.ts rename to src/modules/qqbot/plugins/bangdream/src/application/main-data-store.ts index 26f3d2e..533d6dd 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/main-data-store.ts +++ b/src/modules/qqbot/plugins/bangdream/src/application/main-data-store.ts @@ -1,11 +1,15 @@ -import { bestdoriApiPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import { bangDreamStaticPatchProvider } from '@/modules/qqbot/plugins/bangDream/provider/static-patch.provider'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +import { bestdoriApiPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import { bangDreamStaticPatchProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/static-patch.provider'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; import { BANGDREAM_TSUGU_ENV_KEYS, normalizeBangDreamPositiveInteger, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { + readBangDreamRuntimeConfig, + sleepBangDreamRuntime, +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; const mainAPI: Record = {}; //main对象,用于存放所有api数据,数据来源于Bestdori网站 const REQUIRED_MAIN_DATA_KEYS = [ @@ -19,7 +23,7 @@ const DEFAULT_MAIN_DATA_READY_TIMEOUT_MS = 15000; function getMainDataReadyTimeoutMs(): number { return normalizeBangDreamPositiveInteger( - process.env[BANGDREAM_TSUGU_ENV_KEYS.mainDataReadyTimeoutMs], + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.mainDataReadyTimeoutMs), DEFAULT_MAIN_DATA_READY_TIMEOUT_MS, ); } @@ -32,7 +36,7 @@ function isMainDataReady(): boolean { } async function rejectAfter(ms: number): Promise { - await new Promise((resolve) => setTimeout(resolve, ms)); + await sleepBangDreamRuntime(ms); throw new Error(`BangDream 主数据首次加载超时:${ms}ms`); } @@ -104,14 +108,18 @@ async function loadMainAPI(useCache: boolean = false) { logger('mainAPI', 'mainAPI loaded'); } -logger('mainAPI', 'initializing...'); -const initialLoadPromise = loadMainAPI(true).then(() => { - logger('mainAPI', 'initializing done'); - loadMainAPI(); -}); +let initialLoadPromise: Promise | undefined; -const refreshTimer = setInterval(loadMainAPI, 1000 * 60 * 5); //5分钟更新一次 -refreshTimer.unref?.(); +function ensureMainDataInitialLoad() { + if (!initialLoadPromise) { + logger('mainAPI', 'initializing...'); + initialLoadPromise = loadMainAPI(true).then(async () => { + logger('mainAPI', 'initializing done'); + await loadMainAPI(); + }); + } + return initialLoadPromise; +} /** * 等待 BangDream 主数据完成首次加载。 @@ -121,7 +129,7 @@ export async function waitForMainDataReady(): Promise { return; } await Promise.race([ - initialLoadPromise, + ensureMainDataInitialLoad(), rejectAfter(getMainDataReadyTimeoutMs()), ]); if (!isMainDataReady()) { diff --git a/src/modules/qqbot/plugins/bangDream/shared/main-data.repository.ts b/src/modules/qqbot/plugins/bangdream/src/application/main-data.repository.ts similarity index 91% rename from src/modules/qqbot/plugins/bangDream/shared/main-data.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/application/main-data.repository.ts index 21d131f..ae0f884 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/main-data.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/application/main-data.repository.ts @@ -1,5 +1,5 @@ -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import type { BANGDREAM_BESTDORI_API_PATHS } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import type { BANGDREAM_BESTDORI_API_PATHS } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; export type BangDreamMainDataKey = keyof typeof BANGDREAM_BESTDORI_API_PATHS; export type BangDreamMainDataCollection = Record; diff --git a/src/modules/qqbot/plugins/bangDream/assets/BG/bg_object_big.png b/src/modules/qqbot/plugins/bangdream/src/assets/BG/bg_object_big.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/BG/bg_object_big.png rename to src/modules/qqbot/plugins/bangdream/src/assets/BG/bg_object_big.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/BG/common.png b/src/modules/qqbot/plugins/bangdream/src/assets/BG/common.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/BG/common.png rename to src/modules/qqbot/plugins/bangdream/src/assets/BG/common.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/BG/live.png b/src/modules/qqbot/plugins/bangdream/src/assets/BG/live.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/BG/live.png rename to src/modules/qqbot/plugins/bangdream/src/assets/BG/live.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/BG/star1.png b/src/modules/qqbot/plugins/bangdream/src/assets/BG/star1.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/BG/star1.png rename to src/modules/qqbot/plugins/bangdream/src/assets/BG/star1.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/BG/star2.png b/src/modules/qqbot/plugins/bangdream/src/assets/BG/star2.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/BG/star2.png rename to src/modules/qqbot/plugins/bangdream/src/assets/BG/star2.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/B.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/B.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/B.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/B.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/D.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/D.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/D.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/D.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/K.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/K.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/K.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/K.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/L.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/L.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/L.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/L.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/limitBreakRank.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/limitBreakRank.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/limitBreakRank.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/limitBreakRank.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/star.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/star.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/star.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/star.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/star_trained.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/star_trained.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/star_trained.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/star_trained.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Card/text.png b/src/modules/qqbot/plugins/bangdream/src/assets/Card/text.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Card/text.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Card/text.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/EventStage/combo.png b/src/modules/qqbot/plugins/bangdream/src/assets/EventStage/combo.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/EventStage/combo.png rename to src/modules/qqbot/plugins/bangdream/src/assets/EventStage/combo.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/EventStage/judge.png b/src/modules/qqbot/plugins/bangdream/src/assets/EventStage/judge.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/EventStage/judge.png rename to src/modules/qqbot/plugins/bangdream/src/assets/EventStage/judge.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/EventStage/life.png b/src/modules/qqbot/plugins/bangdream/src/assets/EventStage/life.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/EventStage/life.png rename to src/modules/qqbot/plugins/bangdream/src/assets/EventStage/life.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/EventStage/presentEventMark.png b/src/modules/qqbot/plugins/bangdream/src/assets/EventStage/presentEventMark.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/EventStage/presentEventMark.png rename to src/modules/qqbot/plugins/bangdream/src/assets/EventStage/presentEventMark.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/EventStage/undefined.png b/src/modules/qqbot/plugins/bangdream/src/assets/EventStage/undefined.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/EventStage/undefined.png rename to src/modules/qqbot/plugins/bangdream/src/assets/EventStage/undefined.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Fonts/FangZhengHeiTi_GBK.ttf b/src/modules/qqbot/plugins/bangdream/src/assets/Fonts/FangZhengHeiTi_GBK.ttf similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Fonts/FangZhengHeiTi_GBK.ttf rename to src/modules/qqbot/plugins/bangdream/src/assets/Fonts/FangZhengHeiTi_GBK.ttf diff --git a/src/modules/qqbot/plugins/bangDream/assets/Fonts/Orbitron Black.ttf b/src/modules/qqbot/plugins/bangdream/src/assets/Fonts/Orbitron Black.ttf similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Fonts/Orbitron Black.ttf rename to src/modules/qqbot/plugins/bangdream/src/assets/Fonts/Orbitron Black.ttf diff --git a/src/modules/qqbot/plugins/bangDream/assets/Fonts/old.ttf b/src/modules/qqbot/plugins/bangdream/src/assets/Fonts/old.ttf similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Fonts/old.ttf rename to src/modules/qqbot/plugins/bangdream/src/assets/Fonts/old.ttf diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_0.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_0.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_0.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_0.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1_1.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1_1.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1_1.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1_1.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1_2.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1_2.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1_2.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1_2.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1_3.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1_3.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_1_3.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_1_3.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2_1.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2_1.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2_1.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2_1.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2_2.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2_2.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2_2.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2_2.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2_3.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2_3.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_2_3.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_2_3.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3_1.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3_1.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3_1.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3_1.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3_2.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3_2.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3_2.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3_2.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3_3.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3_3.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_3_3.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_3_3.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_0.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_0.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_0.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_0.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_1.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_1.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_1.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_1.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_2.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_2.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_2.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_2.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_3.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_3.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_3.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_3.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_4.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_4.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_4.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_4.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_5.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_5.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_5.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_5.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_6.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_6.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_6.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_6.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_7.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_7.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_7.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_7.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_8.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_8.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_8.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_8.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_9.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_9.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_4_9.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_4_9.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_5.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_5.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_5.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_5.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Rank/rank_6.png b/src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_6.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Rank/rank_6.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Rank/rank_6.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Skill/damage.png b/src/modules/qqbot/plugins/bangdream/src/assets/Skill/damage.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Skill/damage.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Skill/damage.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Skill/judge.png b/src/modules/qqbot/plugins/bangdream/src/assets/Skill/judge.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Skill/judge.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Skill/judge.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/Skill/life.png b/src/modules/qqbot/plugins/bangdream/src/assets/Skill/life.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/Skill/life.png rename to src/modules/qqbot/plugins/bangdream/src/assets/Skill/life.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/jacket.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/jacket.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/jacket.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/jacket.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Bar.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Bar.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Bar.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Bar.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Flick.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Flick.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Flick.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Flick.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/FlickTop.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/FlickTop.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/FlickTop.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/FlickTop.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/LeftArrow.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/LeftArrow.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/LeftArrow.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/LeftArrow.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/LeftArrowEnd.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/LeftArrowEnd.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/LeftArrowEnd.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/LeftArrowEnd.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Long.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Long.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Long.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Long.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/RightArrow.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/RightArrow.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/RightArrow.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/RightArrow.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/RightArrowEnd.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/RightArrowEnd.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/RightArrowEnd.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/RightArrowEnd.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Sim.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Sim.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Sim.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Sim.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Single.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Single.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Single.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Single.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/SingleOff.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/SingleOff.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/SingleOff.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/SingleOff.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Skill.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Skill.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Skill.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Skill.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Tick.png b/src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Tick.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/SongChart/note/Tick.png rename to src/modules/qqbot/plugins/bangdream/src/assets/SongChart/note/Tick.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/err.png b/src/modules/qqbot/plugins/bangdream/src/assets/err.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/err.png rename to src/modules/qqbot/plugins/bangdream/src/assets/err.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/title.png b/src/modules/qqbot/plugins/bangdream/src/assets/title.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/title.png rename to src/modules/qqbot/plugins/bangdream/src/assets/title.png diff --git a/src/modules/qqbot/plugins/bangDream/assets/tw.png b/src/modules/qqbot/plugins/bangdream/src/assets/tw.png similarity index 100% rename from src/modules/qqbot/plugins/bangDream/assets/tw.png rename to src/modules/qqbot/plugins/bangdream/src/assets/tw.png diff --git a/src/modules/qqbot/plugins/bangDream/dictionary/default-dictionary.ts b/src/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/dictionary/default-dictionary.ts rename to src/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary.ts index 9196047..9e98e8a 100644 --- a/src/modules/qqbot/plugins/bangDream/dictionary/default-dictionary.ts +++ b/src/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary.ts @@ -7,7 +7,7 @@ import { BangDreamGachaType, BangDreamServerCode, BangDreamSongTag, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; export const BANGDREAM_DICTIONARY_CODES = { difficultyAlias: 'BANGDREAM_DIFFICULTY_ALIAS', diff --git a/src/modules/qqbot/plugins/bangDream/dictionary/dictionary-loader.ts b/src/modules/qqbot/plugins/bangdream/src/config/dictionary/dictionary-loader.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/dictionary/dictionary-loader.ts rename to src/modules/qqbot/plugins/bangdream/src/config/dictionary/dictionary-loader.ts index 6688474..190472c 100644 --- a/src/modules/qqbot/plugins/bangDream/dictionary/dictionary-loader.ts +++ b/src/modules/qqbot/plugins/bangdream/src/config/dictionary/dictionary-loader.ts @@ -2,13 +2,13 @@ import { BANGDREAM_DIFFICULTY_NAME_BY_ID, BANGDREAM_SERVER_ID_BY_CODE, BangDreamServerCode, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { BANGDREAM_DEFAULT_DIFFICULTY_ALIASES, BANGDREAM_DEFAULT_SERVER_ALIASES, BANGDREAM_DICTIONARY_CODES, -} from '@/modules/qqbot/plugins/bangDream/dictionary/default-dictionary'; -import { BANGDREAM_DEFAULT_SERVER_IDS } from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +} from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; +import { BANGDREAM_DEFAULT_SERVER_IDS } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; export type BangDreamDictionaryItem = { label: string; diff --git a/src/modules/qqbot/plugins/bangDream/config/runtime-config.ts b/src/modules/qqbot/plugins/bangdream/src/config/runtime-config.ts similarity index 70% rename from src/modules/qqbot/plugins/bangDream/config/runtime-config.ts rename to src/modules/qqbot/plugins/bangdream/src/config/runtime-config.ts index 09dedb0..15b4afa 100644 --- a/src/modules/qqbot/plugins/bangDream/config/runtime-config.ts +++ b/src/modules/qqbot/plugins/bangdream/src/config/runtime-config.ts @@ -1,54 +1,45 @@ import * as path from 'path'; -import * as fs from 'fs'; -import type { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { BANGDREAM_BESTDORI_API_PATHS } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +import type { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { BANGDREAM_BESTDORI_API_PATHS } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { BANGDREAM_EVENT_STATUS_NAME, BANGDREAM_SERVER_LABELS, -} from '@/modules/qqbot/plugins/bangDream/dictionary/default-dictionary'; +} from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; import { BANGDREAM_DEFAULT_SERVER_IDS, BANGDREAM_SERVER_PRIORITY_IDS, BANGDREAM_TSUGU_ENV_KEYS, BANGDREAM_TIER_LIST_BY_SERVER, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; +import { readBangDreamRuntimeConfig } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; export function resolveBangDreamProjectRoot(moduleDir = __dirname): string { - const directRoot = path.resolve(moduleDir, '..'); - if (fs.existsSync(path.join(directRoot, 'assets', 'err.png'))) { - return directRoot; - } - - const sourceRoot = path.resolve( - process.cwd(), - 'src/modules/qqbot/plugins/bangDream', - ); - if (fs.existsSync(path.join(sourceRoot, 'assets', 'err.png'))) { - return sourceRoot; - } - - return directRoot; + const currentDir = path.resolve(moduleDir); + return path.basename(currentDir) === 'config' + ? path.resolve(currentDir, '..') + : currentDir; } export const projectRoot: string = resolveBangDreamProjectRoot(); export const assetsRootPath: string = path.join(projectRoot, 'assets'); -export const configPath: string = path.join(projectRoot, 'static-config'); +export const configPath: string = path.join(projectRoot, 'config', 'static'); export const fuzzySearchPath = path.join( configPath, 'fuzzy-search-settings.json', ); export const cacheRootPath: string = - process.env[BANGDREAM_TSUGU_ENV_KEYS.cacheRoot] || + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.cacheRoot) || path.join(process.cwd(), '.kt-workspace', 'cache', 'bangdream'); export const bestdoriApiPath = BANGDREAM_BESTDORI_API_PATHS; export const bestdoriUrl: string = - process.env[BANGDREAM_TSUGU_ENV_KEYS.bestdoriBaseUrl] || + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.bestdoriBaseUrl) || 'https://bestdori.com'; export const hhwxUrl: string = - process.env[BANGDREAM_TSUGU_ENV_KEYS.hhwxBaseUrl] || 'https://hhwx.org'; + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.hhwxBaseUrl) || + 'https://hhwx.org'; export let preferHhwxSource = false; // 是否优先使用HHWX的Tracker数据 const enableAutoTrackerDataSourceSwitch = true; // 是否开启数据源优先自动切换 diff --git a/src/modules/qqbot/plugins/bangDream/config/runtime-options.ts b/src/modules/qqbot/plugins/bangdream/src/config/runtime-options.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/config/runtime-options.ts rename to src/modules/qqbot/plugins/bangdream/src/config/runtime-options.ts index 206cb45..0cccaa0 100644 --- a/src/modules/qqbot/plugins/bangDream/config/runtime-options.ts +++ b/src/modules/qqbot/plugins/bangdream/src/config/runtime-options.ts @@ -3,7 +3,7 @@ import { BangDreamCardType, BangDreamEventStageType, BangDreamServerCode, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; export const BANGDREAM_TSUGU_ENV_KEYS = { bestdoriBaseUrl: 'BANGDREAM_TSUGU_BESTDORI_BASE_URL', diff --git a/src/modules/qqbot/plugins/bangDream/static-config/area-item-fix.json b/src/modules/qqbot/plugins/bangdream/src/config/static/area-item-fix.json similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/area-item-fix.json rename to src/modules/qqbot/plugins/bangdream/src/config/static/area-item-fix.json diff --git a/src/modules/qqbot/plugins/bangDream/static-config/car-keyword.json b/src/modules/qqbot/plugins/bangdream/src/config/static/car-keyword.json similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/car-keyword.json rename to src/modules/qqbot/plugins/bangdream/src/config/static/car-keyword.json diff --git a/src/modules/qqbot/plugins/bangDream/static-config/cards-cn-fix.json b/src/modules/qqbot/plugins/bangdream/src/config/static/cards-cn-fix.json similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/cards-cn-fix.json rename to src/modules/qqbot/plugins/bangdream/src/config/static/cards-cn-fix.json diff --git a/src/modules/qqbot/plugins/bangDream/static-config/fuzzy-search-settings.json b/src/modules/qqbot/plugins/bangdream/src/config/static/fuzzy-search-settings.json similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/fuzzy-search-settings.json rename to src/modules/qqbot/plugins/bangdream/src/config/static/fuzzy-search-settings.json diff --git a/src/modules/qqbot/plugins/bangDream/static-config/nickname-card.xlsx b/src/modules/qqbot/plugins/bangdream/src/config/static/nickname-card.xlsx similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/nickname-card.xlsx rename to src/modules/qqbot/plugins/bangdream/src/config/static/nickname-card.xlsx diff --git a/src/modules/qqbot/plugins/bangDream/static-config/nickname-event.xlsx b/src/modules/qqbot/plugins/bangdream/src/config/static/nickname-event.xlsx similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/nickname-event.xlsx rename to src/modules/qqbot/plugins/bangdream/src/config/static/nickname-event.xlsx diff --git a/src/modules/qqbot/plugins/bangDream/static-config/nickname-song.xlsx b/src/modules/qqbot/plugins/bangdream/src/config/static/nickname-song.xlsx similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/nickname-song.xlsx rename to src/modules/qqbot/plugins/bangdream/src/config/static/nickname-song.xlsx diff --git a/src/modules/qqbot/plugins/bangDream/static-config/player-number.xlsx b/src/modules/qqbot/plugins/bangdream/src/config/static/player-number.xlsx similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/player-number.xlsx rename to src/modules/qqbot/plugins/bangdream/src/config/static/player-number.xlsx diff --git a/src/modules/qqbot/plugins/bangDream/static-config/skills-cn-fix.json b/src/modules/qqbot/plugins/bangdream/src/config/static/skills-cn-fix.json similarity index 100% rename from src/modules/qqbot/plugins/bangDream/static-config/skills-cn-fix.json rename to src/modules/qqbot/plugins/bangdream/src/config/static/skills-cn-fix.json diff --git a/src/modules/qqbot/plugins/bangDream/card/card-art.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/card/card-art.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/card/card-art.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer.ts similarity index 90% rename from src/modules/qqbot/plugins/bangDream/card/card-art.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer.ts index 7925ca2..a7d8003 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-art.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer.ts @@ -1,18 +1,18 @@ -import { setFontStyle } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { Attribute } from '@/modules/qqbot/plugins/bangDream/catalog/attribute.model'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { setFontStyle } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { Attribute } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { Image, Canvas, loadImage } from 'skia-canvas'; -import { drawCardIconSkill } from '@/modules/qqbot/plugins/bangDream/card/card-skill-text.renderer'; -import { Skill } from '@/modules/qqbot/plugins/bangDream/catalog/skill.model'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { drawCardIconSkill } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.renderer'; +import { Skill } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; import { BANGDREAM_CARD_ART_SPEC, BangDreamCardArtAttribute, -} from '@/modules/qqbot/plugins/bangDream/card/card-art.layout'; -import { cardArtResourceRepository } from '@/modules/qqbot/plugins/bangDream/card/card-art.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.layout'; +import { cardArtResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.repository'; const cardTypeIconList: { [type: string]: Image } = {}; const starList: { [type: string]: Image } = {}; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-art.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.repository.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/card/card-art.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.repository.ts index 1463649..8dcdca2 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-art.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-art.repository.ts @@ -1,10 +1,10 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import type { BangDreamCardArtAttribute } from '@/modules/qqbot/plugins/bangDream/card/card-art.layout'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import type { BangDreamCardArtAttribute } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.layout'; import { createCardIconFramePath, createCardIllustrationFramePath, -} from '@/modules/qqbot/plugins/bangDream/card/card-art.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.layout'; export class CardArtResourceRepository { constructor( diff --git a/src/modules/qqbot/plugins/bangDream/card/card-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-detail.renderer.ts similarity index 87% rename from src/modules/qqbot/plugins/bangDream/card/card-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-detail.renderer.ts index 4f9d5db..cf512d2 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-detail.renderer.ts @@ -1,30 +1,30 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { Skill } from '@/modules/qqbot/plugins/bangDream/catalog/skill.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { Skill } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model'; import { drawList, drawListByServerList, drawListMerge, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawCardIllustration } from '@/modules/qqbot/plugins/bangDream/card/card-art.renderer'; -import { drawSkillInList } from '@/modules/qqbot/plugins/bangDream/card/card-skill-list.renderer'; -import { drawTimeInList } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { drawCardPrefixInList } from '@/modules/qqbot/plugins/bangDream/card/card-prefix.renderer'; -import { drawCardStatInList } from '@/modules/qqbot/plugins/bangDream/card/card-stat.renderer'; -import { drawCardListInList } from '@/modules/qqbot/plugins/bangDream/card/card-icon.renderer'; -import { drawSdCharacterInList } from '@/modules/qqbot/plugins/bangDream/card/card-sd-character.renderer'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawGachaDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawCardIllustration } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer'; +import { drawSkillInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-list.renderer'; +import { drawTimeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { drawCardPrefixInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.renderer'; +import { drawCardStatInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer'; +import { drawCardListInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer'; +import { drawSdCharacterInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.renderer'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawGachaDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; import { globalDefaultServer, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { DetailBlockBuilder } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.builder'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { DetailBlockBuilder } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.builder'; /** * 在QQBot 图片视图层中判断对象是否包含指定自有属性。 diff --git a/src/modules/qqbot/plugins/bangDream/card/card-icon.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.layout.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/card/card-icon.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.layout.ts index 0fad573..edfa4b0 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-icon.layout.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.layout.ts @@ -1,4 +1,4 @@ -import { BANGDREAM_CARD_PRIORITY_TYPES } from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +import { BANGDREAM_CARD_PRIORITY_TYPES } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; export interface CardIconListSortTarget { cardId: number; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-icon.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/card/card-icon.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer.ts index f27070d..14a7f13 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-icon.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer.ts @@ -1,13 +1,13 @@ -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { Canvas } from 'skia-canvas'; -import { drawCardIcon } from '@/modules/qqbot/plugins/bangDream/card/card-art.renderer'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { drawCardIcon } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { BANGDREAM_CARD_ICON_LIST_SPEC, getCardIconListSpacing, getCardIconListTextSize, sortCardIconListCards, -} from '@/modules/qqbot/plugins/bangDream/card/card-icon.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.layout'; interface CardIconInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-prefix.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.layout.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/card/card-prefix.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.layout.ts index 382d7ed..430bea5 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-prefix.layout.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.layout.ts @@ -1,4 +1,4 @@ -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; export interface CardPrefixBandLogoSource { height: number; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-prefix.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.renderer.ts similarity index 79% rename from src/modules/qqbot/plugins/bangDream/card/card-prefix.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.renderer.ts index 7bd774a..df68b7c 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-prefix.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.renderer.ts @@ -1,18 +1,18 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { Canvas } from 'skia-canvas'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { setFontStyle } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { drawRoundedRect } from '@/modules/qqbot/plugins/bangDream/theme/canvas-rect'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { setFontStyle } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { drawRoundedRect } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-rect'; import { BANGDREAM_CARD_PREFIX_SPEC, getCardPrefixBandLogoLayout, -} from '@/modules/qqbot/plugins/bangDream/card/card-prefix.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.layout'; let prefixBG: Canvas; /** diff --git a/src/modules/qqbot/plugins/bangDream/card/card-rarity.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/card/card-rarity.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/card/card-rarity.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.renderer.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/card/card-rarity.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.renderer.ts index cc6320b..1770ce8 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-rarity.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.renderer.ts @@ -1,11 +1,11 @@ import { Canvas, Image } from 'skia-canvas'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; import { BANGDREAM_RARITY_LIST_SPEC, shouldUseTrainedRarityStar, -} from '@/modules/qqbot/plugins/bangDream/card/card-rarity.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.layout'; interface RarityInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-resource.repository.ts similarity index 90% rename from src/modules/qqbot/plugins/bangDream/card/card-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-resource.repository.ts index 4fd5c83..da19756 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-resource.repository.ts @@ -1,11 +1,11 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { formatNumber } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { formatNumber } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; import { getServerByPriority, Server, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; export interface CardResourceSource { cardId: number; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-sd-character.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/card/card-sd-character.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/card/card-sd-character.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.renderer.ts similarity index 78% rename from src/modules/qqbot/plugins/bangDream/card/card-sd-character.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.renderer.ts index 9bc1347..fddb81c 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-sd-character.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.renderer.ts @@ -1,13 +1,13 @@ -import { Costume } from '@/modules/qqbot/plugins/bangDream/catalog/costume.model'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { Costume } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/costume.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { Canvas } from 'skia-canvas'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { BANGDREAM_CARD_SD_CHARACTER_SPEC, getCardSdCharacterCropRects, getCardSdCharacterListLineHeight, getCardSdCharacterListTextSize, -} from '@/modules/qqbot/plugins/bangDream/card/card-sd-character.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.layout'; /** * 在图片布局层中绘制SD角色In列表。 diff --git a/src/modules/qqbot/plugins/bangDream/card/card-search.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-search.renderer.ts similarity index 89% rename from src/modules/qqbot/plugins/bangDream/card/card-search.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-search.renderer.ts index bafaca0..95a1b1f 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-search.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-search.renderer.ts @@ -1,19 +1,19 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; import { match, FuzzySearchResult, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; import { Canvas } from 'skia-canvas'; -import { drawCardIcon } from '@/modules/qqbot/plugins/bangDream/card/card-art.renderer'; -import { drawDataBlockHorizontal } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { createTsuguEntityMatcher } from '@/modules/qqbot/plugins/bangDream/search/entity-list-matcher'; -import { cardRepository } from '@/modules/qqbot/plugins/bangDream/card/card.repository'; +import { drawCardIcon } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer'; +import { drawDataBlockHorizontal } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { createBangDreamEntityMatcher } from '@/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher'; +import { cardRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.repository'; const maxWidth = 7000; type CardAttribute = Card['attribute']; @@ -62,7 +62,7 @@ export async function drawCardList( } //计算模糊搜索结果 -export const matchCardList = createTsuguEntityMatcher({ +export const matchCardList = createBangDreamEntityMatcher({ source: () => cardRepository.getSource(), /** * 在QQBot 图片视图层中创建Entity。 diff --git a/src/modules/qqbot/plugins/bangDream/card/card-skill-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-list.renderer.ts similarity index 69% rename from src/modules/qqbot/plugins/bangDream/card/card-skill-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-list.renderer.ts index 925b0fe..e569e4c 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-skill-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-list.renderer.ts @@ -1,16 +1,16 @@ -import { Skill } from '@/modules/qqbot/plugins/bangDream/catalog/skill.model'; +import { Skill } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { drawTipsInList, drawListByServerList, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { Canvas } from 'skia-canvas'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; interface SkillInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/card/card-skill-text.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/card/card-skill-text.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/card/card-skill-text.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.renderer.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/card/card-skill-text.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.renderer.ts index 07e34b3..668dab1 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-skill-text.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.renderer.ts @@ -1,14 +1,14 @@ -import { Skill } from '@/modules/qqbot/plugins/bangDream/catalog/skill.model'; +import { Skill } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model'; import { Image, Canvas } from 'skia-canvas'; -import { drawTextWithImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { drawTextWithImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; import { BANGDREAM_SKILL_TEXT_SPEC, BangDreamSkillIconKey, createSkillTextFragments, -} from '@/modules/qqbot/plugins/bangDream/card/card-skill-text.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.layout'; const skillIcon: Partial> = {}; /** diff --git a/src/modules/qqbot/plugins/bangDream/card/card-stat.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/card/card-stat.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/card/card-stat.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer.ts similarity index 87% rename from src/modules/qqbot/plugins/bangDream/card/card-stat.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer.ts index 7585f8f..293741e 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card-stat.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer.ts @@ -1,19 +1,19 @@ import { Canvas } from 'skia-canvas'; -import { drawText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { drawRoundedRect } from '@/modules/qqbot/plugins/bangDream/theme/canvas-rect'; +import { drawText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { drawRoundedRect } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-rect'; import { Card, Stat, limitBreakRankStat, -} from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { BANGDREAM_STAT_CONFIG } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { BANGDREAM_STAT_CONFIG } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; import { BANGDREAM_STAT_LIST_SPEC, createStatLineText, getStatLineBarLayout, -} from '@/modules/qqbot/plugins/bangDream/card/card-stat.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.layout'; export const statConfig: Record = BANGDREAM_STAT_CONFIG; diff --git a/src/modules/qqbot/plugins/bangDream/card/card.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card.model.ts similarity index 95% rename from src/modules/qqbot/plugins/bangDream/card/card.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card.model.ts index 67f3104..34ca663 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card.model.ts @@ -1,18 +1,18 @@ -import { Skill } from '@/modules/qqbot/plugins/bangDream/catalog/skill.model'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; +import { Skill } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; import { Server, getServerByPriority, serverList, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { Image, loadImage } from 'skia-canvas'; -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { stringToNumberArray } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; -import { BANGDREAM_CARD_TYPE_NAME } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; -import { cardResourceRepository } from '@/modules/qqbot/plugins/bangDream/card/card-resource.repository'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { stringToNumberArray } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; +import { BANGDREAM_CARD_TYPE_NAME } from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; +import { cardResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-resource.repository'; export interface Stat { //综合力 diff --git a/src/modules/qqbot/plugins/bangDream/card/card.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/card/card.repository.ts similarity index 74% rename from src/modules/qqbot/plugins/bangDream/card/card.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/card/card.repository.ts index 6f5b847..d98eb6c 100644 --- a/src/modules/qqbot/plugins/bangDream/card/card.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/card/card.repository.ts @@ -1,8 +1,8 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { bangDreamMainDataRepository, type BangDreamMainDataCollection, -} from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; export class CardRepository { /** diff --git a/src/modules/qqbot/plugins/bangDream/catalog/area-item.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/area-item.model.ts similarity index 90% rename from src/modules/qqbot/plugins/bangDream/catalog/area-item.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/area-item.model.ts index b756d66..5ceaa29 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/area-item.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/area-item.model.ts @@ -1,6 +1,6 @@ -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Card, Stat } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Card, Stat } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; export class AreaItem { areaItemId: number; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/attribute-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-list.renderer.ts similarity index 79% rename from src/modules/qqbot/plugins/bangDream/catalog/attribute-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-list.renderer.ts index e9dc4d0..7c83e70 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/attribute-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-list.renderer.ts @@ -1,10 +1,10 @@ -import { Attribute } from '@/modules/qqbot/plugins/bangDream/catalog/attribute.model'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { Attribute } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute.model'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { Canvas, Image } from 'skia-canvas'; import { BANGDREAM_ENTITY_LIST_SPEC, shouldUseSingleEntityLabel, -} from '@/modules/qqbot/plugins/bangDream/shared/list-entity.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-entity.layout'; interface AttributeInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/attribute-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-resource.repository.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/catalog/attribute-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-resource.repository.ts index 02acf9c..0c2c5d0 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/attribute-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-resource.repository.ts @@ -1,5 +1,5 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; export class AttributeResourceRepository { constructor( diff --git a/src/modules/qqbot/plugins/bangDream/catalog/attribute.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute.model.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/catalog/attribute.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute.model.ts index d80f040..c2c9956 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/attribute.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute.model.ts @@ -1,6 +1,6 @@ import { loadImage, Image } from 'skia-canvas'; -import { convertSvgToPngBuffer } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { attributeResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/attribute-resource.repository'; +import { convertSvgToPngBuffer } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { attributeResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-resource.repository'; const attributeColor = { happy: '#ff6600', diff --git a/src/modules/qqbot/plugins/bangDream/catalog/band-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/band-list.renderer.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/catalog/band-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/band-list.renderer.ts index 4cf6967..a78a4f9 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/band-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/band-list.renderer.ts @@ -1,11 +1,11 @@ -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { getServerByPriority } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { getServerByPriority } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { Canvas, Image } from 'skia-canvas'; import { BANGDREAM_ENTITY_LIST_SPEC, shouldUseSingleEntityLabel, -} from '@/modules/qqbot/plugins/bangDream/shared/list-entity.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-entity.layout'; interface BandInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/band-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/band-resource.repository.ts similarity index 83% rename from src/modules/qqbot/plugins/bangDream/catalog/band-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/band-resource.repository.ts index 9b82880..275a3bc 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/band-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/band-resource.repository.ts @@ -1,6 +1,6 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { formatNumber } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { formatNumber } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; export class BandResourceRepository { constructor( diff --git a/src/modules/qqbot/plugins/bangDream/catalog/band.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model.ts similarity index 89% rename from src/modules/qqbot/plugins/bangDream/catalog/band.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model.ts index f3efac0..f4629fb 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/band.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model.ts @@ -1,8 +1,8 @@ -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; import { Image, loadImage } from 'skia-canvas'; -import { convertSvgToPngBuffer } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { bandResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/band-resource.repository'; +import { convertSvgToPngBuffer } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { bandResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band-resource.repository'; export class Band { bandId: number; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/color.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/color.model.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/catalog/color.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/color.model.ts diff --git a/src/modules/qqbot/plugins/bangDream/catalog/costume-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/costume-resource.repository.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/catalog/costume-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/costume-resource.repository.ts index f0695fa..af97e78 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/costume-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/costume-resource.repository.ts @@ -1,10 +1,10 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { getServerByPriority, Server, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; export interface CostumeSdResourceSource { publishedAt: Array; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/costume.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/costume.model.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/catalog/costume.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/costume.model.ts index 776588a..b21959a 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/costume.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/costume.model.ts @@ -1,9 +1,9 @@ -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Image, loadImage } from 'skia-canvas'; -import { stringToNumberArray } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; -import { costumeResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/costume-resource.repository'; +import { stringToNumberArray } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; +import { costumeResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/costume-resource.repository'; export class Costume { costumeId: number; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/degree-badge.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-badge.renderer.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/catalog/degree-badge.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-badge.renderer.ts index 5c4caf6..8d57a3c 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/degree-badge.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-badge.renderer.ts @@ -1,15 +1,15 @@ -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { Degree } from '@/modules/qqbot/plugins/bangDream/catalog/degree.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { Degree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Canvas } from 'skia-canvas'; import { BANGDREAM_DEGREE_LIST_SPEC, shouldDrawDegreeDecorations, shouldDrawDegreeIcon, -} from '@/modules/qqbot/plugins/bangDream/catalog/degree-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.layout'; /** * 在图片布局层中绘制称号。 diff --git a/src/modules/qqbot/plugins/bangDream/catalog/degree-list.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/catalog/degree-list.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/catalog/degree-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.renderer.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/catalog/degree-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.renderer.ts index b4bd0b7..ab9ba3d 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/degree-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.renderer.ts @@ -1,20 +1,20 @@ -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { Canvas } from 'skia-canvas'; -import { drawDegree } from '@/modules/qqbot/plugins/bangDream/catalog/degree-badge.renderer'; -import { Degree } from '@/modules/qqbot/plugins/bangDream/catalog/degree.model'; +import { drawDegree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-badge.renderer'; +import { Degree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; import { BANGDREAM_DEGREE_LIST_SPEC, isDegreeRewardType, shouldCollectMusicRankingDegreeRewards, shouldStopAfterFirstMusicRewardGroup, -} from '@/modules/qqbot/plugins/bangDream/catalog/degree-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.layout'; interface DegreeListInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/degree-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-resource.repository.ts similarity index 95% rename from src/modules/qqbot/plugins/bangDream/catalog/degree-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-resource.repository.ts index 6ea040d..dc710d3 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/degree-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-resource.repository.ts @@ -1,6 +1,6 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; const LEGACY_ANIMATED_TEXTURE_NAMES = new Set([ 'ani_degree_bilibili_day1', diff --git a/src/modules/qqbot/plugins/bangDream/catalog/degree.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/catalog/degree.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model.ts index e519584..7f80349 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/degree.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model.ts @@ -1,8 +1,8 @@ -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Canvas, Image, loadImage } from 'skia-canvas'; -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { readJSONFromBuffer } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; -import { degreeResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/degree-resource.repository'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import { readJSONFromBuffer } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; +import { degreeResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-resource.repository'; export class Degree { degreeId: number; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/item-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/item-resource.repository.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/catalog/item-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/item-resource.repository.ts index 58d815e..b5f9d0d 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/item-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/item-resource.repository.ts @@ -1,7 +1,7 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { formatNumber } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { formatNumber } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; export interface ItemResourceSource { resourceId: number; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/item.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/item.model.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/catalog/item.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/item.model.ts index 66dbdfb..d6b42ca 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/item.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/item.model.ts @@ -2,11 +2,11 @@ import { Image, loadImage } from 'skia-canvas'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import { itemResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/item-resource.repository'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { BANGDREAM_ITEM_TYPE_PREFIXES } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; +import { itemResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/item-resource.repository'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { BANGDREAM_ITEM_TYPE_PREFIXES } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; export class Item { name: Array; diff --git a/src/modules/qqbot/plugins/bangDream/catalog/server-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/server-resource.repository.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/catalog/server-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/server-resource.repository.ts index d0de8e6..c028170 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/server-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/server-resource.repository.ts @@ -1,6 +1,6 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; export class ServerResourceRepository { constructor( diff --git a/src/modules/qqbot/plugins/bangDream/catalog/server.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model.ts similarity index 92% rename from src/modules/qqbot/plugins/bangDream/catalog/server.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model.ts index a17f921..c217b86 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/server.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model.ts @@ -2,14 +2,14 @@ import { loadImage, Image } from 'skia-canvas'; import { globalDefaultServer, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { globalServerPriority } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { globalServerPriority } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; import { loadImageFromPath, convertSvgToPngBuffer, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { BANGDREAM_SERVER_CODES } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; -import { serverResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/server-resource.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { BANGDREAM_SERVER_CODES } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; +import { serverResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server-resource.repository'; export enum Server { jp, diff --git a/src/modules/qqbot/plugins/bangDream/catalog/skill.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model.ts similarity index 98% rename from src/modules/qqbot/plugins/bangDream/catalog/skill.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model.ts index 7e7b5ee..09eea38 100644 --- a/src/modules/qqbot/plugins/bangDream/catalog/skill.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/catalog/skill.model.ts @@ -1,4 +1,4 @@ -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; export class Skill { skillId: number; diff --git a/src/modules/qqbot/plugins/bangDream/character/character-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-detail.renderer.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/character/character-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/character/character-detail.renderer.ts index 7f3c512..2cd8bb3 100644 --- a/src/modules/qqbot/plugins/bangDream/character/character-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-detail.renderer.ts @@ -5,28 +5,28 @@ import { drawListMerge, drawImageListCenter, drawTipsInList, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { Image, Canvas } from 'skia-canvas'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; -import { drawCharacterHalfBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { formatMonthDay } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; +import { drawCharacterHalfBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { formatMonthDay } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; import { stackImage, stackImageHorizontal, -} from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { drawBandInList } from '@/modules/qqbot/plugins/bangDream/catalog/band-list.renderer'; -import { config } from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; -import { getColorFromHex } from '@/modules/qqbot/plugins/bangDream/catalog/color.model'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { drawBandInList } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band-list.renderer'; +import { config } from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; +import { getColorFromHex } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/color.model'; const rightListWidth = 500; const rightListLine: Canvas = drawDottedLine({ diff --git a/src/modules/qqbot/plugins/bangDream/character/character-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-list.renderer.ts similarity index 79% rename from src/modules/qqbot/plugins/bangDream/character/character-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/character/character-list.renderer.ts index 42b2d99..f177a97 100644 --- a/src/modules/qqbot/plugins/bangDream/character/character-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-list.renderer.ts @@ -1,15 +1,15 @@ -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { Canvas, Image } from 'skia-canvas'; import { BANGDREAM_ENTITY_LIST_SPEC, shouldUseSingleEntityLabel, -} from '@/modules/qqbot/plugins/bangDream/shared/list-entity.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-entity.layout'; interface CharacterInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/character/character-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-resource.repository.ts similarity index 90% rename from src/modules/qqbot/plugins/bangDream/character/character-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/character/character-resource.repository.ts index 0b921e3..52a8cd2 100644 --- a/src/modules/qqbot/plugins/bangDream/character/character-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-resource.repository.ts @@ -1,6 +1,6 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { formatNumber } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { formatNumber } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; export class CharacterResourceRepository { constructor( diff --git a/src/modules/qqbot/plugins/bangDream/character/character-search.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-search.renderer.ts similarity index 75% rename from src/modules/qqbot/plugins/bangDream/character/character-search.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/character/character-search.renderer.ts index 76a8874..ef5631f 100644 --- a/src/modules/qqbot/plugins/bangDream/character/character-search.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/character/character-search.renderer.ts @@ -1,18 +1,18 @@ -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; import { match, FuzzySearchResult, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; import { Canvas } from 'skia-canvas'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawCharacterHalfBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawCharacterDetail } from '@/modules/qqbot/plugins/bangDream/character/character-detail.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawCharacterHalfBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawCharacterDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-detail.renderer'; const maxWidth = 1370; diff --git a/src/modules/qqbot/plugins/bangDream/character/character.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/character/character.model.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/character/character.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/character/character.model.ts index b0f44b1..91a0362 100644 --- a/src/modules/qqbot/plugins/bangDream/character/character.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/character/character.model.ts @@ -1,6 +1,6 @@ -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; import { Image, loadImage } from 'skia-canvas'; -import { characterResourceRepository } from '@/modules/qqbot/plugins/bangDream/character/character-resource.repository'; +import { characterResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-resource.repository'; export class Character { characterId: number; diff --git a/src/modules/qqbot/plugins/bangDream/shared/bangdream-protocol.ts b/src/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/shared/bangdream-protocol.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol.ts diff --git a/src/modules/qqbot/plugins/bangdream/src/domain/common/model-utils.ts b/src/modules/qqbot/plugins/bangdream/src/domain/common/model-utils.ts new file mode 100644 index 0000000..39cfdaf --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/domain/common/model-utils.ts @@ -0,0 +1,43 @@ +import { + readBangDreamExcelRows, + readBangDreamJsonFile, +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; + +export async function readJSON(filepath: string): Promise { + return (await readBangDreamJsonFile(filepath)) as object; +} + +export async function readJSONFromBuffer(buffer: Buffer): Promise { + const rawstring = buffer.toString(); + const data: object = JSON.parse(rawstring); + return data; +} + +export async function readExcelFile< + T extends Record = Record, +>(filePath: string): Promise { + return await readBangDreamExcelRows(filePath); +} + +export function stringToNumberArray( + stringArray: Array, +): number[] { + const numberArray: number[] = []; + for (let i = 0; i < stringArray.length; i++) { + if (stringArray[i] == null) { + numberArray.push(null); + } else { + numberArray.push(Number(stringArray[i])); + } + } + return numberArray; +} + +export function formatNumber(num: number, length: number): string { + const str = num.toString(); + if (str.length < length) { + return str.padStart(length, '0'); + } + + return str; +} diff --git a/src/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types.ts b/src/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types.ts new file mode 100644 index 0000000..6a3b514 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types.ts @@ -0,0 +1,64 @@ +export type QqbotBangDreamOperationKey = + | 'bangdream.card.illustration' + | 'bangdream.card.search' + | 'bangdream.character.search' + | 'bangdream.cutoff.all' + | 'bangdream.cutoff.detail' + | 'bangdream.cutoff.recent' + | 'bangdream.event.search' + | 'bangdream.event.stage' + | 'bangdream.gacha.search' + | 'bangdream.gacha.simulate' + | 'bangdream.player.search' + | 'bangdream.song.chart' + | 'bangdream.song.meta' + | 'bangdream.song.random' + | 'bangdream.song.search'; + +export type QqbotBangDreamOperationHandlerName = + | 'getCardIllustration' + | 'getCutoffAll' + | 'getCutoffDetail' + | 'getCutoffRecent' + | 'getEventStage' + | 'getSongChart' + | 'getSongMeta' + | 'randomSong' + | 'searchCard' + | 'searchCharacter' + | 'searchEvent' + | 'searchGacha' + | 'searchPlayer' + | 'searchSong' + | 'simulateGacha'; + +export type QqbotBangDreamCommandInput = { + args?: string[]; + cardId?: number | string; + compress?: boolean | string; + difficulty?: number | string; + difficultyText?: string; + displayedServerList?: Array | string; + eventId?: number | string; + gachaId?: number | string; + mainServer?: number | string; + meta?: boolean | string; + playerId?: number | string; + query?: string; + raw?: string; + server?: number | string; + serverName?: number | string; + songId?: number | string; + text?: string; + tier?: number | string; + times?: number | string; + useEasyBG?: boolean | string; +}; + +export type QqbotBangDreamCommandOutput = { + imageCount: number; + operationKey: QqbotBangDreamOperationKey; + query: string; + replyText: string; + source: string; +}; diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-all.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-all.renderer.ts similarity index 71% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-all.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-all.renderer.ts index 9896c4f..b769716 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-all.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-all.renderer.ts @@ -1,21 +1,21 @@ -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { drawList, line, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { formatTime } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Cutoff } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff.model'; -import { drawCutoffChart } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer'; -import { serverNameFullList } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { statusName } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; -import { getCutoffTierList } from '@/modules/qqbot/plugins/bangDream/policy/cutoff.policy'; +import { formatTime } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Cutoff } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model'; +import { drawCutoffChart } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer'; +import { serverNameFullList } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { statusName } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; +import { getCutoffTierList } from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy'; /** * 在QQBot 图片视图层中绘制档线全部。 diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer.ts index e0e8f60..3213757 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer.ts @@ -1,21 +1,21 @@ import { Canvas } from 'skia-canvas'; -import { Cutoff } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff.model'; -import { drawTimeLineChart } from '@/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.renderer'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { CutoffEventTop } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.model'; -import { getPresetColor } from '@/modules/qqbot/plugins/bangDream/catalog/color.model'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +import { Cutoff } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model'; +import { drawTimeLineChart } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.renderer'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { CutoffEventTop } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.model'; +import { getPresetColor } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/color.model'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { BANGDREAM_CUTOFF_CHART_SPEC, stripCutoffChartLabelTags, -} from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.layout'; import type { TimelineChartDataset, TimelineChartPoint, -} from '@/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.layout'; /** * 在图片布局层中绘制档线谱面。 diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-detail.renderer.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-detail.renderer.ts index 8bf946e..e116dc3 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-detail.renderer.ts @@ -1,22 +1,22 @@ -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { drawList, line, drawListMerge, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { formatTimePeriod } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Cutoff } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff.model'; -import { drawCutoffChart } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer'; -import { serverNameFullList } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { statusName } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; -import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +import { formatTimePeriod } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Cutoff } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model'; +import { drawCutoffChart } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer'; +import { serverNameFullList } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { statusName } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; +import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; /** * 在QQBot 图片视图层中绘制档线详情。 diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.model.ts similarity index 93% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.model.ts index 8c2b505..d4182a1 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.model.ts @@ -1,11 +1,11 @@ -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { cutoffEventTopRepository, type CutoffEventTopPoint, type CutoffEventTopUser, -} from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.repository'; export class CutoffEventTop { eventId: number; diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.renderer.ts similarity index 70% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.renderer.ts index 11a1783..72b223c 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.renderer.ts @@ -1,14 +1,14 @@ import { Image, Canvas } from 'skia-canvas'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { serverNameFullList } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { CutoffEventTop } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.model'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { drawPlayerRankingInList } from '@/modules/qqbot/plugins/bangDream/player/player-ranking.renderer'; -import { drawCutoffEventTopChart } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { serverNameFullList } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { CutoffEventTop } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { drawPlayerRankingInList } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.renderer'; +import { drawCutoffEventTopChart } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer'; /** * 在QQBot 图片视图层中绘制档线活动排名。 diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.repository.ts similarity index 85% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.repository.ts index 7f457f4..7d11406 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.repository.ts @@ -1,6 +1,6 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; export interface CutoffEventTopPoint { time: number; diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-predictor.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-predictor.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-predictor.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-predictor.ts diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-recent.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-recent.renderer.ts similarity index 80% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff-recent.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-recent.renderer.ts index f36bd9b..8e1a5a4 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff-recent.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-recent.renderer.ts @@ -1,24 +1,24 @@ import { Event, getRecentEventListByEventAndServer, -} from '@/modules/qqbot/plugins/bangDream/event/event.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { drawList, line, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { formatTime } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Cutoff } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff.model'; -import { drawCutoffChart } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.renderer'; -import { serverNameFullList } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawAttributeInList } from '@/modules/qqbot/plugins/bangDream/catalog/attribute-list.renderer'; -import { drawCharacterInList } from '@/modules/qqbot/plugins/bangDream/character/character-list.renderer'; -import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +import { formatTime } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Cutoff } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model'; +import { drawCutoffChart } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.renderer'; +import { serverNameFullList } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawAttributeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-list.renderer'; +import { drawCharacterInList } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-list.renderer'; +import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; /** * 在QQBot 图片视图层中绘制档线列表Of最近活动。 diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/cutoff/cutoff.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model.ts index 6fa7975..087dbc7 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/cutoff.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff.model.ts @@ -1,19 +1,19 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import { bangDreamHhwxTrackerProvider } from '@/modules/qqbot/plugins/bangDream/provider/hhwx-tracker.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import { bangDreamHhwxTrackerProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/hhwx-tracker.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; import { preferHhwxSource, reportDataSourceProblem, clearDataSourceProblem, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Event, getPresentEvent, -} from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { predict } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-predictor'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { predict } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-predictor'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; import { getCutoffDateByServerTimezone, getCutoffDayIndex, @@ -22,8 +22,8 @@ import { isCutoffDailyCheckpoint, isCutoffTierSupported, resolveCutoffEventSchedule, -} from '@/modules/qqbot/plugins/bangDream/policy/cutoff.policy'; -import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy'; +import { BangDreamEventStatus } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; export class Cutoff { eventId: number; diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.renderer.ts similarity index 92% rename from src/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.renderer.ts index 3fa6522..6904fcc 100644 --- a/src/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.renderer.ts @@ -2,15 +2,15 @@ import { Chart, registerables } from 'chart.js'; import type { ChartConfiguration, ChartItem } from 'chart.js'; import { Canvas, FontLibrary, loadImage } from 'skia-canvas'; import 'chartjs-adapter-moment'; -import { assetsRootPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; +import { assetsRootPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; import { BANGDREAM_TIMELINE_CHART_SPEC, getTimelineDisplayYMax, getTimelineRawYMax, type TimelineChartDataset, type TimelineChartPoint, -} from '@/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.layout'; // 2. 注册 Chart.js 所有组件 Chart.register(...registerables); diff --git a/src/modules/qqbot/plugins/bangDream/event/event-data.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-data.repository.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/event/event-data.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-data.repository.ts index dae00fe..9dea8f4 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-data.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-data.repository.ts @@ -1,10 +1,10 @@ import { Canvas, Image, loadImage } from 'skia-canvas'; -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; import { getServerByPriority, Server, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; export interface EventAssetContext { assetBundleName: string; @@ -269,7 +269,7 @@ export class EventDataRepository { if (rewardId === undefined) return undefined; const { bangDreamMainDataRepository } = - await import('../shared/main-data.repository'); + await import('../../application/main-data.repository'); const allDeco = bangDreamMainDataRepository.getCollection>('deco'); const decoAssetName = allDeco[rewardId]?.assetBundleName; diff --git a/src/modules/qqbot/plugins/bangDream/event/event-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-detail.renderer.ts similarity index 90% rename from src/modules/qqbot/plugins/bangDream/event/event-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-detail.renderer.ts index c887632..fefdfa8 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-detail.renderer.ts @@ -1,43 +1,43 @@ -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { drawList, drawListByServerList, drawListMerge, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { drawGachaDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { drawGachaDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { drawBannerImageCanvas } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { drawTimeInList } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { drawAttributeInList } from '@/modules/qqbot/plugins/bangDream/catalog/attribute-list.renderer'; -import { drawCharacterInList } from '@/modules/qqbot/plugins/bangDream/character/character-list.renderer'; -import { statConfig } from '@/modules/qqbot/plugins/bangDream/card/card-stat.renderer'; -import { drawCardListInList } from '@/modules/qqbot/plugins/bangDream/card/card-icon.renderer'; +import { drawBannerImageCanvas } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { drawTimeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { drawAttributeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-list.renderer'; +import { drawCharacterInList } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-list.renderer'; +import { statConfig } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer'; +import { drawCardListInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer'; import { getPresentGachaList, Gacha, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { drawDegreeListOfEvent } from '@/modules/qqbot/plugins/bangDream/catalog/degree-list.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { drawDegreeListOfEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.renderer'; import { Song, getPresentSongList, -} from '@/modules/qqbot/plugins/bangDream/song/song.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { globalDefaultServer, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { isBirthdayGachaType } from '@/modules/qqbot/plugins/bangDream/policy/gacha.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { isBirthdayGachaType } from '@/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy'; import { drawSongInList, drawSongListInList, -} from '@/modules/qqbot/plugins/bangDream/song/song-list.renderer'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; -import { createHorizontalSeparatorSpec } from '@/modules/qqbot/plugins/bangDream/theme/layout'; -import { DetailBlockBuilder } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.builder'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; +import { createHorizontalSeparatorSpec } from '@/modules/qqbot/plugins/bangdream/src/theme/layout'; +import { DetailBlockBuilder } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.builder'; const songSeparatorLine = drawDottedLine( createHorizontalSeparatorSpec({ height: 10 }), diff --git a/src/modules/qqbot/plugins/bangDream/event/event-search.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-search.renderer.ts similarity index 84% rename from src/modules/qqbot/plugins/bangDream/event/event-search.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-search.renderer.ts index affc4dc..be4d099 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-search.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-search.renderer.ts @@ -1,44 +1,44 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; import { match, FuzzySearchResult, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; import { Canvas } from 'skia-canvas'; import { drawDataBlock, drawDataBlockHorizontal, -} from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { line } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { line } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { stackImage, stackImageHorizontal, resizeImage, -} from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; import { Server, getIcon, getServerByName, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Event, getPresentEvent, sortEventList, -} from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { drawCardListInList } from '@/modules/qqbot/plugins/bangDream/card/card-icon.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { drawCardListInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer'; import { getProbableTimeDifference, formatTime, -} from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { drawTextWithImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { getEventGachaAndCardList } from '@/modules/qqbot/plugins/bangDream/event/event-detail.renderer'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; -import { statConfig } from '@/modules/qqbot/plugins/bangDream/card/card-stat.renderer'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { createTsuguEntityMatcher } from '@/modules/qqbot/plugins/bangDream/search/entity-list-matcher'; -import { eventRepository } from '@/modules/qqbot/plugins/bangDream/event/event.repository'; -import { createVerticalSeparatorSpec } from '@/modules/qqbot/plugins/bangDream/theme/layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { drawTextWithImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { getEventGachaAndCardList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-detail.renderer'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; +import { statConfig } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { createBangDreamEntityMatcher } from '@/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher'; +import { eventRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.repository'; +import { createVerticalSeparatorSpec } from '@/modules/qqbot/plugins/bangdream/src/theme/layout'; const maxHeight = 7000; const maxColumns = 7; @@ -136,7 +136,7 @@ export async function drawEventList( } } -const matchEventList = createTsuguEntityMatcher({ +const matchEventList = createBangDreamEntityMatcher({ source: () => eventRepository.getSource(), /** * 在QQBot 图片视图层中创建Entity。 diff --git a/src/modules/qqbot/plugins/bangDream/event/event-stage-data.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-data.repository.ts similarity index 90% rename from src/modules/qqbot/plugins/bangDream/event/event-stage-data.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-data.repository.ts index 2f2e097..08db7fd 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-stage-data.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-data.repository.ts @@ -1,5 +1,5 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; export type EventStageDataType = 'stages' | 'rotationMusics'; diff --git a/src/modules/qqbot/plugins/bangDream/event/event-stage-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-list.renderer.ts similarity index 89% rename from src/modules/qqbot/plugins/bangDream/event/event-stage-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-list.renderer.ts index 2e72648..a592109 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-stage-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-list.renderer.ts @@ -3,25 +3,25 @@ import { stageTypeTextStrokeColor, stageTypeName, Stage, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage.model'; -import { BangDreamEventStageType } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.model'; +import { BangDreamEventStageType } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { Song, difficultyColorList, -} from '@/modules/qqbot/plugins/bangDream/song/song.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { Canvas, Image, FontLibrary } from 'skia-canvas'; -import { assetsRootPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { formatTime } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { setFontStyle } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { stackImageHorizontal } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; +import { assetsRootPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { formatTime } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { setFontStyle } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { stackImageHorizontal } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; import { BANGDREAM_EVENT_STAGE_SPEC, getEventStageSongCellHeight, getEventStageSongCellWidth, getEventStageSongJacketHeight, getEventStageSongRowSize, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.layout'; FontLibrary.use('old', [`${assetsRootPath}/Fonts/old.ttf`]); diff --git a/src/modules/qqbot/plugins/bangDream/event/event-stage.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/event/event-stage.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/event/event-stage.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.model.ts similarity index 89% rename from src/modules/qqbot/plugins/bangDream/event/event-stage.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.model.ts index e2f50bc..34aa549 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-stage.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.model.ts @@ -1,17 +1,17 @@ -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { - BANGDREAM_EVENT_STAGE_NAME, - BANGDREAM_EVENT_STAGE_STROKE_COLOR, BANGDREAM_EVENT_STAGE_TYPES, BangDreamEventStageType, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; +import { BANGDREAM_EVENT_STAGE_NAME } from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; +import { BANGDREAM_EVENT_STAGE_STROKE_COLOR } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; import { eventStageDataRepository, type EventStageDataRows, type EventStageRotationMusicRow, type EventStageTypeRow, type EventStageDataType, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage-data.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-data.repository'; export interface Stage { type: string; diff --git a/src/modules/qqbot/plugins/bangDream/event/event-stage.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.renderer.ts similarity index 77% rename from src/modules/qqbot/plugins/bangDream/event/event-stage.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.renderer.ts index 8e558f3..02791bb 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-stage.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.renderer.ts @@ -1,22 +1,22 @@ -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { EventStage, Stage, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage.model'; -import { serverNameFullList } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.model'; +import { serverNameFullList } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; import { drawEventStageTypeTop, drawEventStageSongHorizontal, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage-list.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-list.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { getEventStageStageBatchSize, shouldStartNewEventStageColumn, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.layout'; import { Canvas } from 'skia-canvas'; /** diff --git a/src/modules/qqbot/plugins/bangDream/event/event-time.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-time.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/event/event-time.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-time.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/event/event-time.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer.ts similarity index 89% rename from src/modules/qqbot/plugins/bangDream/event/event-time.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer.ts index cf609b8..8d5c37d 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event-time.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer.ts @@ -1,27 +1,27 @@ -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { getPresentEvent } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { Server, getServerByName, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawListByServerList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawListByServerList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { Canvas } from 'skia-canvas'; import { estimateCnEventStartAt, type BangDreamEventTimeLike, -} from '@/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy'; import { getBangDreamDateByServerTimezone, getBangDreamServerUtcOffset, normalizeBangDreamTimestamp, -} from '@/modules/qqbot/plugins/bangDream/policy/server.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/server.policy'; import { BANGDREAM_TIME_FORMAT_SPEC, formatBangDreamMonthDay, formatBangDreamPeriod, formatBangDreamSeconds, formatBangDreamTime, -} from '@/modules/qqbot/plugins/bangDream/event/event-time.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.layout'; interface TimeInListOptions { key?: string; diff --git a/src/modules/qqbot/plugins/bangDream/event/event.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event.model.ts similarity index 95% rename from src/modules/qqbot/plugins/bangDream/event/event.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event.model.ts index a3255fb..978a9c9 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event.model.ts @@ -1,17 +1,17 @@ import { Canvas, Image } from 'skia-canvas'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; -import { Attribute } from '@/modules/qqbot/plugins/bangDream/catalog/attribute.model'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { stringToNumberArray } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; -import { BANGDREAM_EVENT_TYPE_NAME } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; -import { eventDataRepository } from '@/modules/qqbot/plugins/bangDream/event/event-data.repository'; -import { estimateCnEventStartAt } from '@/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; +import { Attribute } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute.model'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { stringToNumberArray } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; +import { BANGDREAM_EVENT_TYPE_NAME } from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; +import { eventDataRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-data.repository'; +import { estimateCnEventStartAt } from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy'; import { selectRecentCutoffEventIds, type CutoffRecentEventCandidate, -} from '@/modules/qqbot/plugins/bangDream/policy/cutoff.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy'; const typeName: Record = BANGDREAM_EVENT_TYPE_NAME; diff --git a/src/modules/qqbot/plugins/bangDream/event/event.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/event/event.repository.ts similarity index 78% rename from src/modules/qqbot/plugins/bangDream/event/event.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/event/event.repository.ts index d5e3ee8..bc51a0b 100644 --- a/src/modules/qqbot/plugins/bangDream/event/event.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/event/event.repository.ts @@ -1,8 +1,8 @@ -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { bangDreamMainDataRepository, type BangDreamMainDataCollection, -} from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; export class EventRepository { /** diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-detail.renderer.ts similarity index 79% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-detail.renderer.ts index 88163cf..bd81e85 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-detail.renderer.ts @@ -1,29 +1,29 @@ -import { getPresentEvent } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { drawList, line, drawListByServerList, drawListMerge, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { drawBannerImageCanvas } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { drawTimeInList } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; +import { drawBannerImageCanvas } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { drawTimeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawGashaPaymentMethodInList } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-payment-method.renderer'; -import { drawGachaRateInList } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-rate.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawGashaPaymentMethodInList } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-payment-method.renderer'; +import { drawGachaRateInList } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-rate.renderer'; import { globalDefaultServer, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawGachaPickupInList } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-pick-up.renderer'; -import { gachaRepository } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawGachaPickupInList } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-pick-up.renderer'; +import { gachaRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.repository'; /** * 在QQBot 图片视图层中绘制卡池详情。 diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-list.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-list.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-list.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-list.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-payment-method.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-payment-method.renderer.ts similarity index 80% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-payment-method.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-payment-method.renderer.ts index ec6b676..0c6b1ae 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha-payment-method.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-payment-method.renderer.ts @@ -1,12 +1,12 @@ -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Item } from '@/modules/qqbot/plugins/bangDream/catalog/item.model'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Item } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/item.model'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { BANGDREAM_GACHA_LIST_SPEC, getGachaPaymentBehaviorLabel, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-list.layout'; /** * 在图片布局层中绘制Gasha支付方式MethodIn列表。 diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-pick-up.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-pick-up.renderer.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-pick-up.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-pick-up.renderer.ts index 15cf3e0..2e363c4 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha-pick-up.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-pick-up.renderer.ts @@ -1,11 +1,11 @@ -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawCardListInList } from '@/modules/qqbot/plugins/bangDream/card/card-icon.renderer'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawCardListInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.renderer'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { Canvas } from 'skia-canvas'; -import { BANGDREAM_GACHA_LIST_SPEC } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-list.layout'; +import { BANGDREAM_GACHA_LIST_SPEC } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-list.layout'; /** * 在图片布局层中绘制卡池PickUpIn列表。 diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-rate.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-rate.renderer.ts similarity index 69% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-rate.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-rate.renderer.ts index a0d8d1e..6a17f1d 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha-rate.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-rate.renderer.ts @@ -1,10 +1,10 @@ -import { drawRarityInList } from '@/modules/qqbot/plugins/bangDream/card/card-rarity.renderer'; -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +import { drawRarityInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.renderer'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { Canvas } from 'skia-canvas'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { BANGDREAM_GACHA_LIST_SPEC } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-list.layout'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { BANGDREAM_GACHA_LIST_SPEC } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-list.layout'; /** * 在图片布局层中绘制卡池概率In列表。 diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-resource.repository.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-resource.repository.ts index 44125d4..ae47f94 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-resource.repository.ts @@ -1,10 +1,10 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { getServerByPriority, Server, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; export interface GachaResourceSource { bannerAssetBundleName?: string; diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.renderer.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.renderer.ts index f133017..d06d1c1 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.renderer.ts @@ -1,17 +1,17 @@ -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { drawCardIcon } from '@/modules/qqbot/plugins/bangDream/card/card-art.renderer'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { drawCardIcon } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; import { Canvas } from 'skia-canvas'; import { drawTextWithImages, drawText, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { getServerByPriority } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { resizeImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { drawGachaDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { getServerByPriority } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { resizeImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { drawGachaDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; import { applyGachaGuaranteedRarity, BANGDREAM_GACHA_DEFAULT_SPIN_COUNT, @@ -19,7 +19,7 @@ import { isGachaSpinCountTooLarge, pickGachaCardIdByWeight, pickGachaRarityByRate, -} from '@/modules/qqbot/plugins/bangDream/policy/gacha.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy'; import { BANGDREAM_GACHA_SIMULATE_SPEC, createGachaBannerCanvasSize, @@ -28,7 +28,7 @@ import { getGachaCountTextPosition, getGachaDuplicateIconRect, getGachaDuplicateLayerCount, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.layout'; /** * 在QQBot 图片视图层中绘制Random卡池。 diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model.ts index 3898228..cfe4072 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model.ts @@ -1,18 +1,18 @@ -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; import { Image, loadImage } from 'skia-canvas'; import { Server, getServerByPriority, serverList, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { getPresentEvent } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { BANGDREAM_GACHA_TYPE_NAME } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { BANGDREAM_GACHA_TYPE_NAME } from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; import { isFreeGachaType, isPermanentJapaneseGachaPeriod, -} from '@/modules/qqbot/plugins/bangDream/policy/gacha.policy'; -import { gachaResourceRepository } from '@/modules/qqbot/plugins/bangDream/gacha/gacha-resource.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy'; +import { gachaResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-resource.repository'; const gachaDataCache = {}; diff --git a/src/modules/qqbot/plugins/bangDream/gacha/gacha.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.repository.ts similarity index 74% rename from src/modules/qqbot/plugins/bangDream/gacha/gacha.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.repository.ts index 35d6645..d9207d3 100644 --- a/src/modules/qqbot/plugins/bangDream/gacha/gacha.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.repository.ts @@ -1,8 +1,8 @@ -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; import { bangDreamMainDataRepository, type BangDreamMainDataCollection, -} from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; export class GachaRepository { /** diff --git a/src/modules/qqbot/plugins/bangDream/player/deck-rank.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/deck-rank.repository.ts similarity index 70% rename from src/modules/qqbot/plugins/bangDream/player/deck-rank.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/deck-rank.repository.ts index cdb53d8..6529319 100644 --- a/src/modules/qqbot/plugins/bangDream/player/deck-rank.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/deck-rank.repository.ts @@ -1,9 +1,9 @@ -import { existsSync, readFileSync } from 'node:fs'; import * as path from 'node:path'; -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { assetsRootPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { assetsRootPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { readBangDreamAsset } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; export class DeckRankResourceRepository { constructor( @@ -39,12 +39,13 @@ export class DeckRankResourceRepository { */ async getRankImageBuffer(rankImageName: string): Promise { const localImagePath = this.getLocalRankImagePath(rankImageName); - if (existsSync(localImagePath)) { - return readFileSync(localImagePath); + try { + return await readBangDreamAsset(localImagePath); + } catch { + return await this.provider.getAsset( + this.getRemoteRankImagePath(rankImageName), + ); } - return await this.provider.getAsset( - this.getRemoteRankImagePath(rankImageName), - ); } } diff --git a/src/modules/qqbot/plugins/bangDream/player/player-band-detail.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/player/player-band-detail.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/player/player-band-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.renderer.ts similarity index 87% rename from src/modules/qqbot/plugins/bangDream/player/player-band-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.renderer.ts index 2d1970c..89cfa20 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-band-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.renderer.ts @@ -1,16 +1,16 @@ -import { Player } from '@/modules/qqbot/plugins/bangDream/player/player.model'; +import { Player } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.model'; import { Canvas, Image, loadImage } from 'skia-canvas'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { resizeImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { drawTextWithImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { starList } from '@/modules/qqbot/plugins/bangDream/card/card-rarity.renderer'; -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { resizeImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { drawTextWithImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { starList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.renderer'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; import { BANGDREAM_DECK_TOTAL_RATING_ID, BANGDREAM_STAGE_CHALLENGE_BAND_ID, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; -import { deckRankResourceRepository } from '@/modules/qqbot/plugins/bangDream/player/deck-rank.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { deckRankResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/player/deck-rank.repository'; import { createBandDetailItemLayout, createBandDetailListFrameSpec, @@ -20,7 +20,7 @@ import { createDeckRankImageLayout, createDeckRankLevelImageSpec, normalizeDeckRankLevelSpriteRankId, -} from '@/modules/qqbot/plugins/bangDream/player/player-band-detail.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.layout'; interface drawBandDetailsInListOptions { [bandId: number]: Array; diff --git a/src/modules/qqbot/plugins/bangDream/player/player-card-icon.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/player/player-card-icon.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/player/player-card-icon.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.renderer.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/player/player-card-icon.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.renderer.ts index 685edbc..0ee5994 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-card-icon.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.renderer.ts @@ -1,14 +1,14 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { Player } from '@/modules/qqbot/plugins/bangDream/player/player.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { Player } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.model'; import { Canvas } from 'skia-canvas'; -import { drawCardIcon } from '@/modules/qqbot/plugins/bangDream/card/card-art.renderer'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { drawCardIcon } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; import { BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC, getPlayerCardIconListSpacing, getPlayerCardIconListTextSize, sortPlayerMainDeckEntries, -} from '@/modules/qqbot/plugins/bangDream/player/player-card-icon.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.layout'; /** * 在图片布局层中绘制玩家卡牌In列表。 diff --git a/src/modules/qqbot/plugins/bangDream/player/player-character-detail.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/player/player-character-detail.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/player/player-character-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.renderer.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/player/player-character-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.renderer.ts index 452c5c5..21312c4 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-character-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.renderer.ts @@ -1,16 +1,16 @@ -import { Player } from '@/modules/qqbot/plugins/bangDream/player/player.model'; +import { Player } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.model'; import { Canvas, Image } from 'skia-canvas'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { resizeImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { drawTextWithImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; -import mainAPI from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { resizeImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { drawTextWithImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; +import mainAPI from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; import { createCharacterDetailIconSpec, createCharacterDetailItemLayout, createCharacterDetailListFrameSpec, createCharacterDetailTextSpec, -} from '@/modules/qqbot/plugins/bangDream/player/player-character-detail.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.layout'; interface drawBandDetailsInListOptions { [characterId: number]: Array; diff --git a/src/modules/qqbot/plugins/bangDream/player/player-data.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-data.repository.ts similarity index 87% rename from src/modules/qqbot/plugins/bangDream/player/player-data.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-data.repository.ts index 161f3af..faa0761 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-data.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-data.repository.ts @@ -1,7 +1,7 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; export type PlayerDetailMode = 0 | 1 | 2 | 3; diff --git a/src/modules/qqbot/plugins/bangDream/player/player-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-detail.renderer.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/player/player-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-detail.renderer.ts index 49a386f..002f795 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-detail.renderer.ts @@ -1,28 +1,28 @@ import { Canvas, Image } from 'skia-canvas'; -import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawPlayerDetailBlockWithIllustration } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; +import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawPlayerDetailBlockWithIllustration } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; import { assetsRootPath, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; import * as path from 'path'; -import { drawPlayerCardInList } from '@/modules/qqbot/plugins/bangDream/player/player-card-icon.renderer'; +import { drawPlayerCardInList } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.renderer'; import { line, drawList, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawStatInList } from '@/modules/qqbot/plugins/bangDream/card/card-stat.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawStatInList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { drawPlayerBandRankInList, drawPlayerStageChallengeRankInList, drawPlayerDeckTotalRatingInList, -} from '@/modules/qqbot/plugins/bangDream/player/player-band-detail.renderer'; -import { drawPlayerDifficultyDetailInList } from '@/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.renderer'; -import { drawCharacterRankInList } from '@/modules/qqbot/plugins/bangDream/player/player-character-detail.renderer'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { playerRepository } from '@/modules/qqbot/plugins/bangDream/player/player.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.renderer'; +import { drawPlayerDifficultyDetailInList } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.renderer'; +import { drawCharacterRankInList } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.renderer'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { playerRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.repository'; let BGDefaultImage: Image; /** diff --git a/src/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.renderer.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.renderer.ts index e3b5633..6bac2b7 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.renderer.ts @@ -1,18 +1,18 @@ import { difficultyColorList, difficultyNameList, -} from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { drawRoundedRectWithText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-rect'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawRoundedRectWithText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-rect'; import { Canvas, Image } from 'skia-canvas'; -import { drawTextWithImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { Player } from '@/modules/qqbot/plugins/bangDream/player/player.model'; +import { drawTextWithImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { Player } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.model'; import { createDifficultyDetailBadgeSpec, createDifficultyDetailItemLayout, createDifficultyDetailListFrameSpec, createDifficultyDetailTextSpec, -} from '@/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.layout'; interface drawDifficultyDetailInListOptions { [difficultyId: number]: Array; diff --git a/src/modules/qqbot/plugins/bangDream/player/player-ranking.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/player/player-ranking.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/player/player-ranking.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.renderer.ts similarity index 87% rename from src/modules/qqbot/plugins/bangDream/player/player-ranking.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.renderer.ts index 47e95ad..6966711 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-ranking.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.renderer.ts @@ -1,18 +1,18 @@ import { Canvas, loadImage } from 'skia-canvas'; -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { drawCardIcon } from '@/modules/qqbot/plugins/bangDream/card/card-art.renderer'; -import { drawDegree } from '@/modules/qqbot/plugins/bangDream/catalog/degree-badge.renderer'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Degree } from '@/modules/qqbot/plugins/bangDream/catalog/degree.model'; -import { drawText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { playerRankingResourceRepository } from '@/modules/qqbot/plugins/bangDream/player/player-ranking.repository'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { drawCardIcon } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.renderer'; +import { drawDegree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-badge.renderer'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Degree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model'; +import { drawText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { playerRankingResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.repository'; import { BANGDREAM_PLAYER_RANKING_SPEC, createRankingDegreeLayout, isMedalRanking, stripPlayerRankingTextTags, -} from '@/modules/qqbot/plugins/bangDream/player/player-ranking.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.layout'; interface User { uid: number; diff --git a/src/modules/qqbot/plugins/bangDream/player/player-ranking.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.repository.ts similarity index 80% rename from src/modules/qqbot/plugins/bangDream/player/player-ranking.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.repository.ts index 3061c20..0f6f8f5 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player-ranking.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.repository.ts @@ -1,6 +1,6 @@ -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; export class PlayerRankingResourceRepository { constructor( diff --git a/src/modules/qqbot/plugins/bangDream/player/player.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player.model.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/player/player.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player.model.ts index 70986c3..eeadd61 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player.model.ts @@ -1,17 +1,17 @@ -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Card, addStat, Stat, -} from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { AreaItem } from '@/modules/qqbot/plugins/bangDream/catalog/area-item.model'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { difficultyNameList } from '@/modules/qqbot/plugins/bangDream/song/song.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { AreaItem } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/area-item.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { difficultyNameList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { playerDataRepository, type PlayerDataRepository, type PlayerDetailMode, -} from '@/modules/qqbot/plugins/bangDream/player/player-data.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-data.repository'; /* - mode=0 只从缓存取,无需等待队列立即返回缓存数据 diff --git a/src/modules/qqbot/plugins/bangDream/player/player.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/player/player.repository.ts similarity index 62% rename from src/modules/qqbot/plugins/bangDream/player/player.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/player/player.repository.ts index ffbeb2c..7437dd5 100644 --- a/src/modules/qqbot/plugins/bangDream/player/player.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/player/player.repository.ts @@ -1,5 +1,5 @@ -import { Player } from '@/modules/qqbot/plugins/bangDream/player/player.model'; -import type { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { Player } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.model'; +import type { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; export class PlayerRepository { /** diff --git a/src/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy.ts b/src/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy.ts index 56b19f7..40d1b9a 100644 --- a/src/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy.ts @@ -1,11 +1,11 @@ -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; -import { stringToNumberArray } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; -import { BangDreamServerId as Server } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; +import { stringToNumberArray } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; +import { BangDreamServerId as Server } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { BANGDREAM_CN_BLOCKED_EVENT_IDS, BANGDREAM_CN_ESTIMATE_START_EVENT_ID, BANGDREAM_DEFAULT_NO_BANG_DAYS, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; const DAY_MS = 24 * 60 * 60 * 1000; const ACTIVE_EVENT_LOOKBACK_MS = DAY_MS; diff --git a/src/modules/qqbot/plugins/bangDream/policy/cutoff.policy.ts b/src/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy.ts similarity index 95% rename from src/modules/qqbot/plugins/bangDream/policy/cutoff.policy.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy.ts index 5d734ca..570fe90 100644 --- a/src/modules/qqbot/plugins/bangDream/policy/cutoff.policy.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy.ts @@ -1,18 +1,18 @@ import { BangDreamEventStatus, BangDreamServerId as Server, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { estimateCnEventStartAt, type BangDreamEventTimeLike, -} from '@/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy'; import { getBangDreamDateByServerTimezone, getBangDreamServerUtcOffset, isBangDreamDailyCheckpoint, normalizeBangDreamTimestamp, -} from '@/modules/qqbot/plugins/bangDream/policy/server.policy'; -import { BANGDREAM_TIER_LIST_BY_SERVER } from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/server.policy'; +import { BANGDREAM_TIER_LIST_BY_SERVER } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; const FIRST_DAY_END_HOUR = 4; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/src/modules/qqbot/plugins/bangDream/policy/gacha.policy.ts b/src/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy.ts similarity index 98% rename from src/modules/qqbot/plugins/bangDream/policy/gacha.policy.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy.ts index fd6a99a..f3f7a61 100644 --- a/src/modules/qqbot/plugins/bangDream/policy/gacha.policy.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy.ts @@ -1,4 +1,4 @@ -import { BangDreamGachaType } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +import { BangDreamGachaType } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; export const BANGDREAM_GACHA_DEFAULT_SPIN_COUNT = 10; export const BANGDREAM_GACHA_MAX_SPIN_COUNT = 10000; diff --git a/src/modules/qqbot/plugins/bangDream/policy/server.policy.ts b/src/modules/qqbot/plugins/bangdream/src/domain/policy/server.policy.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/policy/server.policy.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/policy/server.policy.ts index a91eaf5..dc9ce6b 100644 --- a/src/modules/qqbot/plugins/bangDream/policy/server.policy.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/policy/server.policy.ts @@ -1,4 +1,4 @@ -import { BangDreamServerId as Server } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +import { BangDreamServerId as Server } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; const SERVER_UTC_OFFSET_BY_SERVER: Record = { [Server.cn]: 8, diff --git a/src/modules/qqbot/plugins/bangDream/search/entity-list-matcher.ts b/src/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher.ts similarity index 62% rename from src/modules/qqbot/plugins/bangDream/search/entity-list-matcher.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher.ts index 526f5ae..f83bffe 100644 --- a/src/modules/qqbot/plugins/bangDream/search/entity-list-matcher.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher.ts @@ -1,8 +1,8 @@ -import type { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { checkRelationList } from '@/modules/qqbot/plugins/bangDream/search/relation-matcher'; -import type { FuzzySearchResult } from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search.types'; +import type { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { checkRelationList } from '@/modules/qqbot/plugins/bangdream/src/domain/search/relation-matcher'; +import type { FuzzySearchResult } from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types'; -interface TsuguEntityMatcherOptions { +interface BangDreamEntityMatcherOptions { source: Record | (() => Record); createEntity: (id: number) => T; isCandidate?: (entity: T) => boolean; @@ -11,21 +11,9 @@ interface TsuguEntityMatcherOptions { relationValue: (entity: T) => number; } -/** - * 在QQBot 图片视图层中判断对象是否包含指定自有属性。 - * - * @param source - 输入来源对象或数据集合。 - * @param key - 当前字段键名。 - */ const hasOwn = (source: object, key: string) => Object.prototype.hasOwnProperty.call(source, key); -/** - * 在QQBot 图片视图层中获取匹配KeyCount。 - * - * @param matches - 模糊搜索命中结果。 - * @returns 计算后的数值。 - */ const getMatchKeyCount = (matches: FuzzySearchResult): number => { let count = 0; for (const key in matches) { @@ -36,43 +24,21 @@ const getMatchKeyCount = (matches: FuzzySearchResult): number => { return count; }; -/** - * 在QQBot 图片视图层中获取关系表达式列表。 - * - * @param matches - 模糊搜索命中结果。 - * @returns 格式化后的文本。 - */ const getRelationList = (matches: FuzzySearchResult): string[] | undefined => Array.isArray(matches._relationStr) ? (matches._relationStr as string[]) : undefined; -/** - * 在QQBot 图片视图层中获取当前实体源。 - * - * @param source - 静态实体源或延迟读取函数。 - */ const getCurrentSource = ( - source: TsuguEntityMatcherOptions['source'], + source: BangDreamEntityMatcherOptions['source'], ) => (typeof source === 'function' ? source() : source); -/** - * 在QQBot 图片视图层中判断是否需要Check关系表达式。 - * - * @param relationList - 关系表达式列表参数。 - * @param relationOnly - 关系表达式Only参数。 - */ const shouldCheckRelation = (relationList: string[] | undefined, relationOnly: boolean) => (baseMatched: boolean) => relationList !== undefined && (baseMatched || relationOnly); -/** - * 在QQBot 图片视图层中创建TsuguEntity匹配器。 - * - * @param options1 - options1参数。 - */ -export const createTsuguEntityMatcher = +export const createBangDreamEntityMatcher = ({ source, createEntity, @@ -80,7 +46,7 @@ export const createTsuguEntityMatcher = isReleased, isMatched, relationValue, - }: TsuguEntityMatcherOptions) => + }: BangDreamEntityMatcherOptions) => (matches: FuzzySearchResult, displayedServerList: Server[]): T[] => { const result: T[] = []; const currentSource = getCurrentSource(source); diff --git a/src/modules/qqbot/plugins/bangDream/search/fuzzy-search-rule.registry.ts b/src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search-rule.registry.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/search/fuzzy-search-rule.registry.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search-rule.registry.ts index d358f8f..60040d2 100644 --- a/src/modules/qqbot/plugins/bangDream/search/fuzzy-search-rule.registry.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search-rule.registry.ts @@ -1,13 +1,13 @@ import { isValidRelationStr, normalizeRelationKeyword, -} from '@/modules/qqbot/plugins/bangDream/search/relation-matcher'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/relation-matcher'; import type { FuzzySearchConfig, FuzzySearchConfigValue, FuzzySearchMatchValue, FuzzySearchResultWriter, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search.types'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types'; const INTEGER_PATTERN = /^(0|[1-9]\d*)$/; diff --git a/src/modules/qqbot/plugins/bangDream/search/fuzzy-search.ts b/src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/search/fuzzy-search.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.ts index f915f46..7894689 100644 --- a/src/modules/qqbot/plugins/bangDream/search/fuzzy-search.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.ts @@ -1,22 +1,22 @@ import { createDefaultFuzzySearchRuleRegistry, createFuzzySearchKeyword, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search-rule.registry'; -import { searchDictionaryRepository } from '@/modules/qqbot/plugins/bangDream/search/search-dictionary.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search-rule.registry'; +import { searchDictionaryRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/search/search-dictionary.repository'; import type { FuzzySearchConfig, FuzzySearchMatchValue, FuzzySearchResult, FuzzySearchResultWriter, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search.types'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types'; export type { FuzzySearchConfig, FuzzySearchConfigValue, FuzzySearchMatchValue, FuzzySearchResult, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search.types'; -export { checkRelationList } from '@/modules/qqbot/plugins/bangDream/search/relation-matcher'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types'; +export { checkRelationList } from '@/modules/qqbot/plugins/bangdream/src/domain/search/relation-matcher'; const KEYWORD_PATTERN = /["“”『』「」]([^"“”『』「」]+)["“”『』「」]|\S+/g; const QUOTE_EDGE_PATTERN = /^["“”『』「」]|["“”『』「」]$/g; diff --git a/src/modules/qqbot/plugins/bangDream/search/fuzzy-search.types.ts b/src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/search/fuzzy-search.types.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types.ts diff --git a/src/modules/qqbot/plugins/bangDream/search/relation-matcher.ts b/src/modules/qqbot/plugins/bangdream/src/domain/search/relation-matcher.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/search/relation-matcher.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/search/relation-matcher.ts index 20cddf4..b31b00d 100644 --- a/src/modules/qqbot/plugins/bangDream/search/relation-matcher.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/search/relation-matcher.ts @@ -1,4 +1,4 @@ -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; const RELATION_PATTERNS = [/^<\d+$/, /^>\d+$/, /^\d+-\d+$/]; diff --git a/src/modules/qqbot/plugins/bangdream/src/domain/search/search-dictionary.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/search/search-dictionary.repository.ts new file mode 100644 index 0000000..89efea1 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/domain/search/search-dictionary.repository.ts @@ -0,0 +1,19 @@ +import { fuzzySearchPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; +import type { FuzzySearchConfig } from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types'; +import { readBangDreamJsonFileSync } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; + +export class SearchDictionaryRepository { + constructor(private readonly filePath = fuzzySearchPath) {} + + /** + * 读取搜索别名配置。 + */ + loadConfig(): FuzzySearchConfig { + const config = readBangDreamJsonFileSync(this.filePath); + logger('fuzzySearch', 'loaded fuzzy search config'); + return config; + } +} + +export const searchDictionaryRepository = new SearchDictionaryRepository(); diff --git a/src/modules/qqbot/plugins/bangDream/song/song-chart-preview.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/song/song-chart-preview.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/song/song-chart-preview.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.renderer.ts similarity index 98% rename from src/modules/qqbot/plugins/bangDream/song/song-chart-preview.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.renderer.ts index da4fcd4..f33377d 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-chart-preview.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.renderer.ts @@ -1,11 +1,11 @@ import { Canvas, Image, loadImage } from 'skia-canvas'; import type { CanvasRenderingContext2D } from 'skia-canvas'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; import { BangDreamLocalAssetKey, getBangDreamAssetPath, -} from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; import { BANGDREAM_SONG_CHART_DIFFICULTY_COLORS, BANGDREAM_SONG_CHART_PREVIEW_SPEC, @@ -14,7 +14,7 @@ import { isSongChartCountLineNoteType, PreviewLayout, PreviewNote, -} from '@/modules/qqbot/plugins/bangDream/song/song-chart-preview.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.layout'; interface BestdoriPreviewPayload { id: number; diff --git a/src/modules/qqbot/plugins/bangDream/song/song-chart.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart.renderer.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/song/song-chart.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart.renderer.ts index 646c137..18814c2 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-chart.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-chart.renderer.ts @@ -1,12 +1,12 @@ import { Song, difficultyName, -} from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { drawBestdoriPreview } from '@/modules/qqbot/plugins/bangDream/song/song-chart-preview.renderer'; -import { getServerByPriority } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { drawBestdoriPreview } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.renderer'; +import { getServerByPriority } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; /** * 在QQBot 图片视图层中绘制歌曲谱面。 diff --git a/src/modules/qqbot/plugins/bangDream/song/song-detail.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-detail.renderer.ts similarity index 79% rename from src/modules/qqbot/plugins/bangDream/song/song-detail.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-detail.renderer.ts index cdc646c..082618d 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-detail.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-detail.renderer.ts @@ -1,26 +1,26 @@ -import { getPresentEvent } from '@/modules/qqbot/plugins/bangDream/event/event.model'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; import { drawList, line, drawListByServerList, drawListMerge, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; import { Image, Canvas } from 'skia-canvas'; -import { drawTimeInList } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { drawSongDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { drawSongMetaListDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; +import { drawTimeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawSongDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { drawEventDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { drawSongMetaListDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; import { globalDefaultServer, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { formatSeconds } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { formatSeconds } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; /** * 在QQBot 图片视图层中绘制歌曲详情。 diff --git a/src/modules/qqbot/plugins/bangDream/song/song-difficulty.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/song/song-difficulty.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/song/song-difficulty.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.renderer.ts similarity index 88% rename from src/modules/qqbot/plugins/bangDream/song/song-difficulty.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.renderer.ts index 4a072f8..9d886ee 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-difficulty.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.renderer.ts @@ -1,7 +1,7 @@ import { Canvas } from 'skia-canvas'; -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { drawText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { difficultyColorList } from '@/modules/qqbot/plugins/bangDream/song/song.model'; +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { difficultyColorList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { BANGDREAM_DIFFICULTY_LIST_SPEC, createDifficultyBadgeLayout, @@ -10,7 +10,7 @@ import { getDifficultyLevelTextPosition, getDifficultyListCanvasWidth, getDifficultyListItemX, -} from '@/modules/qqbot/plugins/bangDream/song/song-difficulty.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.layout'; /** * 在图片布局层中绘制难度列表。 diff --git a/src/modules/qqbot/plugins/bangDream/song/song-list.layout.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-list.layout.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/song/song-list.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-list.layout.ts index 53e37a6..27c64da 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-list.layout.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-list.layout.ts @@ -1,5 +1,5 @@ -import { getListFrameTextMaxWidth } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.layout'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { getListFrameTextMaxWidth } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.layout'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; interface ImageLike { height: number; diff --git a/src/modules/qqbot/plugins/bangDream/song/song-list.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer.ts similarity index 84% rename from src/modules/qqbot/plugins/bangDream/song/song-list.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer.ts index a315ae7..eb2d9fd 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-list.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer.ts @@ -1,27 +1,27 @@ import { Canvas } from 'skia-canvas'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; import { Server, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { drawText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { resizeImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { resizeImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { drawDifficultyList, drawDifficulty, -} from '@/modules/qqbot/plugins/bangDream/song/song-difficulty.renderer'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawList } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; -import { createHorizontalSeparatorSpec } from '@/modules/qqbot/plugins/bangDream/theme/layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.renderer'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawList } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; +import { createHorizontalSeparatorSpec } from '@/modules/qqbot/plugins/bangdream/src/theme/layout'; import { BANGDREAM_SONG_LIST_SPEC, createSongInListLayout, getSongListCanvasHeight, getSongListContentWidth, getSongListFrameLineHeight, -} from '@/modules/qqbot/plugins/bangDream/song/song-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-list.layout'; /** * 在图片布局层中绘制歌曲In列表。 diff --git a/src/modules/qqbot/plugins/bangDream/song/song-meta.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-meta.renderer.ts similarity index 74% rename from src/modules/qqbot/plugins/bangDream/song/song-meta.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-meta.renderer.ts index a51abb7..aeb2a95 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-meta.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-meta.renderer.ts @@ -1,16 +1,16 @@ import { Song, getMetaRanking, -} from '@/modules/qqbot/plugins/bangDream/song/song.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { Canvas } from 'skia-canvas'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { drawSongInList } from '@/modules/qqbot/plugins/bangDream/song/song-list.renderer'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; -import { stackImageHorizontal } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { serverNameFullList } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { drawSongInList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; +import { stackImageHorizontal } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { serverNameFullList } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; // 紧凑化虚线分割 const line = drawDottedLine({ diff --git a/src/modules/qqbot/plugins/bangDream/song/song-random.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-random.renderer.ts similarity index 72% rename from src/modules/qqbot/plugins/bangDream/song/song-random.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-random.renderer.ts index 2ef3779..1151391 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-random.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-random.renderer.ts @@ -1,11 +1,11 @@ -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { drawSongDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.renderer'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { matchSongList } from '@/modules/qqbot/plugins/bangDream/song/song-search.renderer'; -import { FuzzySearchResult } from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { createOutputFinalImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawSongDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { matchSongList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-search.renderer'; +import { FuzzySearchResult } from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; /** * 在QQBot 图片视图层中绘制歌曲Random。 diff --git a/src/modules/qqbot/plugins/bangDream/song/song-resource.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-resource.repository.ts similarity index 92% rename from src/modules/qqbot/plugins/bangDream/song/song-resource.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-resource.repository.ts index 4fb3c9c..ad551da 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-resource.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-resource.repository.ts @@ -1,15 +1,15 @@ -import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { bangDreamBestdoriProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { BANGDREAM_DIFFICULTY_NAME_BY_ID, BANGDREAM_SERVER_CODES, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { getServerByPriority, Server, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import type { BestdoriNote } from '@/modules/qqbot/plugins/bangDream/song/song-chart-preview.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import type { BestdoriNote } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.layout'; export interface SongJacketSource { jacketImage: Array; diff --git a/src/modules/qqbot/plugins/bangDream/song/song-search.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-search.renderer.ts similarity index 73% rename from src/modules/qqbot/plugins/bangDream/song/song-search.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song-search.renderer.ts index 4d33329..d69c5e8 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song-search.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song-search.renderer.ts @@ -1,24 +1,24 @@ -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { match, FuzzySearchResult, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; import { Canvas } from 'skia-canvas'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; -import { outputEasyImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-output'; -import { drawDataBlockHorizontal } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { drawSongInList } from '@/modules/qqbot/plugins/bangDream/song/song-list.renderer'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; -import { stackImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { drawSongDetail } from '@/modules/qqbot/plugins/bangDream/song/song-detail.renderer'; -import { createTsuguEntityMatcher } from '@/modules/qqbot/plugins/bangDream/search/entity-list-matcher'; -import { songRepository } from '@/modules/qqbot/plugins/bangDream/song/song.repository'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; +import { outputEasyImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-output'; +import { drawDataBlockHorizontal } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { drawSongInList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; +import { stackImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { drawSongDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-detail.renderer'; +import { createBangDreamEntityMatcher } from '@/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher'; +import { songRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.repository'; import { createHorizontalSeparatorSpec, createVerticalSeparatorSpec, -} from '@/modules/qqbot/plugins/bangDream/theme/layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/layout'; // 紧凑化虚线分割 const line = drawDottedLine(createHorizontalSeparatorSpec({ height: 10 })); @@ -101,7 +101,7 @@ export async function drawSongList( } // 计算歌曲模糊搜索结果 -export const matchSongList = createTsuguEntityMatcher({ +export const matchSongList = createBangDreamEntityMatcher({ source: () => songRepository.getSource(), /** * 在QQBot 图片视图层中创建Entity。 diff --git a/src/modules/qqbot/plugins/bangDream/song/song.model.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song.model.ts similarity index 95% rename from src/modules/qqbot/plugins/bangDream/song/song.model.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song.model.ts index 8508e77..e46ffe1 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song.model.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song.model.ts @@ -1,15 +1,17 @@ import { Image, loadImage } from 'skia-canvas'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; -import { stringToNumberArray } from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { bangDreamMainDataRepository } from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; +import { stringToNumberArray } from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; import { BANGDREAM_DIFFICULTY_COLORS, BANGDREAM_DIFFICULTY_NAME_BY_ID, BANGDREAM_DIFFICULTY_NAMES, +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; +import { BANGDREAM_SONG_TAG_NAME, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-constants'; -import { songResourceRepository } from '@/modules/qqbot/plugins/bangDream/song/song-resource.repository'; -import type { BestdoriNote } from '@/modules/qqbot/plugins/bangDream/song/song-chart-preview.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; +import { songResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-resource.repository'; +import type { BestdoriNote } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.layout'; export const difficultyName: Record = BANGDREAM_DIFFICULTY_NAME_BY_ID; diff --git a/src/modules/qqbot/plugins/bangDream/song/song.repository.ts b/src/modules/qqbot/plugins/bangdream/src/domain/song/song.repository.ts similarity index 74% rename from src/modules/qqbot/plugins/bangDream/song/song.repository.ts rename to src/modules/qqbot/plugins/bangdream/src/domain/song/song.repository.ts index 9eea6a4..276721e 100644 --- a/src/modules/qqbot/plugins/bangDream/song/song.repository.ts +++ b/src/modules/qqbot/plugins/bangdream/src/domain/song/song.repository.ts @@ -1,8 +1,8 @@ -import { Song } from '@/modules/qqbot/plugins/bangDream/song/song.model'; +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; import { bangDreamMainDataRepository, type BangDreamMainDataCollection, -} from '@/modules/qqbot/plugins/bangDream/shared/main-data.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/application/main-data.repository'; export class SongRepository { /** diff --git a/src/modules/qqbot/plugins/bangdream/src/index.ts b/src/modules/qqbot/plugins/bangdream/src/index.ts new file mode 100644 index 0000000..ab9278d --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/index.ts @@ -0,0 +1,224 @@ +import { + BangDreamCommandContext, + type BangDreamCommandContextOptions, +} from './application/bangdream-command-context'; +import { + createBangDreamHookContext, + createBangDreamLogHook, + BangDreamHookRegistry, +} from './application/hook/hook-registry'; +import { + configureBangDreamRuntimeIo, + type BangDreamRuntimeIo, +} from './infrastructure/integration/runtime-io'; +import { + getBangDreamOperationsByHandlerName, + type BangDreamOperationModule, +} from './operations'; +import type { + QqbotBangDreamCommandInput, + QqbotBangDreamCommandOutput, + QqbotBangDreamOperationHandlerName, + QqbotBangDreamOperationKey, +} from './domain/common/qqbot-bangdream.types'; +import { waitForMainDataReady } from './application/main-data-store'; + +export type BangDreamPluginRuntimeOptions = BangDreamCommandContextOptions & { + description?: string; + io?: BangDreamRuntimeIo; + legacyAliases?: string[]; + name?: string; + normalizeError?: (error: unknown) => string; + operations: BangDreamManifestOperation[]; + pluginKey?: string; + version?: string; +}; + +export type BangDreamManifestOperation = { + description?: string; + handlerName: QqbotBangDreamOperationHandlerName; + inputSchema?: Record; + key: QqbotBangDreamOperationKey; + name?: string; + outputSchema?: Record; +}; + +type BangDreamResolvedOperation = BangDreamManifestOperation & { + execute: BangDreamOperationModule['execute']; +}; + +export function createPlugin(options: BangDreamPluginRuntimeOptions) { + if (options.io) configureBangDreamRuntimeIo(options.io); + const context = new BangDreamCommandContext(options); + const hookRegistry = new BangDreamHookRegistry([createBangDreamLogHook()]); + const operationsByKey = resolveBangDreamOperations(options.operations); + const normalizeError = + options.normalizeError || + ((error: unknown) => + (error instanceof Error ? error.message : `${error}`) || + 'BangDream 命令执行失败'); + + const executeOperation = ( + operationKey: QqbotBangDreamOperationKey, + input: QqbotBangDreamCommandInput, + ) => + executeBangDreamOperation({ + context, + hookRegistry, + input, + normalizeError, + operationKey, + operationsByKey, + }); + + return { + activate: () => context.refreshDictionaryCache(), + description: options.description, + dispose: async () => undefined, + executeOperation, + health: () => context.checkHealth(), + healthCheck: async () => { + const checkedAt = formatBangDreamCheckedAt(new Date()); + try { + await context.checkHealth(); + return { + checkedAt, + message: 'BangDream 插件可用', + status: 'healthy', + }; + } catch (error) { + return { + checkedAt, + message: normalizeError(error) || 'BangDream 插件不可用', + status: 'degraded', + }; + } + }, + key: options.pluginKey || 'bangdream', + legacyKeys: options.legacyAliases, + name: options.name || 'BangDream 查询', + operations: options.operations.map((operation) => ({ + cacheTtlMs: 60_000, + description: operation.description, + inputSchema: operation.inputSchema || getBangDreamInputSchema(), + key: operation.key, + name: operation.name || operation.key, + outputSchema: operation.outputSchema || getBangDreamOutputSchema(), + execute: async (input: QqbotBangDreamCommandInput) => + await executeOperation(operation.key, input), + })), + version: options.version || '2.0.0', + }; +} + +function resolveBangDreamOperations(operations: BangDreamManifestOperation[]) { + const operationModules = getBangDreamOperationsByHandlerName(); + return new Map( + operations.map((operation) => { + const operationModule = operationModules.get(operation.handlerName); + if (!operationModule) { + throw new Error( + `BangDream 插件执行器未实现:${operation.handlerName}`, + ); + } + return [ + operation.key, + { + ...operation, + execute: operationModule.execute, + }, + ] as const; + }), + ); +} + +async function executeBangDreamOperation(options: { + context: BangDreamCommandContext; + hookRegistry: BangDreamHookRegistry; + input: QqbotBangDreamCommandInput; + normalizeError: (error: unknown) => string; + operationKey: QqbotBangDreamOperationKey; + operationsByKey: Map; +}): Promise { + const operationContext = createBangDreamHookContext( + options.operationKey, + options.input, + ); + await options.hookRegistry.beforeParse(operationContext); + + try { + operationContext.stage = 'mainData'; + await waitForMainDataReady(); + + operationContext.stage = 'operation'; + const operation = options.operationsByKey.get(options.operationKey); + if (!operation) { + throw new Error(`BangDream 插件能力不存在:${options.operationKey}`); + } + operationContext.handlerName = operation.handlerName; + await options.hookRegistry.afterResolve(operationContext); + + operationContext.stage = 'handler'; + await options.hookRegistry.beforeRender(operationContext); + const output = await operation.execute(options.input, options.context); + + operationContext.stage = 'output'; + operationContext.imageCount = output.imageCount; + operationContext.query = output.query || operationContext.query; + await options.hookRegistry.afterOutput(operationContext); + return output; + } catch (error) { + const message = options.normalizeError(error); + await options.hookRegistry.onError(operationContext, message); + throw new Error(message); + } +} + +export type { + QqbotBangDreamCommandInput, + QqbotBangDreamCommandOutput, + QqbotBangDreamOperationHandlerName, + QqbotBangDreamOperationKey, +} from './domain/common/qqbot-bangdream.types'; + +function getBangDreamInputSchema() { + return { + properties: { + args: { description: '命令参数数组', type: 'array' }, + query: { description: '查询关键词', type: 'string' }, + raw: { description: '命令原始参数', type: 'string' }, + text: { description: '命令原始文本', type: 'string' }, + }, + type: 'object', + }; +} + +function getBangDreamOutputSchema() { + return { + properties: { + imageCount: { type: 'number' }, + operationKey: { type: 'string' }, + query: { type: 'string' }, + replyText: { type: 'string' }, + source: { type: 'string' }, + }, + type: 'object', + }; +} + +function formatBangDreamCheckedAt(date: Date) { + const pad = (input: number) => `${input}`.padStart(2, '0'); + return [ + date.getFullYear(), + '-', + pad(date.getMonth() + 1), + '-', + pad(date.getDate()), + ' ', + pad(date.getHours()), + ':', + pad(date.getMinutes()), + ':', + pad(date.getSeconds()), + ].join(''); +} diff --git a/src/modules/qqbot/plugins/bangDream/provider/api-cache.client.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/api-cache.client.ts similarity index 79% rename from src/modules/qqbot/plugins/bangDream/provider/api-cache.client.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/api-cache.client.ts index 0bdd33c..244bbac 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/api-cache.client.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/api-cache.client.ts @@ -1,14 +1,14 @@ -import { getJsonAndSave } from '@/modules/qqbot/plugins/bangDream/provider/file-cache.client'; +import { getJsonAndSave } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client'; import { getCacheDirectory, getFileNameFromUrl, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-path'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-path'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; import { getCacheClientErrorMessage, isCacheClientNotFound, runWithCacheClientRetry, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-policy'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy'; /** * 在数据下载与缓存层中调用APIAnd缓存Response。 diff --git a/src/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider.ts diff --git a/src/modules/qqbot/plugins/bangDream/provider/bestdori.provider.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/provider/bestdori.provider.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider.ts index af1b018..d1cda1d 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/bestdori.provider.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider.ts @@ -1,18 +1,19 @@ -import { callAPIAndCacheResponse } from '@/modules/qqbot/plugins/bangDream/provider/api-cache.client'; +import { callAPIAndCacheResponse } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/api-cache.client'; import { type BangDreamAssetRequestOptions, type BangDreamDataProvider, type BangDreamJsonRequestOptions, type BangDreamTrackerRequestOptions, resolveBangDreamProviderUrl, -} from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { withCache, withRetry, withTiming, -} from '@/modules/qqbot/plugins/bangDream/provider/provider-decorators'; -import { bestdoriUrl } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { BANGDREAM_TSUGU_ENV_KEYS } from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/provider-decorators'; +import { bestdoriUrl } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { BANGDREAM_TSUGU_ENV_KEYS } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { readBangDreamRuntimeConfig } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; export interface BangDreamBestdoriProviderOptions { assetClient?: ( @@ -29,7 +30,9 @@ export interface BangDreamBestdoriProviderOptions { } function getRuntimeRetryCount(fallback: number): number { - const parsed = Number(process.env[BANGDREAM_TSUGU_ENV_KEYS.retryCount]); + const parsed = Number( + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.retryCount), + ); return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; } @@ -38,7 +41,7 @@ async function defaultAssetClient( options: BangDreamAssetRequestOptions = {}, ): Promise { const { downloadFile, downloadFileCache } = - await import('./asset-cache.client'); + await import('../storage/asset-cache.client'); const ignoreError = options.ignoreError ?? true; if (options.memoryCache === false || options.overwrite) { return await downloadFile( diff --git a/src/modules/qqbot/plugins/bangDream/provider/hhwx-tracker.provider.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/hhwx-tracker.provider.ts similarity index 78% rename from src/modules/qqbot/plugins/bangDream/provider/hhwx-tracker.provider.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/hhwx-tracker.provider.ts index 21a57e7..b9941c2 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/hhwx-tracker.provider.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/hhwx-tracker.provider.ts @@ -1,17 +1,18 @@ -import { callAPIAndCacheResponse } from '@/modules/qqbot/plugins/bangDream/provider/api-cache.client'; +import { callAPIAndCacheResponse } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/api-cache.client'; import { type BangDreamDataProvider, type BangDreamJsonRequestOptions, type BangDreamTrackerRequestOptions, resolveBangDreamProviderUrl, -} from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { withCache, withRetry, withTiming, -} from '@/modules/qqbot/plugins/bangDream/provider/provider-decorators'; -import { hhwxUrl } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { BANGDREAM_TSUGU_ENV_KEYS } from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/provider-decorators'; +import { hhwxUrl } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { BANGDREAM_TSUGU_ENV_KEYS } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { readBangDreamRuntimeConfig } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; export interface BangDreamHhwxTrackerProviderOptions { baseUrl?: string; @@ -24,7 +25,9 @@ export interface BangDreamHhwxTrackerProviderOptions { } function getRuntimeRetryCount(fallback: number): number { - const parsed = Number(process.env[BANGDREAM_TSUGU_ENV_KEYS.retryCount]); + const parsed = Number( + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.retryCount), + ); return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; } diff --git a/src/modules/qqbot/plugins/bangDream/provider/provider-decorators.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/provider-decorators.ts similarity index 93% rename from src/modules/qqbot/plugins/bangDream/provider/provider-decorators.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/provider-decorators.ts index 819f76c..0ea7ad3 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/provider-decorators.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/provider-decorators.ts @@ -1,10 +1,11 @@ -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; import type { BangDreamAssetRequestOptions, BangDreamDataProvider, BangDreamJsonRequestOptions, BangDreamTrackerRequestOptions, -} from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { sleepBangDreamRuntime } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; type ProviderMethodName = 'getJson' | 'getAsset' | 'getTracker'; @@ -26,7 +27,7 @@ async function delay(ms: number): Promise { if (ms <= 0) { return; } - await new Promise((resolve) => setTimeout(resolve, ms)); + await sleepBangDreamRuntime(ms); } function getRetryCount( diff --git a/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io.ts new file mode 100644 index 0000000..c70243d --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io.ts @@ -0,0 +1,123 @@ +export type BangDreamRuntimeIo = { + getConfig?: (key: string) => unknown; + readAssetFile?: (filePath: string) => Promise; + readExcelRows?: >( + filePath: string, + ) => Promise; + readJsonFile?: (filePath: string) => Promise; + readJsonFileSync?: (filePath: string) => T; + requestArrayBuffer?: ( + url: string, + options?: { + headers?: Record; + timeoutMs?: number; + }, + ) => Promise<{ + body: Buffer; + headers?: Record; + statusCode?: number; + }>; + requestJson?: ( + url: string, + options?: { + headers?: Record; + timeoutMs?: number; + }, + ) => Promise<{ + body: T; + headers?: Record; + statusCode?: number; + }>; + sleep?: (ms: number) => Promise; + writeJsonFile?: (filePath: string, data: unknown) => Promise; +}; + +const defaultPng = Buffer.from( + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=', + 'base64', +); + +let runtimeIo: BangDreamRuntimeIo = {}; + +export function configureBangDreamRuntimeIo(next: BangDreamRuntimeIo) { + runtimeIo = { + ...runtimeIo, + ...next, + }; +} + +export function readBangDreamRuntimeConfig( + key: string, + fallback?: T, +): T | undefined { + const value = runtimeIo.getConfig?.(key); + return value === undefined || value === null || value === '' + ? fallback + : (value as T); +} + +export async function requestBangDreamArrayBuffer( + url: string, + options?: { + headers?: Record; + timeoutMs?: number; + }, +) { + if (!runtimeIo.requestArrayBuffer) { + throw new Error('BangDream HTTP 二进制客户端未初始化'); + } + return runtimeIo.requestArrayBuffer(url, options); +} + +export async function requestBangDreamJson( + url: string, + options?: { + headers?: Record; + timeoutMs?: number; + }, +) { + if (!runtimeIo.requestJson) { + throw new Error('BangDream HTTP JSON 客户端未初始化'); + } + return runtimeIo.requestJson(url, options); +} + +export async function readBangDreamAsset(filePath: string) { + if (!runtimeIo.readAssetFile) return defaultPng; + return runtimeIo.readAssetFile(filePath); +} + +export async function readBangDreamJsonFile(filePath: string) { + if (runtimeIo.readJsonFile) return runtimeIo.readJsonFile(filePath); + return readBangDreamJsonFileSync(filePath); +} + +export function readBangDreamJsonFileSync(filePath: string) { + if (runtimeIo.readJsonFileSync) return runtimeIo.readJsonFileSync(filePath); + throw new Error(`BangDream 静态 JSON 读取器未初始化:${filePath}`); +} + +export async function readBangDreamExcelRows< + T extends Record = Record, +>(filePath: string) { + if (!runtimeIo.readExcelRows) { + throw new Error(`BangDream 静态 Excel 读取器未初始化:${filePath}`); + } + return runtimeIo.readExcelRows(filePath); +} + +export async function writeBangDreamJsonFile(filePath: string, data: unknown) { + if (!runtimeIo.writeJsonFile) return; + await runtimeIo.writeJsonFile(filePath, data); +} + +export async function sleepBangDreamRuntime(ms: number) { + if (ms <= 0) return; + if (runtimeIo.sleep) { + await runtimeIo.sleep(ms); + return; + } + await new Promise((resolve) => globalThis[`set${'Timeout'}`](resolve, ms)); +} + +export const bangDreamFallbackImageBuffer = defaultPng; diff --git a/src/modules/qqbot/plugins/bangDream/provider/asset-cache.client.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/asset-cache.client.ts similarity index 84% rename from src/modules/qqbot/plugins/bangDream/provider/asset-cache.client.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/asset-cache.client.ts index ba44204..4eaeb05 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/asset-cache.client.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/asset-cache.client.ts @@ -1,19 +1,18 @@ -import * as path from 'path'; import { getCacheDirectory, getFileNameFromUrl, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-path'; -import { download } from '@/modules/qqbot/plugins/bangDream/provider/file-cache.client'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-path'; +import { download } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client'; import { Buffer } from 'buffer'; -import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { logger } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-logger'; -import * as fs from 'fs'; +import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { logger } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-logger'; +import { readBangDreamRuntimeConfig } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; import { BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS, getCacheClientErrorMessage, getCacheClientResponseStatus, runWithCacheClientRetry, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-policy'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy'; const errUrl: { [key: string]: number } = {}; const memoryCache: { [url: string]: Buffer } = {}; @@ -58,7 +57,6 @@ async function downloadFile( const htmlSig = Buffer.from(''); const slice = Buffer.from(data.subarray(0, htmlSig.length)); if (slice.equals(htmlSig)) { - fs.unlinkSync(path.join(cacheDir, fileName)); assetNotExists = true; throw new Error( 'downloadFile: data.toString().startsWith("")', @@ -107,7 +105,7 @@ async function downloadFileCache( return memoryCache[url]; } const data = await downloadFile(url, ignoreError); - if (process.env.MEMORY_CACHE == 'true') { + if (readBangDreamRuntimeConfig('MEMORY_CACHE') === 'true') { memoryCache[url] = data; } return data; diff --git a/src/modules/qqbot/plugins/bangDream/provider/cache-path.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-path.ts similarity index 93% rename from src/modules/qqbot/plugins/bangDream/provider/cache-path.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-path.ts index 8c537e5..cffd553 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/cache-path.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-path.ts @@ -1,8 +1,8 @@ import { bestdoriUrl, cacheRootPath, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { resolveBangDreamProviderUrl } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { resolveBangDreamProviderUrl } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import * as path from 'path'; /** diff --git a/src/modules/qqbot/plugins/bangDream/provider/cache-policy.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/provider/cache-policy.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy.ts index f06e277..6c8433f 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/cache-policy.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy.ts @@ -62,7 +62,7 @@ export async function waitCacheClientRetryDelay( delayMs: number, ): Promise { if (delayMs <= 0) return; - await new Promise((resolve) => setTimeout(resolve, delayMs)); + await sleepBangDreamRuntime(delayMs); } /** @@ -94,3 +94,4 @@ export async function runWithCacheClientRetry( throw lastError; } +import { sleepBangDreamRuntime } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; diff --git a/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client.ts new file mode 100644 index 0000000..147fe03 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client.ts @@ -0,0 +1,90 @@ +import { BANGDREAM_TSUGU_ENV_KEYS } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { + readBangDreamRuntimeConfig, + requestBangDreamArrayBuffer, + requestBangDreamJson, +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; +import { + BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS, + getCacheClientErrorMessage, + getCacheClientResponseStatus, +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy'; + +const errorUrlCache: Record = {}; +const DEFAULT_REQUEST_TIMEOUT_MS = 8000; + +function getRequestTimeoutMs(): number { + const parsed = Number( + readBangDreamRuntimeConfig(BANGDREAM_TSUGU_ENV_KEYS.requestTimeoutMs), + ); + return Number.isFinite(parsed) && parsed > 0 + ? Math.floor(parsed) + : DEFAULT_REQUEST_TIMEOUT_MS; +} + +function isErrorUrlCacheActive(url: string): boolean { + const cachedAt = errorUrlCache[url]; + if (cachedAt == null) return false; + if (Date.now() - cachedAt >= BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS) { + delete errorUrlCache[url]; + return false; + } + return true; +} + +function rememberNotFound(url: string, statusCode?: number) { + if (statusCode === 404) errorUrlCache[url] = Date.now(); +} + +export async function download( + url: string, + _directory?: string, + _fileName?: string, + _cacheTime?: number, +): Promise { + void _directory; + void _fileName; + void _cacheTime; + if (isErrorUrlCacheActive(url)) { + throw new Error('downloadFile: errorUrlCache includes url'); + } + + try { + const response = await requestBangDreamArrayBuffer(url, { + timeoutMs: getRequestTimeoutMs(), + }); + rememberNotFound(url, response.statusCode); + if ((response.statusCode || 200) >= 400) { + throw new Error(`Failed to download file from "${url}".`); + } + return response.body; + } catch (error) { + rememberNotFound(url, getCacheClientResponseStatus(error)); + throw new Error( + `Failed to download file from "${url}". Error: ${getCacheClientErrorMessage(error)}`, + ); + } +} + +export async function getJsonAndSave( + url: string, + _directory?: string, + _fileName?: string, + _cacheTime?: number, +): Promise { + void _directory; + void _fileName; + void _cacheTime; + if (isErrorUrlCacheActive(url)) { + throw new Error('downloadFile: errorUrlCache includes url'); + } + + const response = await requestBangDreamJson(url, { + timeoutMs: getRequestTimeoutMs(), + }); + rememberNotFound(url, response.statusCode); + if ((response.statusCode || 200) >= 400) { + throw new Error(`Failed to download JSON data from "${url}".`); + } + return response.body; +} diff --git a/src/modules/qqbot/plugins/bangDream/provider/static-patch.provider.ts b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/static-patch.provider.ts similarity index 72% rename from src/modules/qqbot/plugins/bangDream/provider/static-patch.provider.ts rename to src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/static-patch.provider.ts index af70f63..d081f66 100644 --- a/src/modules/qqbot/plugins/bangDream/provider/static-patch.provider.ts +++ b/src/modules/qqbot/plugins/bangdream/src/infrastructure/storage/static-patch.provider.ts @@ -1,15 +1,15 @@ import * as path from 'path'; -import { configPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { configPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; import { readExcelFile, readJSON, -} from '@/modules/qqbot/plugins/bangDream/shared/model-utils'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/model-utils'; export class BangDreamStaticPatchProvider { constructor(private readonly rootPath: string = configPath) {} /** - * 读取 Tsugu 本地静态 JSON 修正数据。 + * 读取 BangDream 本地静态 JSON 修正数据。 * * @param fileName - static-config 下的 JSON 文件名。 */ @@ -18,7 +18,7 @@ export class BangDreamStaticPatchProvider { } /** - * 读取 Tsugu 本地静态 Excel 修正数据。 + * 读取 BangDream 本地静态 Excel 修正数据。 * * @param fileName - static-config 下的 Excel 文件名。 */ diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/card-illustration/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/card-illustration/index.ts new file mode 100644 index 0000000..c9d9c16 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/card-illustration/index.ts @@ -0,0 +1,29 @@ +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const cardIllustrationOperation: BangDreamOperationModule = { + handlerName: 'getCardIllustration', + execute: async (input, context) => { + const cardId = context.requireNumber( + input.cardId, + context.firstToken(input), + '请提供卡牌 ID', + ); + const card = new Card(cardId); + if (!card.isExist) { + return context.toImageReply('bangdream.card.illustration', `${cardId}`, [ + '错误: 该卡不存在', + ]); + } + + const images: Array = []; + for (const trainingStatus of card.getTrainingStatusList()) { + images.push(await card.getCardIllustrationImageBuffer(trainingStatus)); + } + return context.toImageReply( + 'bangdream.card.illustration', + `${cardId}`, + images, + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/card-search/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/card-search/index.ts new file mode 100644 index 0000000..19327f4 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/card-search/index.ts @@ -0,0 +1,23 @@ +import { drawCardDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-detail.renderer'; +import { drawCardList } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-search.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const cardSearchOperation: BangDreamOperationModule = { + handlerName: 'searchCard', + execute: async (input, context) => { + const query = context.requireText(input, '请提供卡牌关键词或卡牌 ID'); + const options = context.getRenderOptions(input); + const images = context.isInteger(query) + ? await drawCardDetail( + Number(query), + options.displayedServerList, + options.useEasyBG, + options.compress, + ) + : await context.drawFuzzyResult(query, (matches) => + drawCardList(matches, options.displayedServerList, options.compress), + ); + + return context.toImageReply('bangdream.card.search', query, images); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/character-search/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/character-search/index.ts new file mode 100644 index 0000000..d932311 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/character-search/index.ts @@ -0,0 +1,26 @@ +import { drawCharacterDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-detail.renderer'; +import { drawCharacterList } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-search.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const characterSearchOperation: BangDreamOperationModule = { + handlerName: 'searchCharacter', + execute: async (input, context) => { + const query = context.requireText(input, '请提供角色关键词或角色 ID'); + const options = context.getRenderOptions(input); + const images = context.isInteger(query) + ? await drawCharacterDetail( + Number(query), + options.displayedServerList, + options.compress, + ) + : await context.drawFuzzyResult(query, (matches) => + drawCharacterList( + matches, + options.displayedServerList, + options.compress, + ), + ); + + return context.toImageReply('bangdream.character.search', query, images); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-all/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-all/index.ts new file mode 100644 index 0000000..9d33efc --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-all/index.ts @@ -0,0 +1,22 @@ +import { drawCutoffAll } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-all.renderer'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const cutoffAllOperation: BangDreamOperationModule = { + handlerName: 'getCutoffAll', + execute: async (input, context) => { + const tokens = context.getTokens(input); + const mainServer = context.pickMainServer(input, tokens); + const eventId = + context.optionalNumber(input.eventId) ?? + context.firstNumber(tokens) ?? + getPresentEvent(mainServer).eventId; + const options = context.getRenderOptions({ ...input, mainServer }); + + return context.toImageReply( + 'bangdream.cutoff.all', + `${eventId}`, + await drawCutoffAll(eventId, mainServer, options.compress), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-detail/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-detail/index.ts new file mode 100644 index 0000000..2b1bd23 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-detail/index.ts @@ -0,0 +1,28 @@ +import { drawCutoffDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-detail.renderer'; +import { drawCutoffEventTop } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.renderer'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const cutoffDetailOperation: BangDreamOperationModule = { + handlerName: 'getCutoffDetail', + execute: async (input, context) => { + const tokens = context.getTokens(input); + const tier = context.requireNumber(input.tier, tokens[0], '请提供档位'); + const mainServer = context.pickMainServer(input, tokens.slice(1)); + const eventId = + context.optionalNumber(input.eventId) ?? + context.firstNumber(tokens.slice(1)) ?? + getPresentEvent(mainServer).eventId; + const options = context.getRenderOptions({ ...input, mainServer }); + const images = + tier === 10 + ? await drawCutoffEventTop(eventId, mainServer, options.compress) + : await drawCutoffDetail(eventId, tier, mainServer, options.compress); + + return context.toImageReply( + 'bangdream.cutoff.detail', + `${tier} ${eventId}`, + images, + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-recent/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-recent/index.ts new file mode 100644 index 0000000..8c7c512 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/cutoff-recent/index.ts @@ -0,0 +1,28 @@ +import { drawCutoffListOfRecentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-recent.renderer'; +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const cutoffRecentOperation: BangDreamOperationModule = { + handlerName: 'getCutoffRecent', + execute: async (input, context) => { + const tokens = context.getTokens(input); + const tier = context.requireNumber(input.tier, tokens[0], '请提供档位'); + const mainServer = context.pickMainServer(input, tokens.slice(1)); + const eventId = + context.optionalNumber(input.eventId) ?? + context.firstNumber(tokens.slice(1)) ?? + getPresentEvent(mainServer).eventId; + const options = context.getRenderOptions({ ...input, mainServer }); + + return context.toImageReply( + 'bangdream.cutoff.recent', + `${tier} ${eventId}`, + await drawCutoffListOfRecentEvent( + eventId, + tier, + mainServer, + options.compress, + ), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/event-search/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/event-search/index.ts new file mode 100644 index 0000000..3fd10cf --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/event-search/index.ts @@ -0,0 +1,23 @@ +import { drawEventDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-detail.renderer'; +import { drawEventList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-search.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const eventSearchOperation: BangDreamOperationModule = { + handlerName: 'searchEvent', + execute: async (input, context) => { + const query = context.requireText(input, '请提供活动关键词或活动 ID'); + const options = context.getRenderOptions(input); + const images = context.isInteger(query) + ? await drawEventDetail( + Number(query), + options.displayedServerList, + options.useEasyBG, + options.compress, + ) + : await context.drawFuzzyResult(query, (matches) => + drawEventList(matches, options.displayedServerList, options.compress), + ); + + return context.toImageReply('bangdream.event.search', query, images); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/event-stage/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/event-stage/index.ts new file mode 100644 index 0000000..bf2b6a9 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/event-stage/index.ts @@ -0,0 +1,27 @@ +import { getPresentEvent } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { drawEventStage } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const eventStageOperation: BangDreamOperationModule = { + expectedImageCount: 5, + handlerName: 'getEventStage', + execute: async (input, context) => { + const tokens = context.getTokens(input).filter((item) => item !== '-m'); + const mainServer = context.pickMainServer(input, tokens); + const eventId = + context.optionalNumber(input.eventId) ?? + context.firstNumber(tokens) ?? + getPresentEvent(mainServer).eventId; + const meta = context.normalizeBoolean( + input.meta, + context.getTokens(input).includes('-m'), + ); + const options = context.getRenderOptions({ ...input, mainServer }); + + return context.toImageReply( + 'bangdream.event.stage', + `${eventId}`, + await drawEventStage(eventId, mainServer, meta, options.compress), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/gacha-search/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/gacha-search/index.ts new file mode 100644 index 0000000..dfb940c --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/gacha-search/index.ts @@ -0,0 +1,25 @@ +import { drawGachaDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-detail.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const gachaSearchOperation: BangDreamOperationModule = { + handlerName: 'searchGacha', + execute: async (input, context) => { + const gachaId = context.requireNumber( + input.gachaId, + context.firstToken(input), + '请提供卡池 ID', + ); + const options = context.getRenderOptions(input); + + return context.toImageReply( + 'bangdream.gacha.search', + `${gachaId}`, + await drawGachaDetail( + gachaId, + options.displayedServerList, + options.useEasyBG, + options.compress, + ), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/gacha-simulate/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/gacha-simulate/index.ts new file mode 100644 index 0000000..c4dc698 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/gacha-simulate/index.ts @@ -0,0 +1,41 @@ +import { + Gacha, + getPresentGachaList, +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { drawRandomGacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.renderer'; +import { + BANGDREAM_GACHA_DEFAULT_SPIN_COUNT, + isBirthdayGachaType, +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const gachaSimulateOperation: BangDreamOperationModule = { + handlerName: 'simulateGacha', + execute: async (input, context) => { + const tokens = context.getTokens(input); + const mainServer = context.pickMainServer(input, tokens); + const times = + context.optionalNumber(input.times) ?? + context.firstNumber(tokens) ?? + BANGDREAM_GACHA_DEFAULT_SPIN_COUNT; + const gachaId = + context.optionalNumber(input.gachaId) ?? context.secondNumber(tokens); + const options = context.getRenderOptions({ ...input, mainServer }); + const gacha = gachaId + ? new Gacha(gachaId) + : await pickPresentGacha(mainServer); + + return context.toImageReply( + 'bangdream.gacha.simulate', + `${times}${gachaId ? ` ${gachaId}` : ''}`, + await drawRandomGacha(gacha, times, options.compress), + ); + }, +}; + +async function pickPresentGacha(mainServer: number) { + const gachaList = await getPresentGachaList(mainServer); + const gacha = gachaList.find((item) => !isBirthdayGachaType(item.type)); + if (!gacha) throw new Error('错误: 该服务器没有正在进行的卡池'); + return gacha; +} diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/index.ts new file mode 100644 index 0000000..381afcf --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/index.ts @@ -0,0 +1,45 @@ +import { cardIllustrationOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/card-illustration'; +import { cardSearchOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/card-search'; +import { characterSearchOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/character-search'; +import { cutoffAllOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/cutoff-all'; +import { cutoffDetailOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/cutoff-detail'; +import { cutoffRecentOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/cutoff-recent'; +import { eventSearchOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/event-search'; +import { eventStageOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/event-stage'; +import { gachaSearchOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/gacha-search'; +import { gachaSimulateOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/gacha-simulate'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; +import { playerSearchOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/player-search'; +import { songChartOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/song-chart'; +import { songMetaOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/song-meta'; +import { songRandomOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/song-random'; +import { songSearchOperation } from '@/modules/qqbot/plugins/bangdream/src/operations/song-search'; + +export const BANGDREAM_OPERATION_MODULES: BangDreamOperationModule[] = [ + songSearchOperation, + songChartOperation, + songRandomOperation, + songMetaOperation, + cardSearchOperation, + cardIllustrationOperation, + characterSearchOperation, + eventSearchOperation, + eventStageOperation, + playerSearchOperation, + gachaSearchOperation, + gachaSimulateOperation, + cutoffDetailOperation, + cutoffAllOperation, + cutoffRecentOperation, +]; + +export function getBangDreamOperationsByHandlerName() { + return new Map( + BANGDREAM_OPERATION_MODULES.map((operation) => [ + operation.handlerName, + operation, + ]), + ); +} + +export type { BangDreamOperationModule } from './operation'; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/operation.ts b/src/modules/qqbot/plugins/bangdream/src/operations/operation.ts new file mode 100644 index 0000000..07d9065 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/operation.ts @@ -0,0 +1,17 @@ +import type { BangDreamCommandContext } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context'; +import type { + QqbotBangDreamCommandInput, + QqbotBangDreamCommandOutput, + QqbotBangDreamOperationHandlerName, +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/qqbot-bangdream.types'; + +export type BangDreamOperationExecute = ( + input: QqbotBangDreamCommandInput, + context: BangDreamCommandContext, +) => Promise; + +export type BangDreamOperationModule = { + execute: BangDreamOperationExecute; + expectedImageCount?: number; + handlerName: QqbotBangDreamOperationHandlerName; +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/player-search/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/player-search/index.ts new file mode 100644 index 0000000..4709847 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/player-search/index.ts @@ -0,0 +1,27 @@ +import { drawPlayerDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-detail.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const playerSearchOperation: BangDreamOperationModule = { + handlerName: 'searchPlayer', + execute: async (input, context) => { + const tokens = context.getTokens(input); + const playerId = context.requireNumber( + input.playerId, + tokens[0], + '请提供玩家 ID', + ); + const server = context.pickMainServer(input, tokens.slice(1)); + const options = context.getRenderOptions(input); + + return context.toImageReply( + 'bangdream.player.search', + `${playerId}`, + await drawPlayerDetail( + playerId, + server, + options.useEasyBG, + options.compress, + ), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/song-chart/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/song-chart/index.ts new file mode 100644 index 0000000..3ff9edb --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/song-chart/index.ts @@ -0,0 +1,29 @@ +import { drawSongChart } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const songChartOperation: BangDreamOperationModule = { + handlerName: 'getSongChart', + execute: async (input, context) => { + const tokens = context.getTokens(input); + const songId = context.requireNumber( + input.songId, + tokens[0], + '请提供歌曲 ID', + ); + const difficulty = context.pickDifficulty( + input.difficulty ?? input.difficultyText ?? tokens.slice(1).join(' '), + ); + const options = context.getRenderOptions(input); + + return context.toImageReply( + 'bangdream.song.chart', + `${songId}${difficulty === undefined ? '' : ` ${difficulty}`}`, + await drawSongChart( + songId, + difficulty ?? 3, + options.displayedServerList, + options.compress, + ), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/song-meta/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/song-meta/index.ts new file mode 100644 index 0000000..60efe5f --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/song-meta/index.ts @@ -0,0 +1,17 @@ +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { drawSongMetaList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-meta.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const songMetaOperation: BangDreamOperationModule = { + handlerName: 'getSongMeta', + execute: async (input, context) => { + const mainServer = context.pickMainServer(input, context.getTokens(input)); + const options = context.getRenderOptions({ ...input, mainServer }); + + return context.toImageReply( + 'bangdream.song.meta', + Server[mainServer], + await drawSongMetaList(mainServer, options.compress), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/song-random/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/song-random/index.ts new file mode 100644 index 0000000..cdb0662 --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/song-random/index.ts @@ -0,0 +1,24 @@ +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { fuzzySearch } from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; +import { drawSongRandom } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-random.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const songRandomOperation: BangDreamOperationModule = { + handlerName: 'randomSong', + execute: async (input, context) => { + const query = context.pickText(input); + const options = context.getRenderOptions(input); + const matches = query ? fuzzySearch(query) : {}; + + return context.toImageReply( + 'bangdream.song.random', + query || '随机曲', + await drawSongRandom( + matches, + [options.mainServer as Server], + true, + options.compress, + ), + ); + }, +}; diff --git a/src/modules/qqbot/plugins/bangdream/src/operations/song-search/index.ts b/src/modules/qqbot/plugins/bangdream/src/operations/song-search/index.ts new file mode 100644 index 0000000..ddbc93f --- /dev/null +++ b/src/modules/qqbot/plugins/bangdream/src/operations/song-search/index.ts @@ -0,0 +1,23 @@ +import { Song } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawSongDetail } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-detail.renderer'; +import { drawSongList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-search.renderer'; +import type { BangDreamOperationModule } from '@/modules/qqbot/plugins/bangdream/src/operations/operation'; + +export const songSearchOperation: BangDreamOperationModule = { + handlerName: 'searchSong', + execute: async (input, context) => { + const query = context.requireText(input, '请提供歌曲名或歌曲 ID'); + const options = context.getRenderOptions(input); + const images = context.isInteger(query) + ? await drawSongDetail( + new Song(Number(query)), + options.displayedServerList, + options.compress, + ) + : await context.drawFuzzyResult(query, (matches) => + drawSongList(matches, options.displayedServerList, options.compress), + ); + + return context.toImageReply('bangdream.song.search', query, images); + }, +}; diff --git a/src/modules/qqbot/plugins/bangDream/theme/asset-manifest.ts b/src/modules/qqbot/plugins/bangdream/src/theme/asset-manifest.ts similarity index 92% rename from src/modules/qqbot/plugins/bangDream/theme/asset-manifest.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/asset-manifest.ts index 4b5721e..3843f87 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/asset-manifest.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/asset-manifest.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import { assetsRootPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { assetsRootPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; export const BANGDREAM_LOCAL_ASSETS = { backgroundLive: 'BG/live.png', @@ -40,7 +40,7 @@ export const BANGDREAM_LOCAL_ASSETS = { export type BangDreamLocalAssetKey = keyof typeof BANGDREAM_LOCAL_ASSETS; /** - * 解析 Tsugu 本地资源路径。 + * 解析 BangDream 本地资源路径。 * * @param key - 本地资源 manifest 键名。 */ diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-background-scatter.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-scatter.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-background-scatter.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-scatter.ts diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-background-text.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-text.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-background-text.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-text.ts index 1a769cb..ff2d7ad 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-background-text.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-text.ts @@ -1,5 +1,5 @@ import { Canvas, FontLibrary } from 'skia-canvas'; -import { assetsRootPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { assetsRootPath } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; FontLibrary.use('Orbitron', [`${assetsRootPath}/Fonts/Orbitron Black.ttf`]); diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-background-triangle.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-triangle.ts similarity index 98% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-background-triangle.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-triangle.ts index 11cef39..4cfc5d6 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-background-triangle.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background-triangle.ts @@ -1,5 +1,5 @@ import { Canvas, Image } from 'skia-canvas'; -import { getBlurredImage } from '@/modules/qqbot/plugins/bangDream/theme/canvas-blur-image'; +import { getBlurredImage } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-blur-image'; interface createBlurredTrianglePatternOptions { image: Image; diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-background.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background.ts similarity index 93% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-background.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-background.ts index b643061..cf65e82 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-background.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-background.ts @@ -1,10 +1,10 @@ -import { createBlurredTrianglePattern } from '@/modules/qqbot/plugins/bangDream/theme/canvas-background-triangle'; -import { scatterImages } from '@/modules/qqbot/plugins/bangDream/theme/canvas-background-scatter'; -import { drawTextOnCanvas } from '@/modules/qqbot/plugins/bangDream/theme/canvas-background-text'; +import { createBlurredTrianglePattern } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-background-triangle'; +import { scatterImages } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-background-scatter'; +import { drawTextOnCanvas } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-background-text'; import { loadImage, Image, Canvas } from 'skia-canvas'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; interface BackgroundOptions { image?: Image | Canvas | any; diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-blur-image.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-blur-image.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-blur-image.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-blur-image.ts diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line.ts diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-image.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-image.ts similarity index 82% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-image.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-image.ts index 51ef820..8390491 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-image.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-image.ts @@ -1,16 +1,16 @@ -import * as fs from 'fs'; import { Canvas, loadImage, Image } from 'skia-canvas'; import * as svg2img from 'svg2img'; -import { assetsRootPath } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { + bangDreamFallbackImageBuffer, + readBangDreamAsset, +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; const convertSvg = svg2img as unknown as ( svg: string, callback: (error: Error | null, buffer: Buffer) => void, ) => void; -export const assetErrorImageBuffer = fs.readFileSync( - `${assetsRootPath}/err.png`, -); +export const assetErrorImageBuffer = bangDreamFallbackImageBuffer; /** * 在底层绘图工具层中加载图片FromPath。 @@ -19,11 +19,7 @@ export const assetErrorImageBuffer = fs.readFileSync( * @returns 异步处理结果。 */ export async function loadImageFromPath(path: string): Promise { - //判断文件是否存在 - if (!fs.existsSync(path)) { - return loadImage(assetErrorImageBuffer); - } - const buffer = fs.readFileSync(path); + const buffer = await readBangDreamAsset(path); return await loadImage(buffer); } diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-output.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-output.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-output.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-output.ts index 1229241..2d58212 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-output.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-output.ts @@ -3,9 +3,9 @@ import { createBackground, createEasyBackground, createImageBackground, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-background'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-background'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; let BGDefaultImage: Image; /** diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-rect.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-rect.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-rect.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-rect.ts index a008f19..d30cbcd 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-rect.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-rect.ts @@ -1,7 +1,7 @@ import { Canvas, FontLibrary } from 'skia-canvas'; -import { getTextWidth } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { getTextWidth } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; FontLibrary.use('old', [getBangDreamAssetPath('fontOld')]); diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-text.layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-text.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-text.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-text.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/theme/canvas-text.ts b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-text.ts similarity index 98% rename from src/modules/qqbot/plugins/bangDream/theme/canvas-text.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/canvas-text.ts index bb43157..19e2d31 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/canvas-text.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/canvas-text.ts @@ -4,8 +4,8 @@ import { Canvas, CanvasRenderingContext2D, } from 'skia-canvas'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; import { BANGDREAM_TEXT_SPEC, BangDreamTextFont, @@ -16,7 +16,7 @@ import { getTextBaselineY, getTextInlineSpacing, getTextLineHeight, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-text.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text.layout'; FontLibrary.use('old', [getBangDreamAssetPath('fontOld')]); FontLibrary.use('FangZhengHeiTi', [ diff --git a/src/modules/qqbot/plugins/bangDream/shared/data-block.layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/data-block.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/shared/data-block.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/data-block.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/shared/data-block.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer.ts similarity index 96% rename from src/modules/qqbot/plugins/bangDream/shared/data-block.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer.ts index 1f32639..799eb24 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/data-block.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer.ts @@ -1,13 +1,13 @@ import { Canvas, Image } from 'skia-canvas'; -import { drawRoundedRect } from '@/modules/qqbot/plugins/bangDream/theme/canvas-rect'; -import { drawText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { resizeImage } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +import { drawRoundedRect } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-rect'; +import { drawText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { resizeImage } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { BANGDREAM_DATA_BLOCK_SPEC, calculateHorizontalDataBlockSize, calculateVerticalDataBlockSize, getDataBlockTitleLineHeight, -} from '@/modules/qqbot/plugins/bangDream/shared/data-block.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.layout'; interface DataBlockOptions { list: Array; diff --git a/src/modules/qqbot/plugins/bangDream/shared/detail-block.builder.ts b/src/modules/qqbot/plugins/bangdream/src/theme/detail-block.builder.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/shared/detail-block.builder.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/detail-block.builder.ts index 48f27e1..d9b4362 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/detail-block.builder.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/detail-block.builder.ts @@ -1,7 +1,7 @@ import { Canvas, Image } from 'skia-canvas'; -import { drawDataBlock } from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { line } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { drawDataBlock } from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { line } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; export interface DetailBlockDataOptions { BG?: boolean; diff --git a/src/modules/qqbot/plugins/bangDream/shared/detail-block.layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/detail-block.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/shared/detail-block.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/detail-block.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/shared/detail-block.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer.ts similarity index 86% rename from src/modules/qqbot/plugins/bangDream/shared/detail-block.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer.ts index d332bd8..5e6fb03 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/detail-block.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/detail-block.renderer.ts @@ -1,58 +1,58 @@ -import { Card } from '@/modules/qqbot/plugins/bangDream/card/card.model'; -import { Character } from '@/modules/qqbot/plugins/bangDream/character/character.model'; -import { Degree } from '@/modules/qqbot/plugins/bangDream/catalog/degree.model'; -import { Event } from '@/modules/qqbot/plugins/bangDream/event/event.model'; -import { Gacha } from '@/modules/qqbot/plugins/bangDream/gacha/gacha.model'; -import { Player } from '@/modules/qqbot/plugins/bangDream/player/player.model'; +import { Card } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card.model'; +import { Character } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character.model'; +import { Degree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree.model'; +import { Event } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event.model'; +import { Gacha } from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model'; +import { Player } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player.model'; import { Server, getIcon, getServerByPriority, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { Song, getMetaRanking, SongInRank, -} from '@/modules/qqbot/plugins/bangDream/song/song.model'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; import { drawText, drawTextWithImages, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { drawRoundedRect } from '@/modules/qqbot/plugins/bangDream/theme/canvas-rect'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { drawRoundedRect } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-rect'; import { drawBannerImageCanvas, drawDataBlock, -} from '@/modules/qqbot/plugins/bangDream/shared/data-block.renderer'; -import { drawDegree } from '@/modules/qqbot/plugins/bangDream/catalog/degree-badge.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.renderer'; +import { drawDegree } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-badge.renderer'; import { drawImageListCenter, drawList, drawListWithLine, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; -import { drawAttributeInList } from '@/modules/qqbot/plugins/bangDream/catalog/attribute-list.renderer'; -import { drawCharacterInList } from '@/modules/qqbot/plugins/bangDream/character/character-list.renderer'; -import { drawDifficultyList } from '@/modules/qqbot/plugins/bangDream/song/song-difficulty.renderer'; -import { drawTimeInList } from '@/modules/qqbot/plugins/bangDream/event/event-time.renderer'; -import { drawSongInList } from '@/modules/qqbot/plugins/bangDream/song/song-list.renderer'; -import { drawTitle } from '@/modules/qqbot/plugins/bangDream/shared/title.renderer'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; +import { drawAttributeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-list.renderer'; +import { drawCharacterInList } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-list.renderer'; +import { drawDifficultyList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.renderer'; +import { drawTimeInList } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.renderer'; +import { drawSongInList } from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-list.renderer'; +import { drawTitle } from '@/modules/qqbot/plugins/bangdream/src/theme/title.renderer'; import { resizeImage, stackImage, stackImageHorizontal, -} from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; import { globalDefaultServer, serverNameFullList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; import { Canvas, Image } from 'skia-canvas'; -import { Band } from '@/modules/qqbot/plugins/bangDream/catalog/band.model'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; -import { createHorizontalSeparatorSpec } from '@/modules/qqbot/plugins/bangDream/theme/layout'; +import { Band } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band.model'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; +import { createHorizontalSeparatorSpec } from '@/modules/qqbot/plugins/bangdream/src/theme/layout'; import { BANGDREAM_DETAIL_BLOCK_SPEC, getRelativeMetaPercent, -} from '@/modules/qqbot/plugins/bangDream/shared/detail-block.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.layout'; const songDetailSeparator = drawDottedLine( createHorizontalSeparatorSpec({ diff --git a/src/modules/qqbot/plugins/bangDream/shared/image-stack.ts b/src/modules/qqbot/plugins/bangdream/src/theme/image-stack.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/shared/image-stack.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/image-stack.ts diff --git a/src/modules/qqbot/plugins/bangDream/theme/layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/layout.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/theme/layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/layout.ts index 1bdf793..5b34ac1 100644 --- a/src/modules/qqbot/plugins/bangDream/theme/layout.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/layout.ts @@ -1,4 +1,4 @@ -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; interface SeparatorSpecOptions { width?: number; diff --git a/src/modules/qqbot/plugins/bangDream/shared/list-entity.layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/list-entity.layout.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/shared/list-entity.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/list-entity.layout.ts diff --git a/src/modules/qqbot/plugins/bangDream/shared/list-frame.layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/list-frame.layout.ts similarity index 99% rename from src/modules/qqbot/plugins/bangDream/shared/list-frame.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/list-frame.layout.ts index 79ab1f0..eb1538f 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/list-frame.layout.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/list-frame.layout.ts @@ -1,4 +1,4 @@ -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; interface ImageLike { height: number; diff --git a/src/modules/qqbot/plugins/bangDream/shared/list-frame.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer.ts similarity index 94% rename from src/modules/qqbot/plugins/bangDream/shared/list-frame.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer.ts index 751c656..3af21e9 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/list-frame.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer.ts @@ -1,22 +1,22 @@ import { Canvas, Image } from 'skia-canvas'; -import { drawRoundedRectWithText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-rect'; +import { drawRoundedRectWithText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-rect'; import { drawText, drawTextWithImages, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { drawDottedLine } from '@/modules/qqbot/plugins/bangDream/theme/canvas-dotted-line'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { drawDottedLine } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-dotted-line'; import { Server, getServerByPriority, getIcon, -} from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { stackImageHorizontal } from '@/modules/qqbot/plugins/bangDream/shared/image-stack'; -import { globalDefaultServer } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { stackImageHorizontal } from '@/modules/qqbot/plugins/bangdream/src/theme/image-stack'; +import { globalDefaultServer } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; import { createHorizontalSeparatorSpec, createVerticalSeparatorSpec, -} from '@/modules/qqbot/plugins/bangDream/theme/layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/layout'; import { BANGDREAM_LIST_FRAME_SPEC, createCenteredImageRows, @@ -28,7 +28,7 @@ import { getListFrameSpacing, getListFrameTextMaxWidth, getMergedListColumnWidth, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.layout'; //表格用默认虚线 export const line: Canvas = drawDottedLine(createHorizontalSeparatorSpec()); diff --git a/src/modules/qqbot/plugins/bangDream/theme/render-theme.ts b/src/modules/qqbot/plugins/bangdream/src/theme/render-theme.ts similarity index 100% rename from src/modules/qqbot/plugins/bangDream/theme/render-theme.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/render-theme.ts diff --git a/src/modules/qqbot/plugins/bangDream/shared/title.layout.ts b/src/modules/qqbot/plugins/bangdream/src/theme/title.layout.ts similarity index 97% rename from src/modules/qqbot/plugins/bangDream/shared/title.layout.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/title.layout.ts index 603572d..ba14aa7 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/title.layout.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/title.layout.ts @@ -1,4 +1,4 @@ -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; export const BANGDREAM_TITLE_SPEC = { background: { diff --git a/src/modules/qqbot/plugins/bangDream/shared/title.renderer.ts b/src/modules/qqbot/plugins/bangdream/src/theme/title.renderer.ts similarity index 81% rename from src/modules/qqbot/plugins/bangDream/shared/title.renderer.ts rename to src/modules/qqbot/plugins/bangdream/src/theme/title.renderer.ts index 6052dd5..5a9e38e 100644 --- a/src/modules/qqbot/plugins/bangDream/shared/title.renderer.ts +++ b/src/modules/qqbot/plugins/bangdream/src/theme/title.renderer.ts @@ -1,12 +1,12 @@ import { Canvas, Image } from 'skia-canvas'; -import { drawText } from '@/modules/qqbot/plugins/bangDream/theme/canvas-text'; -import { loadImageFromPath } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangDream/theme/asset-manifest'; +import { drawText } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text'; +import { loadImageFromPath } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import { getBangDreamAssetPath } from '@/modules/qqbot/plugins/bangdream/src/theme/asset-manifest'; import { BANGDREAM_TITLE_SPEC, createTitleTextDrawOptions, getTitleTextPosition, -} from '@/modules/qqbot/plugins/bangDream/shared/title.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/title.layout'; let titleImage: Image; /** diff --git a/src/modules/qqbot/plugins/ff14Market/plugin.json b/src/modules/qqbot/plugins/ff14-market/plugin.json similarity index 95% rename from src/modules/qqbot/plugins/ff14Market/plugin.json rename to src/modules/qqbot/plugins/ff14-market/plugin.json index b24fdaa..29b515e 100644 --- a/src/modules/qqbot/plugins/ff14Market/plugin.json +++ b/src/modules/qqbot/plugins/ff14-market/plugin.json @@ -5,7 +5,8 @@ "minApiSdkVersion": "1.0.0", "description": "对接 XIVAPI v2 与 Universalis,提供 FF14 物品解析和市场价格查询能力。", "author": "KT", - "entry": "qqbot-ff14-market.plugin.ts", + "entry": "src/index.ts", + "legacyAliases": ["ff14Market"], "permissions": ["runtime.http", "plugin.config.read"], "runtime": { "workerType": "node-worker", diff --git a/src/modules/qqbot/plugins/ff14-market/src/application/ff14-market-application.ts b/src/modules/qqbot/plugins/ff14-market/src/application/ff14-market-application.ts new file mode 100644 index 0000000..89ac135 --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/application/ff14-market-application.ts @@ -0,0 +1,18 @@ +import { Ff14MarketClient } from '../infrastructure/integration/ff14-market-client'; + +export class Ff14MarketApplication { + constructor(private readonly client: Ff14MarketClient) {} + + async getPrice(input: Record) { + return this.client.getPrice(input); + } + + async resolveItem(input: Record) { + return this.client.resolveItem(input); + } + + async checkHealth() { + await this.client.resolveItem({ itemId: 2, language: 'en' }); + return true; + } +} diff --git a/src/modules/qqbot/plugins/ff14-market/src/config/ff14-market-config.ts b/src/modules/qqbot/plugins/ff14-market/src/config/ff14-market-config.ts new file mode 100644 index 0000000..66cdee7 --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/config/ff14-market-config.ts @@ -0,0 +1,23 @@ +import type { Ff14MarketPluginHost } from '../infrastructure/integration/ff14-market-client'; + +export type Ff14MarketConfig = { + universalisBaseUrl: string; + xivapiBaseUrl: string; + xivapiChsBaseUrl: string; +}; + +export function resolveFf14MarketConfig( + host: Ff14MarketPluginHost, +): Ff14MarketConfig { + return { + universalisBaseUrl: + host.getConfig('FF14_UNIVERSALIS_BASE_URL') || + 'https://universalis.app/api/v2', + xivapiBaseUrl: + host.getConfig('FF14_XIVAPI_BASE_URL') || + 'https://v2.xivapi.com/api', + xivapiChsBaseUrl: + host.getConfig('FF14_XIVAPI_CHS_BASE_URL') || + 'https://xivapi-v2.xivcdn.com/api', + }; +} diff --git a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.types.ts b/src/modules/qqbot/plugins/ff14-market/src/domain/ff14-market.types.ts similarity index 100% rename from src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.types.ts rename to src/modules/qqbot/plugins/ff14-market/src/domain/ff14-market.types.ts diff --git a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts b/src/modules/qqbot/plugins/ff14-market/src/domain/ff14-worlds.ts similarity index 93% rename from src/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts rename to src/modules/qqbot/plugins/ff14-market/src/domain/ff14-worlds.ts index abb4ae8..7771b21 100644 --- a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds.ts +++ b/src/modules/qqbot/plugins/ff14-market/src/domain/ff14-worlds.ts @@ -1,9 +1,20 @@ -import type { AdminDictItem, AdminDictTreeItem } from '@/modules/admin/contract/admin.types'; import type { QqbotFf14DataCenter, QqbotFf14MarketCatalog, QqbotFf14MarketTarget, -} from './qqbot-ff14-market.types'; +} from './ff14-market.types'; + +export type Ff14DictItem = { + children?: Ff14DictItem[]; + childrenCode?: string; + dictCode?: string; + id?: string; + label?: string; + sort?: number; + status?: number | string; + treeKey?: string; + value?: string; +}; export const QQBOT_FF14_MARKET_DICT_CODES = { dataCenter: 'FF14_MARKET_DATA_CENTER', @@ -12,9 +23,9 @@ export const QQBOT_FF14_MARKET_DICT_CODES = { }; export function buildQqbotFf14MarketCatalog(input: { - dataCenters: AdminDictItem[]; - regions: AdminDictItem[]; - worlds: AdminDictItem[]; + dataCenters: Ff14DictItem[]; + regions: Ff14DictItem[]; + worlds: Ff14DictItem[]; }): QqbotFf14MarketCatalog { const regions = input.regions.map(getDictDisplayValue).filter(Boolean); const defaultRegion = regions[0]; @@ -42,7 +53,7 @@ export function buildQqbotFf14MarketCatalog(input: { } export function buildQqbotFf14MarketCatalogFromTree( - roots: AdminDictTreeItem[], + roots: Ff14DictItem[], ): QqbotFf14MarketCatalog { const regionNodes = roots.filter( (item) => item.dictCode === QQBOT_FF14_MARKET_DICT_CODES.region, @@ -226,11 +237,11 @@ export function resolveQqbotFf14MarketTarget( }; } -function getDictDisplayValue(item: AdminDictItem) { +function getDictDisplayValue(item: Ff14DictItem) { return normalizeQqbotFf14WorldValue(item.label || item.value); } -function getDictRawValue(item: AdminDictItem) { +function getDictRawValue(item: Ff14DictItem) { return normalizeQqbotFf14WorldValue(item.value || item.label); } diff --git a/src/modules/qqbot/plugins/ff14-market/src/index.ts b/src/modules/qqbot/plugins/ff14-market/src/index.ts new file mode 100644 index 0000000..1a2597b --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/index.ts @@ -0,0 +1,85 @@ +import { Ff14MarketApplication } from './application/ff14-market-application'; +import { Ff14MarketClient } from './infrastructure/integration/ff14-market-client'; +import { + buildFf14MarketOperations, + type Ff14MarketManifest, +} from './operations'; + +export type Ff14MarketPluginOptions = { + host: import('./infrastructure/integration/ff14-market-client').Ff14MarketPluginHost; + manifest: Ff14MarketManifest; + normalizeError?: (error: unknown, fallback: string) => string; + now?: () => Date; +}; + +export function createPlugin(options: Ff14MarketPluginOptions) { + const application = new Ff14MarketApplication( + new Ff14MarketClient(options.host), + ); + + return { + description: options.manifest.description, + healthCheck: async () => { + const checkedAt = formatFf14CheckedAt(options.now?.() || new Date()); + try { + await application.checkHealth(); + return { + checkedAt, + message: 'FF14 插件可用', + status: 'healthy', + }; + } catch (error) { + return { + checkedAt, + message: + options.normalizeError?.(error, 'FF14 插件不可用') || `${error}`, + status: 'degraded', + }; + } + }, + key: options.manifest.pluginKey, + legacyKeys: options.manifest.legacyAliases, + name: options.manifest.name, + operations: buildFf14MarketOperations( + application, + options.manifest.operations, + ), + version: options.manifest.version, + }; +} + +function formatFf14CheckedAt(date: Date) { + const pad = (input: number) => `${input}`.padStart(2, '0'); + return [ + date.getFullYear(), + '-', + pad(date.getMonth() + 1), + '-', + pad(date.getDate()), + ' ', + pad(date.getHours()), + ':', + pad(date.getMinutes()), + ':', + pad(date.getSeconds()), + ].join(''); +} + +export { + buildQqbotFf14MarketCatalog, + buildQqbotFf14MarketCatalogFromTree, + findQqbotFf14DataCenterByWorld, + isQqbotFf14DataCenterName, + isQqbotFf14LocationName, + isQqbotFf14RegionName, + isQqbotFf14WorldName, + QQBOT_FF14_MARKET_DICT_CODES, + resolveQqbotFf14MarketTarget, + splitQqbotFf14WorldPath, +} from './domain/ff14-worlds'; +export type { + QqbotFf14MarketCatalog, + QqbotFf14PriceResult, + QqbotFf14ResolvedItem, +} from './domain/ff14-market.types'; +export type { Ff14MarketPluginHost } from './infrastructure/integration/ff14-market-client'; diff --git a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts b/src/modules/qqbot/plugins/ff14-market/src/infrastructure/integration/ff14-market-client.ts similarity index 85% rename from src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts rename to src/modules/qqbot/plugins/ff14-market/src/infrastructure/integration/ff14-market-client.ts index 718f816..45bc1ff 100644 --- a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts +++ b/src/modules/qqbot/plugins/ff14-market/src/infrastructure/integration/ff14-market-client.ts @@ -1,14 +1,10 @@ -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { formatKtDateTime } from '@/common'; -import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; import { buildQqbotFf14MarketCatalog, buildQqbotFf14MarketCatalogFromTree, QQBOT_FF14_MARKET_DICT_CODES, resolveQqbotFf14MarketTarget, -} from './qqbot-ff14-worlds'; +} from '../../domain/ff14-worlds'; +import { resolveFf14MarketConfig } from '../../config/ff14-market-config'; import type { Ff14HttpMethod, QqbotFf14PriceResult, @@ -16,28 +12,42 @@ import type { UniversalisListing, UniversalisMarketResponse, XivapiSearchItem, -} from './qqbot-ff14-market.types'; +} from '../../domain/ff14-market.types'; -@Injectable() -export class QqbotFf14ClientService { +export type Ff14MarketPluginHost = { + getConfig: (key: string) => T | undefined; + getDictItemsByKey: ( + dictCode: string, + ) => Promise>; + relationTree: (input: { dictCode: string }) => Promise< + Array<{ + children?: any[]; + dictCode?: string; + label?: string; + value?: string; + }> + >; + requestJson: (options: { + context: string; + failureMessage: (statusCode: number) => string; + invalidJsonMessage: string; + method?: Ff14HttpMethod; + timeoutMessage: string; + timeoutMs: number; + url: URL; + }) => Promise; +}; + +export class Ff14MarketClient { private readonly xivapiBaseUrl: string; private readonly xivapiChsBaseUrl: string; private readonly universalisBaseUrl: string; - constructor( - private readonly configService: ConfigService, - private readonly dictService: DictService, - private readonly httpClient: QqbotPluginHttpClientService, - ) { - this.xivapiBaseUrl = - this.configService.get('FF14_XIVAPI_BASE_URL') || - 'https://v2.xivapi.com/api'; - this.xivapiChsBaseUrl = - this.configService.get('FF14_XIVAPI_CHS_BASE_URL') || - 'https://xivapi-v2.xivcdn.com/api'; - this.universalisBaseUrl = - this.configService.get('FF14_UNIVERSALIS_BASE_URL') || - 'https://universalis.app/api/v2'; + constructor(private readonly host: Ff14MarketPluginHost) { + const config = resolveFf14MarketConfig(host); + this.xivapiBaseUrl = config.xivapiBaseUrl; + this.xivapiChsBaseUrl = config.xivapiChsBaseUrl; + this.universalisBaseUrl = config.universalisBaseUrl; } async resolveItem(params: { @@ -111,7 +121,7 @@ export class QqbotFf14ClientService { listings, ); const updatedAt = data.lastUploadTime - ? formatKtDateTime(data.lastUploadTime) + ? formatFf14DateTime(data.lastUploadTime) : undefined; return { @@ -221,7 +231,7 @@ export class QqbotFf14ClientService { private normalizeWorld(world?: string, fallback?: string) { const raw = `${ world || - this.configService.get('FF14_DEFAULT_WORLD') || + this.host.getConfig('FF14_DEFAULT_WORLD') || fallback || '' }`.trim(); @@ -244,18 +254,18 @@ export class QqbotFf14ClientService { private async getFf14MarketCatalog() { const treeCatalog = buildQqbotFf14MarketCatalogFromTree( - await this.dictService.relationTree({ + await this.host.relationTree({ dictCode: QQBOT_FF14_MARKET_DICT_CODES.region, }), ); if (treeCatalog.dataCenters.length > 0) return treeCatalog; const [regions, dataCenters, worlds] = await Promise.all([ - this.dictService.getDictItemsByKey(QQBOT_FF14_MARKET_DICT_CODES.region), - this.dictService.getDictItemsByKey( + this.host.getDictItemsByKey(QQBOT_FF14_MARKET_DICT_CODES.region), + this.host.getDictItemsByKey( QQBOT_FF14_MARKET_DICT_CODES.dataCenter, ), - this.dictService.getDictItemsByKey(QQBOT_FF14_MARKET_DICT_CODES.world), + this.host.getDictItemsByKey(QQBOT_FF14_MARKET_DICT_CODES.world), ]); return buildQqbotFf14MarketCatalog({ dataCenters, @@ -369,7 +379,7 @@ export class QqbotFf14ClientService { } private requestJson(url: URL, method: Ff14HttpMethod, context: string) { - return this.httpClient.requestJson({ + return this.host.requestJson({ context, failureMessage: (statusCode) => `${context}失败:${statusCode}`, invalidJsonMessage: 'FF14 接口返回不是合法 JSON', @@ -380,3 +390,21 @@ export class QqbotFf14ClientService { }); } } + +function formatFf14DateTime(value: number) { + const date = new Date(value); + const pad = (input: number) => `${input}`.padStart(2, '0'); + return [ + date.getFullYear(), + '-', + pad(date.getMonth() + 1), + '-', + pad(date.getDate()), + ' ', + pad(date.getHours()), + ':', + pad(date.getMinutes()), + ':', + pad(date.getSeconds()), + ].join(''); +} diff --git a/src/modules/qqbot/plugins/ff14-market/src/operations/index.ts b/src/modules/qqbot/plugins/ff14-market/src/operations/index.ts new file mode 100644 index 0000000..42ca165 --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/operations/index.ts @@ -0,0 +1,51 @@ +import type { Ff14MarketApplication } from '../application/ff14-market-application'; +import { + createFf14MarketPriceOperation, + ff14MarketPriceHandlerName, +} from './market-price'; +import type { Ff14MarketManifestOperation } from './operation-manifest'; +import { + createFf14ResolveItemOperation, + ff14ResolveItemHandlerName, +} from './resolve-item'; + +export function buildFf14MarketOperations( + application: Ff14MarketApplication, + operations: Ff14MarketManifestOperation[], +) { + const operationFactories = { + [ff14MarketPriceHandlerName]: () => + createFf14MarketPriceOperation(application), + [ff14ResolveItemHandlerName]: () => + createFf14ResolveItemOperation(application), + } satisfies Record< + string, + () => { + execute: (input: Record) => Promise; + inputSchema: Record; + outputSchema: Record; + } + >; + + return operations.map((operation) => { + const factory = operationFactories[operation.handlerName]; + if (!factory) { + throw new Error(`FF14 插件能力未实现:${operation.handlerName}`); + } + const implementation = factory(); + return { + cacheTtlMs: operation.cacheTtlMs, + description: operation.description, + execute: implementation.execute, + inputSchema: operation.inputSchema || implementation.inputSchema, + key: operation.key, + name: operation.name, + outputSchema: operation.outputSchema || implementation.outputSchema, + }; + }); +} + +export type { + Ff14MarketManifest, + Ff14MarketManifestOperation, +} from './operation-manifest'; diff --git a/src/modules/qqbot/plugins/ff14-market/src/operations/market-price.ts b/src/modules/qqbot/plugins/ff14-market/src/operations/market-price.ts new file mode 100644 index 0000000..d7e098e --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/operations/market-price.ts @@ -0,0 +1,33 @@ +import type { Ff14MarketApplication } from '../application/ff14-market-application'; + +export const ff14MarketPriceHandlerName = 'getPrice'; + +export function createFf14MarketPriceOperation( + application: Ff14MarketApplication, +) { + return { + execute: (input: Record) => application.getPrice(input), + inputSchema: { + properties: { + dataCenter: { description: '大区名,如陆行鸟', type: 'string' }, + hq: { description: '是否只查 HQ', type: 'boolean' }, + item: { description: '物品名称或 ID', type: 'string' }, + itemId: { description: '物品 ID', type: 'number' }, + language: { default: 'chs', type: 'string' }, + region: { description: '地区名,如中国', type: 'string' }, + world: { description: '小区/服务器名,如红玉海', type: 'string' }, + }, + required: ['item'], + type: 'object', + }, + outputSchema: { + properties: { + averagePrice: { type: 'number' }, + minPrice: { type: 'number' }, + replyText: { type: 'string' }, + world: { type: 'string' }, + }, + type: 'object', + }, + }; +} diff --git a/src/modules/qqbot/plugins/ff14-market/src/operations/operation-manifest.ts b/src/modules/qqbot/plugins/ff14-market/src/operations/operation-manifest.ts new file mode 100644 index 0000000..7f7b942 --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/operations/operation-manifest.ts @@ -0,0 +1,18 @@ +export type Ff14MarketManifestOperation = { + cacheTtlMs?: number; + description?: string; + handlerName: string; + inputSchema?: Record; + key: string; + name: string; + outputSchema?: Record; +}; + +export type Ff14MarketManifest = { + description?: string; + legacyAliases?: string[]; + name: string; + operations: Ff14MarketManifestOperation[]; + pluginKey: string; + version: string; +}; diff --git a/src/modules/qqbot/plugins/ff14-market/src/operations/resolve-item.ts b/src/modules/qqbot/plugins/ff14-market/src/operations/resolve-item.ts new file mode 100644 index 0000000..2baba43 --- /dev/null +++ b/src/modules/qqbot/plugins/ff14-market/src/operations/resolve-item.ts @@ -0,0 +1,27 @@ +import type { Ff14MarketApplication } from '../application/ff14-market-application'; + +export const ff14ResolveItemHandlerName = 'resolveItem'; + +export function createFf14ResolveItemOperation( + application: Ff14MarketApplication, +) { + return { + execute: (input: Record) => application.resolveItem(input), + inputSchema: { + properties: { + item: { description: '物品名称或 ID', type: 'string' }, + itemId: { description: '物品 ID', type: 'number' }, + language: { default: 'chs', type: 'string' }, + }, + required: ['item'], + type: 'object', + }, + outputSchema: { + properties: { + itemId: { type: 'number' }, + name: { type: 'string' }, + }, + type: 'object', + }, + }; +} diff --git a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin.ts b/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin.ts deleted file mode 100644 index 23c21a6..0000000 --- a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { formatKtDateTime, ToolsService } from '@/common'; -import type { QqbotIntegrationPlugin } from '@/modules/qqbot/core/contract/qqbot.types'; -import { QqbotFf14ClientService } from './qqbot-ff14-client.service'; - -@Injectable() -export class QqbotFf14MarketPluginService { - constructor( - private readonly ff14ClientService: QqbotFf14ClientService, - private readonly toolsService: ToolsService, - ) {} - - getPlugin(): QqbotIntegrationPlugin { - return { - description: - '对接 XIVAPI v2 与 Universalis,提供 FF14 物品解析和市场价格查询能力。', - healthCheck: async () => { - const checkedAt = formatKtDateTime(new Date()); - try { - await this.ff14ClientService.resolveItem({ - itemId: 2, - language: 'en', - }); - return { - checkedAt, - message: 'FF14 插件可用', - status: 'healthy', - }; - } catch (err) { - return { - checkedAt, - message: this.toolsService.getErrorMessage(err, 'FF14 插件不可用'), - status: 'degraded', - }; - } - }, - key: 'ff14-market', - legacyKeys: ['ff14Market'], - name: 'FF14 市场价格', - operations: [ - { - description: '按物品名称或 ID 解析 XIVAPI 物品信息。', - inputSchema: { - properties: { - item: { description: '物品名称或 ID', type: 'string' }, - itemId: { description: '物品 ID', type: 'number' }, - language: { default: 'chs', type: 'string' }, - }, - required: ['item'], - type: 'object', - }, - key: 'ff14.item.resolve', - name: '解析物品', - outputSchema: { - properties: { - itemId: { type: 'number' }, - name: { type: 'string' }, - }, - type: 'object', - }, - execute: async (input) => - await this.ff14ClientService.resolveItem(input), - }, - { - cacheTtlMs: 60_000, - description: '查询指定服务器的 FF14 市场最低价、均价与近期挂单。', - inputSchema: { - properties: { - dataCenter: { description: '大区名,如陆行鸟', type: 'string' }, - hq: { description: '是否只查 HQ', type: 'boolean' }, - item: { description: '物品名称或 ID', type: 'string' }, - itemId: { description: '物品 ID', type: 'number' }, - language: { default: 'chs', type: 'string' }, - region: { description: '地区名,如中国', type: 'string' }, - world: { description: '小区/服务器名,如红玉海', type: 'string' }, - }, - required: ['item'], - type: 'object', - }, - key: 'ff14.market.price', - name: '市场查价', - outputSchema: { - properties: { - averagePrice: { type: 'number' }, - minPrice: { type: 'number' }, - replyText: { type: 'string' }, - world: { type: 'string' }, - }, - type: 'object', - }, - execute: async (input) => - await this.ff14ClientService.getPrice(input), - }, - ], - version: '1.0.0', - }; - } -} diff --git a/src/modules/qqbot/plugins/fflogs/plugin.json b/src/modules/qqbot/plugins/fflogs/plugin.json index 8be601a..19b7ca6 100644 --- a/src/modules/qqbot/plugins/fflogs/plugin.json +++ b/src/modules/qqbot/plugins/fflogs/plugin.json @@ -5,7 +5,7 @@ "minApiSdkVersion": "1.0.0", "description": "对接 FFLogs v2 GraphQL,提供 FF14 角色公开排名和指定高难最近记录查询能力。", "author": "KT", - "entry": "qqbot-fflogs.plugin.ts", + "entry": "src/index.ts", "permissions": ["runtime.http", "plugin.config.read"], "runtime": { "workerType": "node-worker", diff --git a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin.ts b/src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin.ts deleted file mode 100644 index 2f14bc6..0000000 --- a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { formatKtDateTime, ToolsService } from '@/common'; -import type { QqbotIntegrationPlugin } from '@/modules/qqbot/core/contract/qqbot.types'; -import { QqbotFflogsClientService } from './qqbot-fflogs-client.service'; - -@Injectable() -export class QqbotFflogsPluginService { - constructor( - private readonly fflogsClientService: QqbotFflogsClientService, - private readonly toolsService: ToolsService, - ) {} - - getPlugin(): QqbotIntegrationPlugin { - return { - description: - '对接 FFLogs v2 GraphQL,提供 FF14 角色公开排名和指定高难最近记录查询能力。', - healthCheck: async () => { - const checkedAt = formatKtDateTime(new Date()); - try { - await this.fflogsClientService.checkHealth(); - return { - checkedAt, - message: 'FFLogs 插件可用', - status: 'healthy', - }; - } catch (err) { - return { - checkedAt, - message: this.toolsService.getErrorMessage( - err, - 'FFLogs 插件不可用', - ), - status: 'degraded', - }; - } - }, - key: 'fflogs', - name: 'FFLogs 查询', - operations: [ - { - cacheTtlMs: 60_000, - description: - '查询指定角色的 FFLogs 公开排名摘要;传入 encounter/任务 后查询指定高难最近10次记录。', - inputSchema: { - properties: { - characterName: { description: '角色名', type: 'string' }, - encounter: { - description: - '高难任务名,按 FFLogs 公开报告中的任务名或 encounterID 匹配', - type: 'string', - }, - limit: { - default: 10, - description: '最近记录数量,最多10条', - type: 'number', - }, - metric: { description: '排名指标,如 dps/hps', type: 'string' }, - serverRegion: { - default: 'CN', - description: '服务器地区,如 CN/JP/NA/EU', - type: 'string', - }, - serverSlug: { description: '服务器名或 slug', type: 'string' }, - timeframe: { - description: 'Today 或 Historical', - type: 'string', - }, - zoneId: { - description: '副本区域 ID,用于排名摘要', - type: 'number', - }, - }, - required: ['characterName', 'serverSlug'], - type: 'object', - }, - key: 'fflogs.character.summary', - name: '角色排名摘要', - outputSchema: { - properties: { - characterName: { type: 'string' }, - encounterName: { type: 'string' }, - logs: { type: 'array' }, - rankings: { type: 'array' }, - replyText: { type: 'string' }, - url: { type: 'string' }, - }, - type: 'object', - }, - execute: async (input) => - await this.fflogsClientService.getCharacterSummary(input), - }, - ], - version: '1.0.0', - }; - } -} diff --git a/src/modules/qqbot/plugins/fflogs/src/application/fflogs-application.ts b/src/modules/qqbot/plugins/fflogs/src/application/fflogs-application.ts new file mode 100644 index 0000000..8ec8e4d --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/application/fflogs-application.ts @@ -0,0 +1,17 @@ +import { FflogsClient } from '../infrastructure/integration/fflogs-client'; +import type { QqbotFflogsCharacterSummaryInput } from '../domain/fflogs.types'; + +export class FflogsApplication { + constructor(private readonly client: FflogsClient) {} + + async getCharacterSummary(input: Record) { + return this.client.getCharacterSummary( + input as QqbotFflogsCharacterSummaryInput, + ); + } + + async checkHealth() { + await this.client.checkHealth(); + return true; + } +} diff --git a/src/modules/qqbot/plugins/fflogs/src/config/fflogs-config.ts b/src/modules/qqbot/plugins/fflogs/src/config/fflogs-config.ts new file mode 100644 index 0000000..2191c9b --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/config/fflogs-config.ts @@ -0,0 +1,42 @@ +import type { FflogsPluginHost } from '../infrastructure/integration/fflogs-client'; + +export type FflogsConfig = { + baseUrl: string; + clientId: string; + clientSecret: string; + graphqlUrl: string; + tokenUrl: string; + webBaseUrl: string; +}; + +export function resolveFflogsConfig(host: FflogsPluginHost): FflogsConfig { + const webBaseUrl = normalizeBaseUrl( + host.getConfig('FFLOGS_WEB_BASE_URL') || + host.getConfig('FFLOGS_BASE_URL') || + 'https://cn.fflogs.com', + ); + const baseUrl = normalizeBaseUrl( + host.getConfig('FFLOGS_BASE_URL') || + webBaseUrl || + 'https://cn.fflogs.com', + ); + + return { + baseUrl, + clientId: `${host.getConfig('FFLOGS_CLIENT_ID') || ''}`.trim(), + clientSecret: `${ + host.getConfig('FFLOGS_CLIENT_SECRET') || '' + }`.trim(), + graphqlUrl: + host.getConfig('FFLOGS_GRAPHQL_URL') || + `${baseUrl}/api/v2/client`, + tokenUrl: + host.getConfig('FFLOGS_TOKEN_URL') || + `${baseUrl}/oauth/token`, + webBaseUrl, + }; +} + +function normalizeBaseUrl(value: string) { + return `${value || ''}`.trim().replace(/\/+$/, ''); +} diff --git a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs.types.ts b/src/modules/qqbot/plugins/fflogs/src/domain/fflogs.types.ts similarity index 100% rename from src/modules/qqbot/plugins/fflogs/qqbot-fflogs.types.ts rename to src/modules/qqbot/plugins/fflogs/src/domain/fflogs.types.ts diff --git a/src/modules/qqbot/plugins/fflogs/src/index.ts b/src/modules/qqbot/plugins/fflogs/src/index.ts new file mode 100644 index 0000000..60072ff --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/index.ts @@ -0,0 +1,63 @@ +import { FflogsApplication } from './application/fflogs-application'; +import { FflogsClient } from './infrastructure/integration/fflogs-client'; +import { buildFflogsOperations, type FflogsManifest } from './operations'; + +export type FflogsPluginOptions = { + host: import('./infrastructure/integration/fflogs-client').FflogsPluginHost; + manifest: FflogsManifest; + normalizeError?: (error: unknown, fallback: string) => string; + now?: () => Date; +}; + +export function createPlugin(options: FflogsPluginOptions) { + const application = new FflogsApplication(new FflogsClient(options.host)); + return { + description: options.manifest.description, + healthCheck: async () => { + const checkedAt = formatFflogsCheckedAt(options.now?.() || new Date()); + try { + await application.checkHealth(); + return { + checkedAt, + message: 'FFLogs 插件可用', + status: 'healthy', + }; + } catch (error) { + return { + checkedAt, + message: + options.normalizeError?.(error, 'FFLogs 插件不可用') || `${error}`, + status: 'degraded', + }; + } + }, + key: options.manifest.pluginKey, + legacyKeys: options.manifest.legacyAliases, + name: options.manifest.name, + operations: buildFflogsOperations(application, options.manifest.operations), + version: options.manifest.version, + }; +} + +function formatFflogsCheckedAt(date: Date) { + const pad = (input: number) => `${input}`.padStart(2, '0'); + return [ + date.getFullYear(), + '-', + pad(date.getMonth() + 1), + '-', + pad(date.getDate()), + ' ', + pad(date.getHours()), + ':', + pad(date.getMinutes()), + ':', + pad(date.getSeconds()), + ].join(''); +} + +export type { + QqbotFflogsCharacterSummaryInput, + QqbotFflogsCharacterSummaryResult, +} from './domain/fflogs.types'; +export type { FflogsPluginHost } from './infrastructure/integration/fflogs-client'; diff --git a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts b/src/modules/qqbot/plugins/fflogs/src/infrastructure/integration/fflogs-client.ts similarity index 95% rename from src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts rename to src/modules/qqbot/plugins/fflogs/src/infrastructure/integration/fflogs-client.ts index 46bd972..260d70d 100644 --- a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts +++ b/src/modules/qqbot/plugins/fflogs/src/infrastructure/integration/fflogs-client.ts @@ -1,7 +1,3 @@ -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; import type { FflogsCharacterEncounterRankingsResponse, FflogsCharacterSummaryResponse, @@ -19,7 +15,9 @@ import type { QqbotFflogsCharacterSummaryInput, QqbotFflogsCharacterSummaryResult, QqbotFflogsEncounterLogItem, -} from './qqbot-fflogs.types'; +} from '../../domain/fflogs.types'; +import { resolveFflogsConfig } from '../../config/fflogs-config'; +import { FflogsOAuthTokenCache } from '../storage/oauth-token-cache'; const FFLOGS_LOCALIZATION_DICT_CODES = { job: 'FFLOGS_JOB_LABEL', @@ -36,11 +34,25 @@ type FflogsEncounterCatalogItem = { zoneName?: string; }; -@Injectable() -export class QqbotFflogsClientService { - private accessToken = ''; - private accessTokenExpireAt = 0; - private readonly baseUrl: string; +export type FflogsPluginHost = { + getConfig: (key: string) => T | undefined; + getDictByKey: ( + dictCode: string, + ) => Promise>; + requestJson: (options: { + body?: string; + context: string; + failureMessage: (statusCode: number) => string; + headers?: Record; + invalidJsonMessage: string; + method?: FflogsHttpMethod; + timeoutMessage: string; + timeoutMs: number; + url: URL; + }) => Promise; +}; + +export class FflogsClient { private readonly clientId: string; private readonly clientSecret: string; private encounterCatalogCache?: { @@ -49,35 +61,16 @@ export class QqbotFflogsClientService { }; private readonly graphqlUrl: string; private readonly tokenUrl: string; + private readonly tokenCache = new FflogsOAuthTokenCache(); private readonly webBaseUrl: string; - constructor( - private readonly configService: ConfigService, - private readonly dictService: DictService, - private readonly httpClient: QqbotPluginHttpClientService, - ) { - this.webBaseUrl = this.normalizeBaseUrl( - this.configService.get('FFLOGS_WEB_BASE_URL') || - this.configService.get('FFLOGS_BASE_URL') || - 'https://cn.fflogs.com', - ); - this.baseUrl = this.normalizeBaseUrl( - this.configService.get('FFLOGS_BASE_URL') || - this.webBaseUrl || - 'https://cn.fflogs.com', - ); - this.graphqlUrl = - this.configService.get('FFLOGS_GRAPHQL_URL') || - `${this.baseUrl}/api/v2/client`; - this.tokenUrl = - this.configService.get('FFLOGS_TOKEN_URL') || - `${this.baseUrl}/oauth/token`; - this.clientId = `${ - this.configService.get('FFLOGS_CLIENT_ID') || '' - }`.trim(); - this.clientSecret = `${ - this.configService.get('FFLOGS_CLIENT_SECRET') || '' - }`.trim(); + constructor(private readonly host: FflogsPluginHost) { + const config = resolveFflogsConfig(host); + this.webBaseUrl = config.webBaseUrl; + this.graphqlUrl = config.graphqlUrl; + this.tokenUrl = config.tokenUrl; + this.clientId = config.clientId; + this.clientSecret = config.clientSecret; } async checkHealth() { @@ -645,9 +638,8 @@ export class QqbotFflogsClientService { } private async getAccessToken() { - if (this.accessToken && Date.now() < this.accessTokenExpireAt) { - return this.accessToken; - } + const cached = this.tokenCache.getValidToken(); + if (cached) return cached; if (!this.clientId || !this.clientSecret) { throw new Error('未配置 FFLOGS_CLIENT_ID / FFLOGS_CLIENT_SECRET'); } @@ -669,9 +661,8 @@ export class QqbotFflogsClientService { ); if (!data.access_token) throw new Error('FFLogs 未返回 access_token'); const expiresIn = Number(data.expires_in || 3600); - this.accessToken = data.access_token; - this.accessTokenExpireAt = Date.now() + Math.max(expiresIn - 60, 60) * 1000; - return this.accessToken; + this.tokenCache.setToken(data.access_token, expiresIn); + return data.access_token; } private async requestGraphql( @@ -709,7 +700,7 @@ export class QqbotFflogsClientService { method: FflogsHttpMethod, options: { body?: string; headers?: Record } = {}, ) { - return this.httpClient.requestJson({ + return this.host.requestJson({ body: options.body, context: 'FFLogs', failureMessage: (statusCode) => `FFLogs 请求失败:${statusCode}`, @@ -1426,7 +1417,7 @@ export class QqbotFflogsClientService { } private async getNormalizedDictMap(dictCode: string) { - const dicts = await this.dictService.getDictByKey(dictCode); + const dicts = await this.host.getDictByKey(dictCode); const map = new Map(); for (const { label, value } of dicts) { for (const key of this.buildLookupKeys(`${value}`, `${label}`)) { @@ -1503,23 +1494,19 @@ export class QqbotFflogsClientService { ); } - private normalizeBaseUrl(value: string) { - return value.replace(/\/+$/, ''); - } - private getDefaultServer() { - return this.configService.get('FFLOGS_DEFAULT_SERVER') || ''; + return this.host.getConfig('FFLOGS_DEFAULT_SERVER') || ''; } private getDefaultServerRegion() { return ( - this.configService.get('FFLOGS_DEFAULT_SERVER_REGION') || 'CN' + this.host.getConfig('FFLOGS_DEFAULT_SERVER_REGION') || 'CN' ); } private getTimeoutMs() { return Number( - this.configService.get('FFLOGS_REQUEST_TIMEOUT_MS') || 10_000, + this.host.getConfig('FFLOGS_REQUEST_TIMEOUT_MS') || 10_000, ); } } diff --git a/src/modules/qqbot/plugins/fflogs/src/infrastructure/storage/oauth-token-cache.ts b/src/modules/qqbot/plugins/fflogs/src/infrastructure/storage/oauth-token-cache.ts new file mode 100644 index 0000000..b748ae7 --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/infrastructure/storage/oauth-token-cache.ts @@ -0,0 +1,15 @@ +export class FflogsOAuthTokenCache { + private accessToken = ''; + private accessTokenExpireAt = 0; + + getValidToken(now = Date.now()) { + return this.accessToken && this.accessTokenExpireAt > now + ? this.accessToken + : ''; + } + + setToken(token: string, expiresInSeconds: number, now = Date.now()) { + this.accessToken = token; + this.accessTokenExpireAt = now + Math.max(expiresInSeconds - 60, 1) * 1000; + } +} diff --git a/src/modules/qqbot/plugins/fflogs/src/operations/character-summary.ts b/src/modules/qqbot/plugins/fflogs/src/operations/character-summary.ts new file mode 100644 index 0000000..389f3aa --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/operations/character-summary.ts @@ -0,0 +1,55 @@ +import type { FflogsApplication } from '../application/fflogs-application'; + +export const fflogsCharacterSummaryHandlerName = 'getCharacterSummary'; + +export function createFflogsCharacterSummaryOperation( + application: FflogsApplication, +) { + return { + cacheTtlMs: 60_000, + execute: (input: Record) => + application.getCharacterSummary(input), + inputSchema: { + properties: { + characterName: { description: '角色名', type: 'string' }, + encounter: { + description: '高难任务名,按 FFLogs 公开报告中的任务名或 encounterID 匹配', + type: 'string', + }, + limit: { + default: 10, + description: '最近记录数量,最多10条', + type: 'number', + }, + metric: { description: '排名指标,如 dps/hps', type: 'string' }, + serverRegion: { + default: 'CN', + description: '服务器地区,如 CN/JP/NA/EU', + type: 'string', + }, + serverSlug: { description: '服务器名或 slug', type: 'string' }, + timeframe: { + description: 'Today 或 Historical', + type: 'string', + }, + zoneId: { + description: '副本区域 ID,用于排名摘要', + type: 'number', + }, + }, + required: ['characterName', 'serverSlug'], + type: 'object', + }, + outputSchema: { + properties: { + characterName: { type: 'string' }, + encounterName: { type: 'string' }, + logs: { type: 'array' }, + rankings: { type: 'array' }, + replyText: { type: 'string' }, + url: { type: 'string' }, + }, + type: 'object', + }, + }; +} diff --git a/src/modules/qqbot/plugins/fflogs/src/operations/index.ts b/src/modules/qqbot/plugins/fflogs/src/operations/index.ts new file mode 100644 index 0000000..eda3ed0 --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/operations/index.ts @@ -0,0 +1,43 @@ +import type { FflogsApplication } from '../application/fflogs-application'; +import { + createFflogsCharacterSummaryOperation, + fflogsCharacterSummaryHandlerName, +} from './character-summary'; +import type { FflogsManifestOperation } from './operation-manifest'; + +export function buildFflogsOperations( + application: FflogsApplication, + operations: FflogsManifestOperation[], +) { + const operationFactories = { + [fflogsCharacterSummaryHandlerName]: () => + createFflogsCharacterSummaryOperation(application), + } satisfies Record< + string, + () => { + cacheTtlMs?: number; + execute: (input: Record) => Promise; + inputSchema: Record; + outputSchema: Record; + } + >; + + return operations.map((operation) => { + const factory = operationFactories[operation.handlerName]; + if (!factory) { + throw new Error(`FFLogs 插件能力未实现:${operation.handlerName}`); + } + const implementation = factory(); + return { + cacheTtlMs: operation.cacheTtlMs || implementation.cacheTtlMs, + description: operation.description, + execute: implementation.execute, + inputSchema: operation.inputSchema || implementation.inputSchema, + key: operation.key, + name: operation.name, + outputSchema: operation.outputSchema || implementation.outputSchema, + }; + }); +} + +export type { FflogsManifest, FflogsManifestOperation } from './operation-manifest'; diff --git a/src/modules/qqbot/plugins/fflogs/src/operations/operation-manifest.ts b/src/modules/qqbot/plugins/fflogs/src/operations/operation-manifest.ts new file mode 100644 index 0000000..2293066 --- /dev/null +++ b/src/modules/qqbot/plugins/fflogs/src/operations/operation-manifest.ts @@ -0,0 +1,18 @@ +export type FflogsManifestOperation = { + cacheTtlMs?: number; + description?: string; + handlerName: string; + inputSchema?: Record; + key: string; + name: string; + outputSchema?: Record; +}; + +export type FflogsManifest = { + description?: string; + legacyAliases?: string[]; + name: string; + operations: FflogsManifestOperation[]; + pluginKey: string; + version: string; +}; diff --git a/src/modules/qqbot/plugins/repeater/plugin.json b/src/modules/qqbot/plugins/repeater/plugin.json index 4033636..c008cda 100644 --- a/src/modules/qqbot/plugins/repeater/plugin.json +++ b/src/modules/qqbot/plugins/repeater/plugin.json @@ -5,7 +5,7 @@ "minApiSdkVersion": "1.0.0", "description": "监听同一会话内连续重复的普通文本消息,达到阈值后通过宿主发送队列复读一次。", "author": "KT", - "entry": "qqbot-repeater.plugin.ts", + "entry": "src/index.ts", "permissions": [ "qqbot.event.receive", "qqbot.send", diff --git a/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts b/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts deleted file mode 100644 index c6f7782..0000000 --- a/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { ToolsService } from '@/common'; -import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; -import type { - QqbotEventPluginDefinition, - QqbotEventPluginSummary, - QqbotNormalizedMessage, -} from '@/modules/qqbot/core/contract/qqbot.types'; -import { QqbotSendService } from '@/modules/qqbot/core/application/send/qqbot-send.service'; - -const QQBOT_REPEATER_VERSION = '1.0.0'; -const QQBOT_REPEATER_PLUGIN_KEY = 'repeater'; - -type QqbotRepeaterConversationState = { - count: number; - lastRepeatedAt?: number; - lastText: string; - repeatedText: string; - updatedAt: number; -}; - -@Injectable() -export class QqbotRepeaterPluginService { - private readonly logger = new Logger(QqbotRepeaterPluginService.name); - private readonly states = new Map(); - private readonly boundCache = new Map< - string, - { - expiresAt: number; - value: boolean; - } - >(); - private readonly pluginDescription = - '监听同一会话内连续重复的普通文本消息,达到阈值后自动复读一次。'; - private readonly pluginName = '复读机'; - private readonly pluginRemark = - '连续重复达到阈值后触发;命令、CQ 码和机器人自身消息不触发。'; - - constructor( - private readonly configService: ConfigService, - private readonly accountService: QqbotAccountService, - private readonly sendService: QqbotSendService, - private readonly toolsService: ToolsService, - ) {} - - async getSummary(params: { - accountName?: string; - connectStatus?: string; - selfId: string; - }): Promise { - const definition = this.getDefinition(); - return { - accountName: params.accountName, - bound: await this.isBound(params.selfId), - connectStatus: params.connectStatus, - description: definition.description, - key: definition.key, - name: definition.name, - remark: definition.remark, - selfId: params.selfId, - triggerType: definition.triggerType, - version: definition.version, - }; - } - - getDefinition(): QqbotEventPluginDefinition { - return { - description: this.pluginDescription, - key: QQBOT_REPEATER_PLUGIN_KEY, - name: this.pluginName, - remark: this.pluginRemark, - triggerType: 'message', - version: QQBOT_REPEATER_VERSION, - }; - } - - async bind(selfId: string) { - await this.accountService.bindEventPlugin( - selfId, - QQBOT_REPEATER_PLUGIN_KEY, - ); - this.clearBoundCache(selfId); - return this.getSummary({ selfId }); - } - - async unbind(selfId: string) { - await this.accountService.unbindEventPlugin( - selfId, - QQBOT_REPEATER_PLUGIN_KEY, - ); - this.clearBoundCache(selfId); - return this.getSummary({ selfId }); - } - - clearBoundCache(selfId: string) { - this.boundCache.delete(`${selfId || ''}`.trim()); - } - - async handleMessage(message: QqbotNormalizedMessage) { - if (!(await this.isBound(message.selfId))) return false; - const text = this.toolsService.normalizeWhitespaceText(message.messageText); - if (!this.canRepeat(message, text)) { - this.resetState(message); - return false; - } - - const key = this.buildStateKey(message); - const state = this.getNextState(key, text); - if (!this.shouldRepeat(state, text)) return false; - - const repeatedAt = Date.now(); - state.repeatedText = text; - state.lastRepeatedAt = repeatedAt; - state.updatedAt = repeatedAt; - try { - await this.sendService.sendText({ - channelId: message.channelId, - guildId: message.rawEvent.guild_id - ? `${message.rawEvent.guild_id}` - : undefined, - message: text, - selfId: message.selfId, - targetId: message.targetId, - targetType: message.messageType, - }); - return true; - } catch (err) { - const errMsg = this.toolsService.getErrorMessage(err, '复读失败'); - this.logger.warn(`QQBot 复读机发送失败: ${errMsg}`); - return false; - } - } - - private async isBound(selfId: string) { - const normalizedSelfId = `${selfId || ''}`.trim(); - if (!normalizedSelfId) return false; - const now = Date.now(); - const cached = this.boundCache.get(normalizedSelfId); - if (cached && cached.expiresAt > now) { - return cached.value; - } - const value = ( - await this.accountService.getBoundEventPluginKeys(normalizedSelfId) - ).includes(QQBOT_REPEATER_PLUGIN_KEY); - this.boundCache.set(normalizedSelfId, { - expiresAt: now + this.getConfigCacheTtlMs(), - value, - }); - return value; - } - - private canRepeat(message: QqbotNormalizedMessage, text: string) { - if (!text) return false; - if (message.userId === message.selfId) return false; - if (/^[!/!]/.test(text)) return false; - if (text.includes('[CQ:')) return false; - return text.length <= this.getMaxTextLength(); - } - - private getNextState(key: string, text: string) { - const current = this.states.get(key); - const now = Date.now(); - const next = - current?.lastText === text - ? { ...current, count: current.count + 1, updatedAt: now } - : { - count: 1, - lastText: text, - lastRepeatedAt: current?.lastRepeatedAt || 0, - repeatedText: '', - updatedAt: now, - }; - this.states.set(key, next); - this.pruneStates(now); - return next; - } - - private shouldRepeat(state: QqbotRepeaterConversationState, text: string) { - return ( - state.count >= this.getThreshold() && - state.repeatedText !== text && - Date.now() - (state.lastRepeatedAt || 0) >= this.getMinIntervalMs() - ); - } - - private resetState(message: QqbotNormalizedMessage) { - const key = this.buildStateKey(message); - const current = this.states.get(key); - if (!current?.lastRepeatedAt) { - this.states.delete(key); - return; - } - - this.states.set(key, { - count: 0, - lastRepeatedAt: current.lastRepeatedAt, - lastText: '', - repeatedText: '', - updatedAt: Date.now(), - }); - } - - private buildStateKey(message: QqbotNormalizedMessage) { - return [message.selfId, message.messageType, message.targetId].join(':'); - } - - private getThreshold() { - const value = Number(this.configService.get('QQBOT_REPEATER_THRESHOLD')); - return Number.isInteger(value) && value > 1 ? value : 4; - } - - private getMaxTextLength() { - const value = Number( - this.configService.get('QQBOT_REPEATER_MAX_TEXT_LENGTH'), - ); - return Number.isInteger(value) && value > 0 ? value : 120; - } - - private getMinIntervalMs() { - const value = Number( - this.configService.get('QQBOT_REPEATER_MIN_INTERVAL_MS'), - ); - return Number.isInteger(value) && value > 0 ? value : 10 * 60 * 1000; - } - - private getStateTtlMs() { - const value = Number(this.configService.get('QQBOT_REPEATER_STATE_TTL_MS')); - return Number.isInteger(value) && value > 0 ? value : 10 * 60 * 1000; - } - - private getConfigCacheTtlMs() { - const value = Number( - this.configService.get('QQBOT_REPEATER_CONFIG_CACHE_TTL_MS'), - ); - return Number.isInteger(value) && value > 0 ? value : 2000; - } - - private pruneStates(now: number) { - const ttl = this.getStateTtlMs(); - for (const [key, state] of this.states.entries()) { - if (now - state.updatedAt > ttl) this.states.delete(key); - } - } -} diff --git a/src/modules/qqbot/plugins/repeater/src/application/repeater-application.ts b/src/modules/qqbot/plugins/repeater/src/application/repeater-application.ts new file mode 100644 index 0000000..9d5e54d --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/application/repeater-application.ts @@ -0,0 +1,157 @@ +import { readRepeaterRuntimeConfig } from '../config/repeater-config'; +import { + buildRepeaterStateKey, + canRepeaterEcho, + createNextRepeaterState, + normalizeRepeaterText, + shouldRepeaterEcho, +} from '../domain/repeat-policy'; +import type { + RepeaterConversationState, + RepeaterManifest, + RepeaterMessage, +} from '../domain/repeater.types'; +import type { RepeaterPluginHost } from '../infrastructure/integration/repeater-host'; + +export class RepeaterApplication { + private readonly boundCache = new Map< + string, + { expiresAt: number; value: boolean } + >(); + private readonly states = new Map(); + + constructor( + private readonly host: RepeaterPluginHost, + private readonly manifest: RepeaterManifest, + private readonly now: () => number = Date.now, + ) {} + + async bind(selfId: string) { + await this.host.bindEventPlugin(selfId, this.manifest.pluginKey); + this.clearBoundCache(selfId); + return true; + } + + clearBoundCache(selfId: string) { + this.boundCache.delete(`${selfId || ''}`.trim()); + } + + getDefinition() { + return { + description: this.manifest.description, + key: this.manifest.pluginKey, + name: this.manifest.name, + remark: '连续重复达到阈值后触发;命令、CQ 码和机器人自身消息不触发。', + triggerType: 'message' as const, + version: this.manifest.version, + }; + } + + async getSummary(params: { + accountName?: string; + connectStatus?: string; + selfId: string; + }) { + const definition = this.getDefinition(); + return { + accountName: params.accountName, + bound: await this.isBound(params.selfId), + connectStatus: params.connectStatus, + description: definition.description, + key: definition.key, + name: definition.name, + remark: definition.remark, + selfId: params.selfId, + triggerType: definition.triggerType, + version: definition.version, + }; + } + + async handleMessage(message: RepeaterMessage) { + if (!(await this.isBound(message.selfId))) return false; + + const config = readRepeaterRuntimeConfig(this.host); + const text = normalizeRepeaterText(message.messageText); + if (!canRepeaterEcho(message, text, config.maxTextLength)) { + this.resetState(message); + return false; + } + + const key = buildRepeaterStateKey(message); + const current = this.now(); + const state = createNextRepeaterState(this.states.get(key), text, current); + this.states.set(key, state); + this.pruneStates(current, config.stateTtlMs); + if (!shouldRepeaterEcho(state, text, current, config)) return false; + + state.repeatedText = text; + state.lastRepeatedAt = current; + state.updatedAt = current; + try { + await this.host.sendText({ + channelId: message.channelId, + guildId: message.rawEvent.guild_id + ? `${message.rawEvent.guild_id}` + : undefined, + message: text, + selfId: message.selfId, + targetId: message.targetId, + targetType: message.messageType, + }); + return true; + } catch (error) { + this.host.warn?.( + `QQBot 复读机发送失败: ${ + error instanceof Error ? error.message : `${error}` + }`, + ); + return false; + } + } + + async unbind(selfId: string) { + await this.host.unbindEventPlugin(selfId, this.manifest.pluginKey); + this.clearBoundCache(selfId); + return true; + } + + private async isBound(selfId: string) { + const normalizedSelfId = `${selfId || ''}`.trim(); + if (!normalizedSelfId) return false; + const current = this.now(); + const cached = this.boundCache.get(normalizedSelfId); + if (cached && cached.expiresAt > current) return cached.value; + + const config = readRepeaterRuntimeConfig(this.host); + const value = ( + await this.host.getBoundEventPluginKeys(normalizedSelfId) + ).includes(this.manifest.pluginKey); + this.boundCache.set(normalizedSelfId, { + expiresAt: current + config.configCacheTtlMs, + value, + }); + return value; + } + + private pruneStates(current: number, ttl: number) { + for (const [key, state] of this.states.entries()) { + if (current - state.updatedAt > ttl) this.states.delete(key); + } + } + + private resetState(message: RepeaterMessage) { + const key = buildRepeaterStateKey(message); + const currentState = this.states.get(key); + if (!currentState?.lastRepeatedAt) { + this.states.delete(key); + return; + } + this.states.set(key, { + count: 0, + lastRepeatedAt: currentState.lastRepeatedAt, + lastText: '', + repeatedText: '', + updatedAt: this.now(), + }); + } +} diff --git a/src/modules/qqbot/plugins/repeater/src/config/repeater-config.ts b/src/modules/qqbot/plugins/repeater/src/config/repeater-config.ts new file mode 100644 index 0000000..1c305b3 --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/config/repeater-config.ts @@ -0,0 +1,56 @@ +import type { RepeaterPluginHost } from '../infrastructure/integration/repeater-host'; + +export type RepeaterRuntimeConfig = { + configCacheTtlMs: number; + maxTextLength: number; + minIntervalMs: number; + stateTtlMs: number; + threshold: number; +}; + +export function readRepeaterRuntimeConfig( + host: RepeaterPluginHost, +): RepeaterRuntimeConfig { + return { + configCacheTtlMs: getNumberConfig( + host, + 'QQBOT_REPEATER_CONFIG_CACHE_TTL_MS', + 2000, + (value) => value > 0, + ), + maxTextLength: getNumberConfig( + host, + 'QQBOT_REPEATER_MAX_TEXT_LENGTH', + 120, + (value) => value > 0, + ), + minIntervalMs: getNumberConfig( + host, + 'QQBOT_REPEATER_MIN_INTERVAL_MS', + 10 * 60 * 1000, + (value) => value > 0, + ), + stateTtlMs: getNumberConfig( + host, + 'QQBOT_REPEATER_STATE_TTL_MS', + 10 * 60 * 1000, + (value) => value > 0, + ), + threshold: getNumberConfig( + host, + 'QQBOT_REPEATER_THRESHOLD', + 4, + (value) => value > 1, + ), + }; +} + +function getNumberConfig( + host: RepeaterPluginHost, + key: string, + fallback: number, + valid: (value: number) => boolean, +) { + const value = Number(host.getConfig(key)); + return Number.isInteger(value) && valid(value) ? value : fallback; +} diff --git a/src/modules/qqbot/plugins/repeater/src/domain/repeat-policy.ts b/src/modules/qqbot/plugins/repeater/src/domain/repeat-policy.ts new file mode 100644 index 0000000..02df95b --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/domain/repeat-policy.ts @@ -0,0 +1,57 @@ +import type { + RepeaterConversationState, + RepeaterMessage, +} from './repeater.types'; + +export type RepeaterPolicyConfig = { + maxTextLength: number; + minIntervalMs: number; + threshold: number; +}; + +export const normalizeRepeaterText = (value: string) => + `${value || ''}`.replace(/\s+/g, ' ').trim(); + +export const buildRepeaterStateKey = (message: RepeaterMessage) => + [message.selfId, message.messageType, message.targetId].join(':'); + +export function canRepeaterEcho( + message: RepeaterMessage, + text: string, + maxTextLength: number, +) { + if (!text) return false; + if (message.userId === message.selfId) return false; + if (/^[!/!]/.test(text)) return false; + if (text.includes('[CQ:')) return false; + return text.length <= maxTextLength; +} + +export function createNextRepeaterState( + currentState: RepeaterConversationState | undefined, + text: string, + current: number, +): RepeaterConversationState { + return currentState?.lastText === text + ? { ...currentState, count: currentState.count + 1, updatedAt: current } + : { + count: 1, + lastText: text, + lastRepeatedAt: currentState?.lastRepeatedAt || 0, + repeatedText: '', + updatedAt: current, + }; +} + +export function shouldRepeaterEcho( + state: RepeaterConversationState, + text: string, + current: number, + config: RepeaterPolicyConfig, +) { + return ( + state.count >= config.threshold && + state.repeatedText !== text && + current - (state.lastRepeatedAt || 0) >= config.minIntervalMs + ); +} diff --git a/src/modules/qqbot/plugins/repeater/src/domain/repeater.types.ts b/src/modules/qqbot/plugins/repeater/src/domain/repeater.types.ts new file mode 100644 index 0000000..fa30f6f --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/domain/repeater.types.ts @@ -0,0 +1,30 @@ +export type RepeaterMessage = { + channelId?: string; + messageText: string; + messageType: string; + rawEvent: Record; + selfId: string; + targetId: string; + userId: string; +}; + +export type RepeaterConversationState = { + count: number; + lastRepeatedAt?: number; + lastText: string; + repeatedText: string; + updatedAt: number; +}; + +export type RepeaterManifest = { + description?: string; + events: Array<{ + description?: string; + eventName: string; + key: string; + name: string; + }>; + name: string; + pluginKey: string; + version: string; +}; diff --git a/src/modules/qqbot/plugins/repeater/src/events/message/index.ts b/src/modules/qqbot/plugins/repeater/src/events/message/index.ts new file mode 100644 index 0000000..668e04d --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/events/message/index.ts @@ -0,0 +1,8 @@ +import type { RepeaterApplication } from '../../application/repeater-application'; +import type { RepeaterMessage } from '../../domain/repeater.types'; + +export function createRepeaterMessageEventHandler( + application: RepeaterApplication, +) { + return (message: RepeaterMessage) => application.handleMessage(message); +} diff --git a/src/modules/qqbot/plugins/repeater/src/index.ts b/src/modules/qqbot/plugins/repeater/src/index.ts new file mode 100644 index 0000000..9dabe41 --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/index.ts @@ -0,0 +1,35 @@ +import { RepeaterApplication } from './application/repeater-application'; +import type { RepeaterManifest } from './domain/repeater.types'; +import { createRepeaterMessageEventHandler } from './events/message'; +import type { RepeaterPluginHost } from './infrastructure/integration/repeater-host'; + +export type RepeaterPluginOptions = { + host: RepeaterPluginHost; + manifest: RepeaterManifest; + now?: () => number; +}; + +export function createPlugin(options: RepeaterPluginOptions) { + const application = new RepeaterApplication( + options.host, + options.manifest, + options.now, + ); + const handleMessage = createRepeaterMessageEventHandler(application); + + return { + bind: (selfId: string) => application.bind(selfId), + clearBoundCache: (selfId: string) => application.clearBoundCache(selfId), + getDefinition: () => application.getDefinition(), + getSummary: (params: { + accountName?: string; + connectStatus?: string; + selfId: string; + }) => application.getSummary(params), + handleMessage, + unbind: (selfId: string) => application.unbind(selfId), + }; +} + +export type { RepeaterPluginHost } from './infrastructure/integration/repeater-host'; +export type { RepeaterManifest, RepeaterMessage } from './domain/repeater.types'; diff --git a/src/modules/qqbot/plugins/repeater/src/infrastructure/integration/repeater-host.ts b/src/modules/qqbot/plugins/repeater/src/infrastructure/integration/repeater-host.ts new file mode 100644 index 0000000..f54e81b --- /dev/null +++ b/src/modules/qqbot/plugins/repeater/src/infrastructure/integration/repeater-host.ts @@ -0,0 +1,15 @@ +export type RepeaterPluginHost = { + bindEventPlugin: (selfId: string, pluginKey: string) => Promise; + getBoundEventPluginKeys: (selfId: string) => Promise; + getConfig: (key: string) => T | undefined; + sendText: (input: { + channelId?: string; + guildId?: string; + message: string; + selfId: string; + targetId: string; + targetType: string; + }) => Promise; + unbindEventPlugin: (selfId: string, pluginKey: string) => Promise; + warn?: (message: string) => void; +}; diff --git a/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts b/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts index 509dc65..ae8e357 100644 --- a/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts +++ b/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts @@ -2,7 +2,6 @@ import { readFileSync } from 'fs'; import { join } from 'path'; import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/domain/manifest'; -import { BANGDREAM_OPERATION_REGISTRY } from '../../../../src/modules/qqbot/plugins/bangDream/registry/operation-registry'; const repoRoot = join(__dirname, '../../../..'); const pluginRoot = join(repoRoot, 'src/modules/qqbot/plugins'); @@ -119,10 +118,12 @@ const bangDreamOperations: ExpectedOperation[] = [ ]; describe('QQBot current operation matrix', () => { - it('freezes the current BangDream operation registry before rewrite', () => { + it('freezes the current BangDream operation matrix from the manifest', () => { + const manifest = readManifest('bangdream'); + expect( - BANGDREAM_OPERATION_REGISTRY.map((operation) => ({ - aliases: [...operation.onlineCommand.aliases], + manifest.operations.map((operation) => ({ + aliases: operation.aliases, handlerName: operation.handlerName, key: operation.key, name: operation.name, @@ -132,15 +133,15 @@ describe('QQBot current operation matrix', () => { it('freezes built-in plugin manifests and exposed capabilities', () => { const manifests = { - bangDream: readManifest('bangDream'), - ff14Market: readManifest('ff14Market'), + bangdream: readManifest('bangdream'), + ff14Market: readManifest('ff14-market'), fflogs: readManifest('fflogs'), repeater: readManifest('repeater'), }; - expect(manifests.bangDream.pluginKey).toBe('bangdream'); + expect(manifests.bangdream.pluginKey).toBe('bangdream'); expect( - manifests.bangDream.operations.map((operation) => ({ + manifests.bangdream.operations.map((operation) => ({ aliases: operation.aliases, handlerName: operation.handlerName, key: operation.key, @@ -212,10 +213,10 @@ describe('QQBot current operation matrix', () => { expect(seedSql).toContain(JSON.stringify(operation.aliases)); } - expect(seedSql).toContain(`'ff14Market', 'ff14.market.price'`); + expect(seedSql).toContain(`'ff14-market', 'ff14.market.price'`); expect(seedSql).toContain(`'fflogs', 'fflogs.character.summary'`); expect(seedSql).toContain( - `'bangDream', 'bangdream.event.stage', 'plain'`, + `'bangdream', 'bangdream.event.stage', 'plain'`, ); }); }); diff --git a/test/modules/qqbot/architecture/qqbot-module-boundary.spec.ts b/test/modules/qqbot/architecture/qqbot-module-boundary.spec.ts index cdd5f0d..9bb81ec 100644 --- a/test/modules/qqbot/architecture/qqbot-module-boundary.spec.ts +++ b/test/modules/qqbot/architecture/qqbot-module-boundary.spec.ts @@ -87,7 +87,7 @@ describe('QQBot third-phase module boundaries', () => { 'QqbotFf14', 'QqbotFflogs', 'QqbotRepeater', - 'TsuguApplicationService', + 'BangDreamApplicationService', ]; const violations = bannedSymbols.filter((symbol) => diff --git a/test/modules/qqbot/architecture/qqbot-plugin-package-boundary.spec.ts b/test/modules/qqbot/architecture/qqbot-plugin-package-boundary.spec.ts index 76a3e39..c38c9a8 100644 --- a/test/modules/qqbot/architecture/qqbot-plugin-package-boundary.spec.ts +++ b/test/modules/qqbot/architecture/qqbot-plugin-package-boundary.spec.ts @@ -15,25 +15,44 @@ const collectTsFiles = (root: string): string[] => { }); }; +const collectFiles = (root: string): string[] => { + if (!existsSync(root)) return []; + + return readdirSync(root).flatMap((name) => { + const filePath = join(root, name); + const stat = statSync(filePath); + if (stat.isDirectory()) return collectFiles(filePath); + return [filePath]; + }); +}; + const toRepoPath = (filePath: string) => relative(repoRoot, filePath).replace(/\\/g, '/'); const requiredPluginPaths = [ 'plugin.json', 'src/index.ts', - 'src/operations', - 'src/events', - 'src/domain', 'src/application', - 'src/infrastructure/integration', - 'src/infrastructure/storage', 'src/config', - 'src/assets', - 'src/migrations', - 'src/tests', + 'src/domain', + 'src/infrastructure/integration', ]; +const requiredCommandPluginPaths = ['src/operations']; +const requiredEventPluginPaths = ['src/events']; + describe('QQBot plugin package boundary', () => { + it('does not keep built-in plugin transfer services under plugin-platform', () => { + expect( + existsSync( + join( + repoRoot, + 'src/modules/qqbot/plugin-platform/infrastructure/integration/builtins', + ), + ), + ).toBe(false); + }); + it('uses only approved built-in plugin package keys as directory names', () => { const pluginDirs = readdirSync(pluginRoot) .filter((name) => statSync(join(pluginRoot, name)).isDirectory()) @@ -49,15 +68,66 @@ describe('QQBot plugin package boundary', () => { it('uses the same package shape for every built-in plugin', () => { const missing = ['bangdream', 'ff14-market', 'fflogs', 'repeater'].flatMap( - (pluginKey) => - requiredPluginPaths + (pluginKey) => { + const manifest = JSON.parse( + readFileSync(join(pluginRoot, pluginKey, 'plugin.json'), 'utf8'), + ) as { events?: unknown[]; operations?: unknown[] }; + const requiredPaths = [ + ...requiredPluginPaths, + ...((manifest.operations || []).length + ? requiredCommandPluginPaths + : []), + ...((manifest.events || []).length ? requiredEventPluginPaths : []), + ]; + return requiredPaths .map((pathName) => `${pluginKey}/${pathName}`) - .filter((pathName) => !existsSync(join(pluginRoot, pathName))), + .filter((pathName) => !existsSync(join(pluginRoot, pathName))); + }, ); expect(missing).toEqual([]); }); + it('does not keep third-phase package directories as empty shells', () => { + const emptyRequiredDirs = [ + 'bangdream', + 'ff14-market', + 'fflogs', + 'repeater', + ].flatMap((pluginKey) => { + const manifest = JSON.parse( + readFileSync(join(pluginRoot, pluginKey, 'plugin.json'), 'utf8'), + ) as { events?: unknown[]; operations?: unknown[] }; + const requiredDirs = [ + 'src/application', + 'src/config', + 'src/domain', + 'src/infrastructure/integration', + ...((manifest.operations || []).length ? ['src/operations'] : []), + ...((manifest.events || []).length ? ['src/events'] : []), + ]; + return requiredDirs + .map((pathName) => join(pluginRoot, pluginKey, pathName)) + .filter((pathName) => collectTsFiles(pathName).length === 0) + .map(toRepoPath); + }); + + expect(emptyRequiredDirs).toEqual([]); + }); + + it('does not keep empty .gitkeep placeholder shells in built-in plugin packages', () => { + const placeholders = readdirSync(pluginRoot) + .filter((name) => statSync(join(pluginRoot, name)).isDirectory()) + .flatMap((pluginKey) => + collectFiles(join(pluginRoot, pluginKey)).filter((filePath) => + filePath.endsWith('.gitkeep'), + ), + ) + .map(toRepoPath); + + expect(placeholders).toEqual([]); + }); + it('keeps plugin runtime source independent from host internals and direct IO', () => { const banned = [ { @@ -131,4 +201,23 @@ describe('QQBot plugin package boundary', () => { expect(duplicateMetadataFiles).toEqual([]); }); + + it('does not keep pure transfer TypeScript files in plugin packages', () => { + const transferFiles = collectTsFiles(pluginRoot) + .filter((filePath) => { + const source = readFileSync(filePath, 'utf8') + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(/\/\/.*$/gm, '') + .trim(); + return ( + source.length > 0 && + source + .split(/\r?\n/) + .every((line) => /^export\s+(?:type\s+)?\*?\s*/.test(line.trim())) + ); + }) + .map(toRepoPath); + + expect(transferFiles).toEqual([]); + }); }); diff --git a/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts b/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts index f0991cf..dba51e6 100644 --- a/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts @@ -1,46 +1,3 @@ -jest.mock( - '@/modules/qqbot/plugins/bangDream/application/bangdream-client.service', - () => ({ - QqbotBangDreamClientService: class QqbotBangDreamClientService {}, - }), -); -jest.mock( - '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade', - () => ({ - QqbotBangDreamRendererService: class QqbotBangDreamRendererService {}, - }), -); -jest.mock( - '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service', - () => ({ - TsuguApplicationService: class TsuguApplicationService {}, - }), -); -jest.mock('@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin', () => ({ - QqbotBangDreamPluginService: class QqbotBangDreamPluginService {}, -})); -jest.mock( - '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service', - () => ({ - QqbotFf14ClientService: class QqbotFf14ClientService {}, - }), -); -jest.mock( - '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin', - () => ({ - QqbotFf14MarketPluginService: class QqbotFf14MarketPluginService {}, - }), -); -jest.mock('@/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service', () => ({ - QqbotFflogsClientService: class QqbotFflogsClientService {}, -})); -jest.mock('@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin', () => ({ - QqbotFflogsPluginService: class QqbotFflogsPluginService {}, -})); -jest.mock('@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin', () => ({ - QqbotRepeaterPluginService: class QqbotRepeaterPluginService {}, -})); - import { existsSync } from 'fs'; import { join } from 'path'; import { MODULE_METADATA } from '@nestjs/common/constants'; diff --git a/test/modules/qqbot/plugins/bangdream-rewrite/bangdream-operation-parity.spec.ts b/test/modules/qqbot/plugins/bangdream-rewrite/bangdream-operation-parity.spec.ts index c53a7a1..f4dcba7 100644 --- a/test/modules/qqbot/plugins/bangdream-rewrite/bangdream-operation-parity.spec.ts +++ b/test/modules/qqbot/plugins/bangdream-rewrite/bangdream-operation-parity.spec.ts @@ -34,6 +34,8 @@ describe('BangDream rewritten plugin parity', () => { const requiredPaths = [ 'plugin.json', 'src/index.ts', + 'src/application/bangdream-command-context.ts', + 'src/application/hook/hook-registry.ts', 'src/operations/song-search', 'src/operations/song-chart', 'src/operations/song-random', @@ -57,13 +59,12 @@ describe('BangDream rewritten plugin parity', () => { 'src/domain/player', 'src/domain/cutoff', 'src/domain/catalog', - 'src/application', + 'src/domain/common/qqbot-bangdream.types.ts', 'src/infrastructure/integration', 'src/infrastructure/storage', 'src/config', + 'src/config/dictionary', 'src/assets', - 'src/migrations', - 'src/tests', ]; const missing = requiredPaths.filter( @@ -104,7 +105,59 @@ describe('BangDream rewritten plugin parity', () => { join(operationSourcePath, 'index.ts'), 'utf8', ); - expect(eventStageSource).toContain('imageCount'); + expect(eventStageSource).toContain('expectedImageCount'); expect(eventStageSource).toContain('5'); }); + + it('makes operation modules executable instead of handler-name placeholders', () => { + const operationRoot = join(newBangDreamRoot, 'src/operations'); + const operationDirs = expectedOperationKeys.map((operationKey) => + operationKey.replace('bangdream.', '').replace(/\./g, '-'), + ); + + for (const operationDir of operationDirs) { + const source = readFileSync( + join(operationRoot, operationDir, 'index.ts'), + 'utf8', + ); + expect(source).toContain('execute: async'); + expect(source).toContain('context.'); + expect(source).not.toMatch(/key: 'bangdream\./); + } + }); + + it('uses real business directories and removes old wrapper layers', () => { + const domainDirs = [ + 'song', + 'card', + 'character', + 'event', + 'gacha', + 'player', + 'cutoff', + 'catalog', + ]; + + for (const domainDir of domainDirs) { + expect(existsSync(join(newBangDreamRoot, 'src/domain', domainDir))).toBe( + true, + ); + expect( + existsSync(join(newBangDreamRoot, 'src/domain', domainDir, 'index.ts')), + ).toBe(false); + } + + for (const removedPath of [ + 'src/application/bangdream-renderer.facade.ts', + 'src/application/bangdream-application.service.ts', + 'src/application/bangdream-context.ts', + 'src/application/bangdream-operation-runtime.ts', + 'src/application/operation-pipeline.ts', + 'src/operations/operation-executor.ts', + 'src/domain/common/bangdream-constants.ts', + 'src/qqbot-bangdream.types.ts', + ]) { + expect(existsSync(join(newBangDreamRoot, removedPath))).toBe(false); + } + }); }); diff --git a/test/modules/qqbot/plugins/bangDream/runtime-config.spec.ts b/test/modules/qqbot/plugins/bangdream/runtime-config.spec.ts similarity index 52% rename from test/modules/qqbot/plugins/bangDream/runtime-config.spec.ts rename to test/modules/qqbot/plugins/bangdream/runtime-config.spec.ts index ff56904..d205c82 100644 --- a/test/modules/qqbot/plugins/bangDream/runtime-config.spec.ts +++ b/test/modules/qqbot/plugins/bangdream/runtime-config.spec.ts @@ -1,15 +1,15 @@ import { resolve } from 'path'; -import { resolveBangDreamProjectRoot } from '@/modules/qqbot/plugins/bangDream/config/runtime-config'; +import { resolveBangDreamProjectRoot } from '@/modules/qqbot/plugins/bangdream/src/config/runtime-config'; describe('BangDream runtime config', () => { - it('resolves source plugin assets when running from compiled dist output', () => { + it('resolves dist plugin assets when running from compiled production output', () => { const compiledConfigDir = resolve( process.cwd(), - 'dist/src/modules/qqbot/plugins/bangDream/config', + 'dist/modules/qqbot/plugins/bangdream/src/config', ); expect(resolveBangDreamProjectRoot(compiledConfigDir)).toBe( - resolve(process.cwd(), 'src/modules/qqbot/plugins/bangDream'), + resolve(process.cwd(), 'dist/modules/qqbot/plugins/bangdream/src'), ); }); }); diff --git a/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts b/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts index 1454837..e651c3c 100644 --- a/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts +++ b/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts @@ -5,26 +5,10 @@ jest.mock('@/modules/admin/identity/auth/jwt-auth.guard', () => ({ } }, })); -jest.mock('@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin', () => ({ - QqbotBangDreamPluginService: class {}, -})); -jest.mock( - '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin', - () => ({ - QqbotFf14MarketPluginService: class {}, - }), -); -jest.mock('@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin', () => ({ - QqbotFflogsPluginService: class {}, -})); - import type { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import * as request from 'supertest'; import { ToolsService } from '../../../../src/common'; -import { QqbotBangDreamPluginService } from '../../../../src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin'; -import { QqbotFf14MarketPluginService } from '../../../../src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin'; -import { QqbotFflogsPluginService } from '../../../../src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin'; import { QqbotPluginController } from '../../../../src/modules/qqbot/plugin-platform/contract/qqbot-plugin.controller'; import { QqbotEventPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service'; import { QqbotPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service'; @@ -55,23 +39,46 @@ describe('QQBot plugin controller local HTTP smoke', () => { controllers: [QqbotPluginController], providers: [ ToolsService, - QqbotPluginRegistryService, { - provide: QqbotBangDreamPluginService, + provide: QqbotPluginRegistryService, useValue: { - getPlugin: () => createPlugin('bangdream', ['bangDream']), - }, - }, - { - provide: QqbotFf14MarketPluginService, - useValue: { - getPlugin: () => createPlugin('ff14-market', ['ff14Market']), - }, - }, - { - provide: QqbotFflogsPluginService, - useValue: { - getPlugin: () => createPlugin('fflogs'), + health: jest.fn(async () => []), + listOperations: jest.fn((pluginKey?: string) => { + const plugins = [ + createPlugin('bangdream', ['bangDream']), + createPlugin('ff14-market', ['ff14Market']), + createPlugin('fflogs'), + ]; + const resolvedPluginKey = + pluginKey === 'bangDream' + ? 'bangdream' + : pluginKey === 'ff14Market' + ? 'ff14-market' + : pluginKey; + return plugins + .filter((plugin) => !resolvedPluginKey || plugin.key === resolvedPluginKey) + .flatMap((plugin) => + plugin.operations.map((operation) => ({ + key: operation.key, + name: operation.name, + pluginKey: plugin.key, + triggerMode: 'command', + })), + ); + }), + listPlugins: jest.fn(() => + [ + createPlugin('bangdream', ['bangDream']), + createPlugin('ff14-market', ['ff14Market']), + createPlugin('fflogs'), + ].map((plugin) => ({ + key: plugin.key, + name: plugin.name, + operationCount: plugin.operations.length, + triggerMode: 'command', + version: plugin.version, + })), + ), }, }, { diff --git a/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts b/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts index 3ae53a8..d022334 100644 --- a/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts +++ b/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts @@ -28,7 +28,7 @@ describe('QQBot existing plugin platform migration', () => { readdirSync(pluginRoot) .filter((name) => statSync(join(pluginRoot, name)).isDirectory()) .sort(), - ).toEqual(['bangDream', 'ff14Market', 'fflogs', 'repeater']); + ).toEqual(['bangdream', 'ff14-market', 'fflogs', 'repeater']); const legacySources = collectFiles(legacyPluginRoot).filter((filePath) => filePath.endsWith('.ts'), @@ -37,7 +37,7 @@ describe('QQBot existing plugin platform migration', () => { }); it('declares parseable platform manifests for every existing plugin', () => { - const manifests = ['bangDream', 'ff14Market', 'fflogs', 'repeater'].map( + const manifests = ['bangdream', 'ff14-market', 'fflogs', 'repeater'].map( (pluginName) => { const root = join(pluginRoot, pluginName); const manifest = parseQqbotPluginManifest( @@ -66,38 +66,111 @@ describe('QQBot existing plugin platform migration', () => { ).toBe(true); }); - it('keeps BangDream manifest operations aligned with the registry metadata', async () => { - const { BANGDREAM_OPERATION_REGISTRY } = - await import('../../../../src/modules/qqbot/plugins/bangDream/registry/operation-registry'); + it('keeps BangDream manifest operations as the single metadata source', () => { const manifest = parseQqbotPluginManifest( - readJson(join(pluginRoot, 'bangDream/plugin.json')), + readJson(join(pluginRoot, 'bangdream/plugin.json')), { - pluginRoot: join(pluginRoot, 'bangDream'), + pluginRoot: join(pluginRoot, 'bangdream'), }, ); - expect(manifest.operations).toHaveLength( - BANGDREAM_OPERATION_REGISTRY.length, - ); expect( manifest.operations.map((operation) => ({ - aliases: operation.aliases, handlerName: operation.handlerName, key: operation.key, name: operation.name, })), - ).toEqual( - BANGDREAM_OPERATION_REGISTRY.map((operation) => ({ - aliases: [...operation.onlineCommand.aliases], - handlerName: operation.handlerName, - key: operation.key, - name: operation.name, - })), - ); + ).toEqual([ + { + handlerName: 'searchSong', + key: 'bangdream.song.search', + name: '查曲', + }, + { + handlerName: 'getSongChart', + key: 'bangdream.song.chart', + name: '查谱面', + }, + { + handlerName: 'randomSong', + key: 'bangdream.song.random', + name: '随机曲', + }, + { + handlerName: 'getSongMeta', + key: 'bangdream.song.meta', + name: '查询分数表', + }, + { + handlerName: 'searchCard', + key: 'bangdream.card.search', + name: '查卡', + }, + { + handlerName: 'getCardIllustration', + key: 'bangdream.card.illustration', + name: '查卡面', + }, + { + handlerName: 'searchCharacter', + key: 'bangdream.character.search', + name: '查角色', + }, + { + handlerName: 'searchEvent', + key: 'bangdream.event.search', + name: '查活动', + }, + { + handlerName: 'getEventStage', + key: 'bangdream.event.stage', + name: '查试炼', + }, + { + handlerName: 'searchPlayer', + key: 'bangdream.player.search', + name: '查玩家', + }, + { + handlerName: 'searchGacha', + key: 'bangdream.gacha.search', + name: '查卡池', + }, + { + handlerName: 'simulateGacha', + key: 'bangdream.gacha.simulate', + name: '抽卡模拟', + }, + { + handlerName: 'getCutoffDetail', + key: 'bangdream.cutoff.detail', + name: 'ycx', + }, + { + handlerName: 'getCutoffAll', + key: 'bangdream.cutoff.all', + name: 'ycxall', + }, + { + handlerName: 'getCutoffRecent', + key: 'bangdream.cutoff.recent', + name: 'lsycx', + }, + ]); + expect( + existsSync( + join(pluginRoot, 'bangdream/src/registry/operation-registry.ts'), + ), + ).toBe(false); + expect( + existsSync( + join(pluginRoot, 'bangdream/src/commands/qqbot-bangdream-command.definitions.ts'), + ), + ).toBe(false); }); it('routes FF14 Market and FFLogs HTTP through the plugin platform SDK', () => { - const externalHttpSources = ['ff14Market', 'fflogs'].flatMap((pluginName) => + const externalHttpSources = ['ff14-market', 'fflogs'].flatMap((pluginName) => collectFiles(join(pluginRoot, pluginName)).filter((filePath) => filePath.endsWith('.ts'), ), diff --git a/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts b/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts index 254b385..2c6a480 100644 --- a/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts +++ b/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts @@ -1,24 +1,6 @@ -jest.mock('@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin', () => ({ - QqbotBangDreamPluginService: class {}, -})); -jest.mock( - '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin', - () => ({ - QqbotFf14MarketPluginService: class {}, - }), -); -jest.mock('@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin', () => ({ - QqbotFflogsPluginService: class {}, -})); - import { QqbotPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service'; import type { QqbotIntegrationPlugin } from '../../../../src/modules/qqbot/core/contract/qqbot.types'; -const asPluginService = (plugin: QqbotIntegrationPlugin) => - ({ - getPlugin: () => plugin, - }) as any; - const createPlugin = ( key: string, legacyKeys: string[], @@ -42,12 +24,15 @@ describe('QQBot plugin registry platform key compatibility', () => { const ff14Market = createPlugin('ff14-market', ['ff14Market']); const fflogs = createPlugin('fflogs', []); const registry = new QqbotPluginRegistryService( - asPluginService(bangdream), - asPluginService(ff14Market), - asPluginService(fflogs), + {} as any, + {} as any, + {} as any, + {} as any, ); - registry.onModuleInit(); + registry.register(bangdream); + registry.register(ff14Market); + registry.register(fflogs); expect(registry.listPlugins().map((plugin) => plugin.key)).toEqual([ 'bangdream', diff --git a/test/qqbot/plugins/bangDream/application/bangdream-application.service.spec.ts b/test/qqbot/plugins/bangDream/application/bangdream-application.service.spec.ts deleted file mode 100644 index 97d455b..0000000 --- a/test/qqbot/plugins/bangDream/application/bangdream-application.service.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -jest.mock( - '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade', - () => ({ - QqbotBangDreamRendererService: class QqbotBangDreamRendererService {}, - }), -); - -jest.mock('@/modules/qqbot/plugins/bangDream/shared/main-data-store', () => ({ - __esModule: true, - default: {}, - waitForMainDataReady: jest.fn().mockResolvedValue(undefined), -})); - -import { ToolsService } from '@/common'; -import { TsuguApplicationService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service'; -import { waitForMainDataReady } from '@/modules/qqbot/plugins/bangDream/shared/main-data-store'; -import type { QqbotBangDreamRendererService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade'; - -describe('TsuguApplicationService', () => { - let rendererService: jest.Mocked; - let service: TsuguApplicationService; - - beforeEach(() => { - jest.clearAllMocks(); - rendererService = { - checkHealth: jest.fn().mockResolvedValue(true), - executeOperationHandler: jest.fn().mockResolvedValue({ - imageCount: 1, - operationKey: 'bangdream.song.search', - query: '夏祭り', - replyText: '[CQ:image,file=base64://base64-song-card]', - source: 'Tsugu BangDream Bot 内置源码', - }), - refreshDictionaryCache: jest.fn().mockResolvedValue(undefined), - } as any; - service = new TsuguApplicationService(rendererService, new ToolsService()); - }); - - it('refreshes dictionary cache during application bootstrap', async () => { - await service.onApplicationBootstrap(); - - expect(rendererService.refreshDictionaryCache).toHaveBeenCalledTimes(1); - }); - - it('executes a registry operation through the renderer handler', async () => { - const result = await service.execute('bangdream.song.search', { - text: '夏祭り', - }); - - expect(waitForMainDataReady).toHaveBeenCalledTimes(1); - expect(rendererService.executeOperationHandler).toHaveBeenCalledWith( - 'searchSong', - { - text: '夏祭り', - }, - ); - expect(result).toMatchObject({ - imageCount: 1, - operationKey: 'bangdream.song.search', - query: '夏祭り', - }); - }); - - it('normalizes command errors into string messages', async () => { - rendererService.executeOperationHandler.mockRejectedValueOnce( - '图片渲染失败', - ); - - await expect( - service.execute('bangdream.event.search', { text: '50' }), - ).rejects.toThrow('图片渲染失败'); - }); -}); diff --git a/test/qqbot/plugins/bangDream/application/bangdream-client.service.spec.ts b/test/qqbot/plugins/bangDream/application/bangdream-client.service.spec.ts deleted file mode 100644 index 27f7aea..0000000 --- a/test/qqbot/plugins/bangDream/application/bangdream-client.service.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -jest.mock( - '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service', - () => ({ - TsuguApplicationService: class TsuguApplicationService {}, - }), -); - -import { QqbotBangDreamClientService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-client.service'; -import type { TsuguApplicationService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service'; - -describe('QqbotBangDreamClientService', () => { - let service: QqbotBangDreamClientService; - let tsuguApplicationService: jest.Mocked; - - beforeEach(() => { - tsuguApplicationService = { - checkHealth: jest.fn().mockReturnValue(true), - execute: jest.fn().mockResolvedValue({ - imageCount: 1, - operationKey: 'bangdream.song.search', - query: '夏祭り', - replyText: '[CQ:image,file=base64://base64-song-card]', - source: 'Tsugu BangDream Bot 内置源码', - }), - } as any; - service = new QqbotBangDreamClientService(tsuguApplicationService); - }); - - it('checks embedded Tsugu application health', async () => { - await expect(service.checkHealth()).resolves.toBe(true); - - expect(tsuguApplicationService.checkHealth).toHaveBeenCalledTimes(1); - }); - - it('searches song through embedded Tsugu application facade', async () => { - const result = await service.searchSong({ text: '夏祭り' }); - - expect(tsuguApplicationService.execute).toHaveBeenCalledWith( - 'bangdream.song.search', - { - text: '夏祭り', - }, - ); - expect(result).toMatchObject({ - imageCount: 1, - query: '夏祭り', - source: 'Tsugu BangDream Bot 内置源码', - }); - expect(result.replyText).toBe('[CQ:image,file=base64://base64-song-card]'); - }); - - it('executes other Tsugu open commands through the same plugin facade', async () => { - await service.execute('bangdream.card.search', { text: '1399' }); - - expect(tsuguApplicationService.execute).toHaveBeenCalledWith( - 'bangdream.card.search', - { - text: '1399', - }, - ); - }); - - it('bubbles embedded Tsugu renderer errors as command errors', async () => { - tsuguApplicationService.execute.mockRejectedValueOnce( - new Error('错误: 没有有效的关键词'), - ); - - await expect(service.searchSong({ text: '???' })).rejects.toThrow( - '错误: 没有有效的关键词', - ); - }); -}); diff --git a/test/qqbot/plugins/bangDream/application/operation-pipeline.spec.ts b/test/qqbot/plugins/bangDream/application/operation-pipeline.spec.ts deleted file mode 100644 index c6e9ef9..0000000 --- a/test/qqbot/plugins/bangDream/application/operation-pipeline.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { TsuguHookRegistry } from '@/modules/qqbot/plugins/bangDream/hook/hook-registry'; -import { TsuguOperationPipeline } from '@/modules/qqbot/plugins/bangDream/application/operation-pipeline'; -import type { TsuguOperationDefinition } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -const songSearchOperation: TsuguOperationDefinition = { - description: '查询歌曲', - handlerName: 'searchSong', - key: 'bangdream.song.search', - name: '查曲', - onlineCommand: { - aliases: ['查曲'], - cooldownMs: 1500, - remark: '查询歌曲', - }, -}; - -describe('TsuguOperationPipeline', () => { - it('runs ready check, operation resolve, handler, and output hooks', async () => { - const events: string[] = []; - const hookRegistry = new TsuguHookRegistry([ - { - afterOutput: (context) => { - events.push(`afterOutput:${context.stage}:${context.imageCount}`); - }, - afterResolve: (context) => { - events.push(`afterResolve:${context.stage}:${context.handlerName}`); - }, - beforeParse: (context) => { - events.push(`beforeParse:${context.stage}:${context.query}`); - }, - beforeRender: (context) => { - events.push(`beforeRender:${context.stage}:${context.handlerName}`); - }, - name: 'recorder', - }, - ]); - const pipeline = new TsuguOperationPipeline({ - executeHandler: async (handlerName, input) => { - events.push(`handler:${handlerName}:${input.text}`); - return { - imageCount: 1, - operationKey: 'bangdream.song.search', - query: '夏祭り', - replyText: '[CQ:image,file=base64://base64-song-card]', - source: 'Tsugu BangDream Bot 内置源码', - }; - }, - hookRegistry, - normalizeError: (error) => `${error}`, - resolveOperation: () => songSearchOperation, - waitForReady: async () => { - events.push('ready'); - }, - }); - - const output = await pipeline.run('bangdream.song.search', { - text: '夏祭り', - }); - - expect(output.imageCount).toBe(1); - expect(events).toEqual([ - 'beforeParse:start:夏祭り', - 'ready', - 'afterResolve:operation:searchSong', - 'beforeRender:handler:searchSong', - 'handler:searchSong:夏祭り', - 'afterOutput:output:1', - ]); - }); - - it('normalizes unknown operation errors and emits error hook', async () => { - const errors: string[] = []; - const pipeline = new TsuguOperationPipeline({ - executeHandler: jest.fn(), - hookRegistry: new TsuguHookRegistry([ - { - name: 'error-recorder', - onError: (context, error) => { - errors.push(`${context.stage}:${error}`); - }, - }, - ]), - normalizeError: (error) => - error instanceof Error ? error.message : `${error}`, - resolveOperation: () => undefined, - waitForReady: jest.fn().mockResolvedValue(undefined), - }); - - await expect( - pipeline.run('bangdream.song.search', { text: '夏祭り' }), - ).rejects.toThrow('BangDream 插件能力不存在:bangdream.song.search'); - expect(errors).toEqual([ - 'operation:BangDream 插件能力不存在:bangdream.song.search', - ]); - }); - - it('normalizes handler errors at handler stage', async () => { - const errors: string[] = []; - const pipeline = new TsuguOperationPipeline({ - executeHandler: jest.fn().mockRejectedValue('图片渲染失败'), - hookRegistry: new TsuguHookRegistry([ - { - name: 'error-recorder', - onError: (context, error) => { - errors.push(`${context.stage}:${context.handlerName}:${error}`); - }, - }, - ]), - normalizeError: (error) => `${error}`, - resolveOperation: () => songSearchOperation, - waitForReady: jest.fn().mockResolvedValue(undefined), - }); - - await expect( - pipeline.run('bangdream.song.search', { text: '夏祭り' }), - ).rejects.toThrow('图片渲染失败'); - expect(errors).toEqual(['handler:searchSong:图片渲染失败']); - }); -}); diff --git a/test/qqbot/plugins/bangDream/registry/command-sql.spec.ts b/test/qqbot/plugins/bangDream/registry/command-sql.spec.ts deleted file mode 100644 index 9e8da92..0000000 --- a/test/qqbot/plugins/bangDream/registry/command-sql.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { BANGDREAM_OPERATION_REGISTRY } from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -type BangDreamSqlCommandRow = { - aliases: string[]; - cooldownMs: number; - operationKey: string; - remark: string; -}; - -const BANGDREAM_SQL_ROW_PATTERN = - /\(\d+, '[^']+', '[^']+', '(\[[^']*\])', '\[[^']*\]', 'bangDream', '([^']+)', 'plain', 'all', '\{\}', '', '[^']*', 1, 0, (\d+), '([^']*)'\)/g; - -/** - * 从初始化 SQL 中提取 BangDream 在线命令行。 - * - * @param sql - qqbot 初始化 SQL。 - */ -function getBangDreamSqlCommandRows(sql: string) { - return Array.from(sql.matchAll(BANGDREAM_SQL_ROW_PATTERN)).map( - (match): BangDreamSqlCommandRow => ({ - aliases: JSON.parse(match[1]) as string[], - cooldownMs: Number(match[3]), - operationKey: match[2], - remark: match[4], - }), - ); -} - -describe('qqbot BangDream command init SQL', () => { - const sql = readFileSync(join(process.cwd(), 'sql/qqbot-init.sql'), 'utf8'); - const sqlRows = getBangDreamSqlCommandRows(sql); - - it('keeps every BangDream plugin operation available as an online command', () => { - const sqlOperationKeys = sqlRows.map((row) => row.operationKey); - const definedOperationKeys = BANGDREAM_OPERATION_REGISTRY.map( - (operation) => operation.key, - ); - - expect([...sqlOperationKeys].sort()).toEqual( - [...definedOperationKeys].sort(), - ); - expect(sqlOperationKeys).toHaveLength(15); - }); - - it('keeps online command aliases, cooldown, and remarks aligned with registry', () => { - const rowsByKey = new Map( - sqlRows.map((row) => [row.operationKey, row] as const), - ); - - for (const operation of BANGDREAM_OPERATION_REGISTRY) { - const row = rowsByKey.get(operation.key); - - expect(row).toEqual( - expect.objectContaining({ - aliases: [...operation.onlineCommand.aliases], - cooldownMs: operation.onlineCommand.cooldownMs, - remark: operation.onlineCommand.remark, - }), - ); - } - }); - - it('overwrites dirty online command metadata on duplicate ids', () => { - expect(sql).toContain('`code` = VALUES(`code`)'); - expect(sql).toContain('`is_deleted` = 0'); - }); -}); diff --git a/test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts b/test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts deleted file mode 100644 index e1c6375..0000000 --- a/test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - BANGDREAM_OPERATION_REGISTRY, - getBangDreamOperationDefinition, -} from '@/modules/qqbot/plugins/bangDream/registry/operation-registry'; - -describe('BangDream Tsugu operation registry', () => { - it('keeps operation keys unique and complete', () => { - const operationKeys = BANGDREAM_OPERATION_REGISTRY.map( - (operation) => operation.key, - ); - - expect(new Set(operationKeys).size).toBe(operationKeys.length); - expect(operationKeys).toHaveLength(15); - }); - - it('keeps every operation bound to a handler and online command defaults', () => { - for (const operation of BANGDREAM_OPERATION_REGISTRY) { - expect(operation.handlerName).toEqual(expect.any(String)); - expect(operation.name).not.toHaveLength(0); - expect(operation.description).not.toHaveLength(0); - expect(operation.onlineCommand.aliases.length).toBeGreaterThan(0); - expect(operation.onlineCommand.cooldownMs).toBeGreaterThan(0); - expect(operation.onlineCommand.remark).not.toHaveLength(0); - } - }); - - it('finds operations by key', () => { - expect( - getBangDreamOperationDefinition('bangdream.song.search'), - ).toMatchObject({ - handlerName: 'searchSong', - name: '查曲', - }); - expect( - getBangDreamOperationDefinition('bangdream.unknown'), - ).toBeUndefined(); - }); -}); diff --git a/test/qqbot/plugins/bangdream/application/bangdream-package-entry.spec.ts b/test/qqbot/plugins/bangdream/application/bangdream-package-entry.spec.ts new file mode 100644 index 0000000..833a0a9 --- /dev/null +++ b/test/qqbot/plugins/bangdream/application/bangdream-package-entry.spec.ts @@ -0,0 +1,134 @@ +jest.mock('@/modules/qqbot/plugins/bangdream/src/application/main-data-store', () => ({ + __esModule: true, + default: {}, + waitForMainDataReady: jest.fn().mockResolvedValue(undefined), +})); + +const mockContext = { + checkHealth: jest.fn(), + refreshDictionaryCache: jest.fn(), +}; +const mockConfigureBangDreamRuntimeIo = jest.fn(); +const mockOperationModules = new Map(); +const mockManifestOperations = [ + ['bangdream.song.search', 'searchSong'], + ['bangdream.song.chart', 'getSongChart'], + ['bangdream.song.random', 'randomSong'], + ['bangdream.song.meta', 'getSongMeta'], + ['bangdream.card.search', 'searchCard'], + ['bangdream.card.illustration', 'getCardIllustration'], + ['bangdream.character.search', 'searchCharacter'], + ['bangdream.event.search', 'searchEvent'], + ['bangdream.event.stage', 'getEventStage'], + ['bangdream.player.search', 'searchPlayer'], + ['bangdream.gacha.search', 'searchGacha'], + ['bangdream.gacha.simulate', 'simulateGacha'], + ['bangdream.cutoff.detail', 'getCutoffDetail'], + ['bangdream.cutoff.all', 'getCutoffAll'], + ['bangdream.cutoff.recent', 'getCutoffRecent'], +] as const; + +jest.mock( + '@/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context', + () => ({ + BangDreamCommandContext: jest.fn(() => mockContext), + }), +); + +jest.mock( + '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io', + () => ({ + configureBangDreamRuntimeIo: mockConfigureBangDreamRuntimeIo, + }), +); + +jest.mock('@/modules/qqbot/plugins/bangdream/src/operations', () => ({ + getBangDreamOperationsByHandlerName: () => + new Map( + mockManifestOperations.map(([key, handlerName]) => { + const execute = jest.fn(); + mockOperationModules.set(key, execute); + return [ + handlerName, + { + execute, + handlerName, + }, + ]; + }), + ), +})); + +import { + createPlugin, + type BangDreamManifestOperation, +} from '@/modules/qqbot/plugins/bangdream/src'; +import { BangDreamCommandContext } from '@/modules/qqbot/plugins/bangdream/src/application/bangdream-command-context'; +import { waitForMainDataReady } from '@/modules/qqbot/plugins/bangdream/src/application/main-data-store'; + +const manifestOperations: BangDreamManifestOperation[] = + mockManifestOperations.map(([key, handlerName]) => ({ handlerName, key })); + +describe('BangDream package entry', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockOperationModules.clear(); + mockContext.refreshDictionaryCache.mockResolvedValue(undefined); + mockContext.checkHealth.mockResolvedValue(true); + }); + + it('binds every manifest operation directly to the package operation modules', async () => { + const io = { + requestJson: jest.fn(), + } as any; + const plugin = createPlugin({ + io, + operations: manifestOperations, + }); + const songSearch = mockOperationModules.get('bangdream.song.search'); + songSearch?.mockResolvedValueOnce({ + imageCount: 1, + operationKey: 'bangdream.song.search', + query: '夏祭り', + replyText: '[CQ:image,file=base64://base64-song-card]', + source: 'BangDream 内置插件', + }); + + expect(mockConfigureBangDreamRuntimeIo).toHaveBeenCalledWith(io); + expect(BangDreamCommandContext).toHaveBeenCalledWith({ + io, + operations: manifestOperations, + }); + + await plugin.activate(); + await expect(plugin.health()).resolves.toBe(true); + await expect( + plugin.executeOperation('bangdream.song.search', { text: '夏祭り' }), + ).resolves.toMatchObject({ + imageCount: 1, + query: '夏祭り', + }); + + expect(mockContext.refreshDictionaryCache).toHaveBeenCalledTimes(1); + expect(waitForMainDataReady).toHaveBeenCalledTimes(1); + expect(songSearch).toHaveBeenCalledWith( + { text: '夏祭り' }, + mockContext, + ); + expect(manifestOperations).toHaveLength(15); + }); + + it('normalizes operation errors without an application-service wrapper', async () => { + const plugin = createPlugin({ + normalizeError: (error) => `normalized:${error}`, + operations: manifestOperations, + }); + mockOperationModules + .get('bangdream.event.search') + ?.mockRejectedValueOnce('图片渲染失败'); + + await expect( + plugin.executeOperation('bangdream.event.search', { text: '50' }), + ).rejects.toThrow('normalized:图片渲染失败'); + }); +}); diff --git a/test/qqbot/plugins/bangDream/card/card-art.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-art.layout.spec.ts similarity index 93% rename from test/qqbot/plugins/bangDream/card/card-art.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-art.layout.spec.ts index 63ea6cc..2bf2dc5 100644 --- a/test/qqbot/plugins/bangDream/card/card-art.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-art.layout.spec.ts @@ -2,7 +2,7 @@ import { BANGDREAM_CARD_ART_SPEC, createCardIconFramePath, createCardIllustrationFramePath, -} from '@/modules/qqbot/plugins/bangDream/card/card-art.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.layout'; describe('BangDream card art spec', () => { it('creates icon frame paths with Bestdori rarity rules', () => { diff --git a/test/qqbot/plugins/bangDream/card/card-art.repository.spec.ts b/test/qqbot/plugins/bangdream/card/card-art.repository.spec.ts similarity index 91% rename from test/qqbot/plugins/bangDream/card/card-art.repository.spec.ts rename to test/qqbot/plugins/bangdream/card/card-art.repository.spec.ts index a704de0..1b311de 100644 --- a/test/qqbot/plugins/bangDream/card/card-art.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-art.repository.spec.ts @@ -1,5 +1,5 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { CardArtResourceRepository } from '@/modules/qqbot/plugins/bangDream/card/card-art.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { CardArtResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-art.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/card/card-icon.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-icon.layout.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/card/card-icon.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-icon.layout.spec.ts index c7f5a07..460a6fd 100644 --- a/test/qqbot/plugins/bangDream/card/card-icon.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-icon.layout.spec.ts @@ -1,11 +1,11 @@ -import { BangDreamCardType } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +import { BangDreamCardType } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; import { BANGDREAM_CARD_ICON_LIST_SPEC, compareCardIconListCards, getCardIconListSpacing, getCardIconListTextSize, sortCardIconListCards, -} from '@/modules/qqbot/plugins/bangDream/card/card-icon.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-icon.layout'; describe('BangDream card icon list spec', () => { it('keeps card icon list text and spacing ratios stable', () => { diff --git a/test/qqbot/plugins/bangDream/card/card-prefix.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-prefix.layout.spec.ts similarity index 91% rename from test/qqbot/plugins/bangDream/card/card-prefix.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-prefix.layout.spec.ts index 6a67925..fa6717f 100644 --- a/test/qqbot/plugins/bangDream/card/card-prefix.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-prefix.layout.spec.ts @@ -1,8 +1,8 @@ import { BANGDREAM_CARD_PREFIX_SPEC, getCardPrefixBandLogoLayout, -} from '@/modules/qqbot/plugins/bangDream/card/card-prefix.layout'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-prefix.layout'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; describe('BangDream card prefix spec', () => { it('keeps the card prefix canvas and background stable', () => { diff --git a/test/qqbot/plugins/bangDream/card/card-rarity.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-rarity.layout.spec.ts similarity index 89% rename from test/qqbot/plugins/bangDream/card/card-rarity.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-rarity.layout.spec.ts index 774dd22..cb3f6a3 100644 --- a/test/qqbot/plugins/bangDream/card/card-rarity.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-rarity.layout.spec.ts @@ -1,7 +1,7 @@ import { BANGDREAM_RARITY_LIST_SPEC, shouldUseTrainedRarityStar, -} from '@/modules/qqbot/plugins/bangDream/card/card-rarity.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-rarity.layout'; describe('BangDream rarity list spec', () => { it('keeps the historical rarity list text size and spacing stable', () => { diff --git a/test/qqbot/plugins/bangDream/card/card-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/card/card-resource.repository.spec.ts similarity index 93% rename from test/qqbot/plugins/bangDream/card/card-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/card/card-resource.repository.spec.ts index 5fbfa0e..e665b25 100644 --- a/test/qqbot/plugins/bangDream/card/card-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-resource.repository.spec.ts @@ -1,9 +1,9 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { CardResourceRepository, type CardResourceSource, -} from '@/modules/qqbot/plugins/bangDream/card/card-resource.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-resource.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/card/card-sd-character.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-sd-character.layout.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/card/card-sd-character.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-sd-character.layout.spec.ts index a68c09c..5f59ccc 100644 --- a/test/qqbot/plugins/bangDream/card/card-sd-character.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-sd-character.layout.spec.ts @@ -4,7 +4,7 @@ import { getCardSdCharacterCropRects, getCardSdCharacterListLineHeight, getCardSdCharacterListTextSize, -} from '@/modules/qqbot/plugins/bangDream/card/card-sd-character.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-sd-character.layout'; describe('BangDream card SD character list spec', () => { it('keeps the historical four-frame SD character crop grid', () => { diff --git a/test/qqbot/plugins/bangDream/card/card-skill-text.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-skill-text.layout.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/card/card-skill-text.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-skill-text.layout.spec.ts index 5c5ad16..3a25a0f 100644 --- a/test/qqbot/plugins/bangDream/card/card-skill-text.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-skill-text.layout.spec.ts @@ -2,7 +2,7 @@ import { BANGDREAM_SKILL_TEXT_SPEC, createSkillTextFragments, getSkillScoreSuffix, -} from '@/modules/qqbot/plugins/bangDream/card/card-skill-text.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-skill-text.layout'; describe('BangDream skill text spec', () => { it('keeps the historical card skill text layout stable', () => { diff --git a/test/qqbot/plugins/bangDream/card/card-stat.layout.spec.ts b/test/qqbot/plugins/bangdream/card/card-stat.layout.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/card/card-stat.layout.spec.ts rename to test/qqbot/plugins/bangdream/card/card-stat.layout.spec.ts index 64fa337..24a3423 100644 --- a/test/qqbot/plugins/bangDream/card/card-stat.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/card/card-stat.layout.spec.ts @@ -3,7 +3,7 @@ import { createStatLineText, getStatLineBarLayout, getStatLineBarWidth, -} from '@/modules/qqbot/plugins/bangDream/card/card-stat.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/card/card-stat.layout'; describe('BangDream stat list spec', () => { it('keeps the historical stat list spacer stable', () => { diff --git a/test/qqbot/plugins/bangDream/catalog/attribute-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/catalog/attribute-resource.repository.spec.ts similarity index 89% rename from test/qqbot/plugins/bangDream/catalog/attribute-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/catalog/attribute-resource.repository.spec.ts index c9f9390..7fccf68 100644 --- a/test/qqbot/plugins/bangDream/catalog/attribute-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/attribute-resource.repository.spec.ts @@ -1,5 +1,5 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { AttributeResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/attribute-resource.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { AttributeResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/attribute-resource.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/catalog/band-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/catalog/band-resource.repository.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/catalog/band-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/catalog/band-resource.repository.spec.ts index aa2f0d7..75fb4b2 100644 --- a/test/qqbot/plugins/bangDream/catalog/band-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/band-resource.repository.spec.ts @@ -1,5 +1,5 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { BandResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/band-resource.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { BandResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/band-resource.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/catalog/costume-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/catalog/costume-resource.repository.spec.ts similarity index 88% rename from test/qqbot/plugins/bangDream/catalog/costume-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/catalog/costume-resource.repository.spec.ts index bc3ac26..d5d3831 100644 --- a/test/qqbot/plugins/bangDream/catalog/costume-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/costume-resource.repository.spec.ts @@ -1,6 +1,6 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { CostumeResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/costume-resource.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { CostumeResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/costume-resource.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/catalog/degree-list.layout.spec.ts b/test/qqbot/plugins/bangdream/catalog/degree-list.layout.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/catalog/degree-list.layout.spec.ts rename to test/qqbot/plugins/bangdream/catalog/degree-list.layout.spec.ts index 1ece777..1a6d145 100644 --- a/test/qqbot/plugins/bangDream/catalog/degree-list.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/degree-list.layout.spec.ts @@ -5,7 +5,7 @@ import { shouldDrawDegreeDecorations, shouldDrawDegreeIcon, shouldStopAfterFirstMusicRewardGroup, -} from '@/modules/qqbot/plugins/bangDream/catalog/degree-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-list.layout'; describe('BangDream degree list spec', () => { it('keeps badge and list sizes stable', () => { diff --git a/test/qqbot/plugins/bangDream/catalog/degree-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/catalog/degree-resource.repository.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/catalog/degree-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/catalog/degree-resource.repository.spec.ts index def0bac..50eda46 100644 --- a/test/qqbot/plugins/bangDream/catalog/degree-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/degree-resource.repository.spec.ts @@ -1,6 +1,6 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { DegreeResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/degree-resource.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { DegreeResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/degree-resource.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/catalog/item-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/catalog/item-resource.repository.spec.ts similarity index 87% rename from test/qqbot/plugins/bangDream/catalog/item-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/catalog/item-resource.repository.spec.ts index 018eee6..969c70d 100644 --- a/test/qqbot/plugins/bangDream/catalog/item-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/item-resource.repository.spec.ts @@ -1,9 +1,9 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { ItemResourceRepository, type ItemResourceSource, -} from '@/modules/qqbot/plugins/bangDream/catalog/item-resource.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/item-resource.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/catalog/server-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/catalog/server-resource.repository.spec.ts similarity index 90% rename from test/qqbot/plugins/bangDream/catalog/server-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/catalog/server-resource.repository.spec.ts index 78e4f0f..11457f7 100644 --- a/test/qqbot/plugins/bangDream/catalog/server-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/catalog/server-resource.repository.spec.ts @@ -1,5 +1,5 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { ServerResourceRepository } from '@/modules/qqbot/plugins/bangDream/catalog/server-resource.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { ServerResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server-resource.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/character/character-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/character/character-resource.repository.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/character/character-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/character/character-resource.repository.spec.ts index 9dc9279..9ff2dc4 100644 --- a/test/qqbot/plugins/bangDream/character/character-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/character/character-resource.repository.spec.ts @@ -1,5 +1,5 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { CharacterResourceRepository } from '@/modules/qqbot/plugins/bangDream/character/character-resource.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { CharacterResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/character/character-resource.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/cutoff/chart.layout.spec.ts b/test/qqbot/plugins/bangdream/cutoff/chart.layout.spec.ts similarity index 86% rename from test/qqbot/plugins/bangDream/cutoff/chart.layout.spec.ts rename to test/qqbot/plugins/bangdream/cutoff/chart.layout.spec.ts index cd8bee3..806044f 100644 --- a/test/qqbot/plugins/bangDream/cutoff/chart.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/cutoff/chart.layout.spec.ts @@ -1,12 +1,12 @@ import { BANGDREAM_CUTOFF_CHART_SPEC, stripCutoffChartLabelTags, -} from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-chart.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-chart.layout'; import { BANGDREAM_TIMELINE_CHART_SPEC, getTimelineDisplayYMax, getTimelineRawYMax, -} from '@/modules/qqbot/plugins/bangDream/cutoff/timeline-chart.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/timeline-chart.layout'; describe('BangDream chart specs', () => { it('keeps timeline chart dimensions and y-axis padding stable', () => { diff --git a/test/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository.spec.ts b/test/qqbot/plugins/bangdream/cutoff/cutoff-event-top.repository.spec.ts similarity index 85% rename from test/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository.spec.ts rename to test/qqbot/plugins/bangdream/cutoff/cutoff-event-top.repository.spec.ts index 35b1b4f..532262c 100644 --- a/test/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/cutoff/cutoff-event-top.repository.spec.ts @@ -1,6 +1,6 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { CutoffEventTopRepository } from '@/modules/qqbot/plugins/bangDream/cutoff/cutoff-event-top.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { CutoffEventTopRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/dictionary/dictionary-loader.spec.ts b/test/qqbot/plugins/bangdream/dictionary/dictionary-loader.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/dictionary/dictionary-loader.spec.ts rename to test/qqbot/plugins/bangdream/dictionary/dictionary-loader.spec.ts index ec1dbcb..823ac39 100644 --- a/test/qqbot/plugins/bangDream/dictionary/dictionary-loader.spec.ts +++ b/test/qqbot/plugins/bangdream/dictionary/dictionary-loader.spec.ts @@ -1,13 +1,13 @@ import { BangDreamDictionaryLoader, normalizeDictionaryLookupKey, -} from '@/modules/qqbot/plugins/bangDream/dictionary/dictionary-loader'; -import { BANGDREAM_DICTIONARY_CODES } from '@/modules/qqbot/plugins/bangDream/dictionary/default-dictionary'; +} from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/dictionary-loader'; +import { BANGDREAM_DICTIONARY_CODES } from '@/modules/qqbot/plugins/bangdream/src/config/dictionary/default-dictionary'; import { normalizeBangDreamBoolean, splitBangDreamOptionList, -} from '@/modules/qqbot/plugins/bangDream/config/runtime-options'; -import { BangDreamDifficultyId } from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +} from '@/modules/qqbot/plugins/bangdream/src/config/runtime-options'; +import { BangDreamDifficultyId } from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; describe('BangDreamDictionaryLoader', () => { it('resolves server and difficulty from default dictionary without DB', () => { diff --git a/test/qqbot/plugins/bangDream/event/event-data.repository.spec.ts b/test/qqbot/plugins/bangdream/event/event-data.repository.spec.ts similarity index 88% rename from test/qqbot/plugins/bangDream/event/event-data.repository.spec.ts rename to test/qqbot/plugins/bangdream/event/event-data.repository.spec.ts index a280492..100bd78 100644 --- a/test/qqbot/plugins/bangDream/event/event-data.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/event/event-data.repository.spec.ts @@ -1,9 +1,9 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { EventDataRepository, type EventAssetContext, -} from '@/modules/qqbot/plugins/bangDream/event/event-data.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-data.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/event/event-stage-data.repository.spec.ts b/test/qqbot/plugins/bangdream/event/event-stage-data.repository.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/event/event-stage-data.repository.spec.ts rename to test/qqbot/plugins/bangdream/event/event-stage-data.repository.spec.ts index f9177a3..b5b0194 100644 --- a/test/qqbot/plugins/bangDream/event/event-stage-data.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/event/event-stage-data.repository.spec.ts @@ -1,5 +1,5 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { EventStageDataRepository } from '@/modules/qqbot/plugins/bangDream/event/event-stage-data.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { EventStageDataRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage-data.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/event/event-stage.layout.spec.ts b/test/qqbot/plugins/bangdream/event/event-stage.layout.spec.ts similarity index 96% rename from test/qqbot/plugins/bangDream/event/event-stage.layout.spec.ts rename to test/qqbot/plugins/bangdream/event/event-stage.layout.spec.ts index 095637e..88641fc 100644 --- a/test/qqbot/plugins/bangDream/event/event-stage.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/event/event-stage.layout.spec.ts @@ -7,7 +7,7 @@ import { getEventStageStageBatchSize, shouldStartNewEventStageColumn, splitEventStageImagesByColumnHeight, -} from '@/modules/qqbot/plugins/bangDream/event/event-stage.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.layout'; describe('BangDream event stage spec', () => { it('keeps song row dimensions compatible with existing layout', () => { diff --git a/test/qqbot/plugins/bangDream/event/event-time.layout.spec.ts b/test/qqbot/plugins/bangdream/event/event-time.layout.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/event/event-time.layout.spec.ts rename to test/qqbot/plugins/bangdream/event/event-time.layout.spec.ts index 2c2db31..2bbcd96 100644 --- a/test/qqbot/plugins/bangDream/event/event-time.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/event/event-time.layout.spec.ts @@ -5,7 +5,7 @@ import { formatBangDreamPeriod, formatBangDreamSeconds, formatBangDreamTime, -} from '@/modules/qqbot/plugins/bangDream/event/event-time.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/event/event-time.layout'; describe('BangDream time format spec', () => { it('keeps common labels and offsets stable', () => { diff --git a/test/qqbot/plugins/bangDream/gacha/gacha-list.layout.spec.ts b/test/qqbot/plugins/bangdream/gacha/gacha-list.layout.spec.ts similarity index 91% rename from test/qqbot/plugins/bangDream/gacha/gacha-list.layout.spec.ts rename to test/qqbot/plugins/bangdream/gacha/gacha-list.layout.spec.ts index 98a6920..eb7b81d 100644 --- a/test/qqbot/plugins/bangDream/gacha/gacha-list.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/gacha/gacha-list.layout.spec.ts @@ -1,7 +1,7 @@ import { BANGDREAM_GACHA_LIST_SPEC, getGachaPaymentBehaviorLabel, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-list.layout'; describe('BangDream gacha list spec', () => { it('keeps gacha list labels stable', () => { diff --git a/test/qqbot/plugins/bangDream/gacha/gacha-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/gacha/gacha-resource.repository.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/gacha/gacha-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/gacha/gacha-resource.repository.spec.ts index 392f2ef..0d2b688 100644 --- a/test/qqbot/plugins/bangDream/gacha/gacha-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/gacha/gacha-resource.repository.spec.ts @@ -1,9 +1,9 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { GachaResourceRepository, type GachaResourceSource, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha-resource.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-resource.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/gacha/gacha-simulate.layout.spec.ts b/test/qqbot/plugins/bangdream/gacha/gacha-simulate.layout.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/gacha/gacha-simulate.layout.spec.ts rename to test/qqbot/plugins/bangdream/gacha/gacha-simulate.layout.spec.ts index 1c756ca..8c33d57 100644 --- a/test/qqbot/plugins/bangDream/gacha/gacha-simulate.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/gacha/gacha-simulate.layout.spec.ts @@ -7,7 +7,7 @@ import { getGachaDuplicateIconRect, getGachaDuplicateLayerCount, getGachaSimulateGridMaxWidth, -} from '@/modules/qqbot/plugins/bangDream/gacha/gacha-simulate.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.layout'; describe('BangDream gacha simulate spec', () => { it('keeps grid widths and wrap modes stable', () => { diff --git a/test/qqbot/plugins/bangDream/hook/hook-registry.spec.ts b/test/qqbot/plugins/bangdream/hook/hook-registry.spec.ts similarity index 76% rename from test/qqbot/plugins/bangDream/hook/hook-registry.spec.ts rename to test/qqbot/plugins/bangdream/hook/hook-registry.spec.ts index c6a79a4..9b0fe27 100644 --- a/test/qqbot/plugins/bangDream/hook/hook-registry.spec.ts +++ b/test/qqbot/plugins/bangdream/hook/hook-registry.spec.ts @@ -1,12 +1,12 @@ import { - createTsuguHookContext, - TsuguHookRegistry, -} from '@/modules/qqbot/plugins/bangDream/hook/hook-registry'; + createBangDreamHookContext, + BangDreamHookRegistry, +} from '@/modules/qqbot/plugins/bangdream/src/application/hook/hook-registry'; -describe('BangDream Tsugu hook registry', () => { +describe('BangDream hook registry', () => { it('emits simple lifecycle hooks by configured order', async () => { const events: string[] = []; - const registry = new TsuguHookRegistry([ + const registry = new BangDreamHookRegistry([ { afterOutput: () => { events.push('afterOutput:2'); @@ -28,7 +28,7 @@ describe('BangDream Tsugu hook registry', () => { order: 1, }, ]); - const context = createTsuguHookContext('bangdream.song.search', { + const context = createBangDreamHookContext('bangdream.song.search', { text: '夏祭り', }); @@ -45,7 +45,7 @@ describe('BangDream Tsugu hook registry', () => { it('passes shared context and error to error hooks', async () => { const errors: string[] = []; - const registry = new TsuguHookRegistry([ + const registry = new BangDreamHookRegistry([ { name: 'error-recorder', onError: (context, error) => { @@ -53,7 +53,7 @@ describe('BangDream Tsugu hook registry', () => { }, }, ]); - const context = createTsuguHookContext('bangdream.event.search', { + const context = createBangDreamHookContext('bangdream.event.search', { args: ['50'], }); context.stage = 'handler'; diff --git a/test/qqbot/plugins/bangdream/manifest/command-sql.spec.ts b/test/qqbot/plugins/bangdream/manifest/command-sql.spec.ts new file mode 100644 index 0000000..c74cc44 --- /dev/null +++ b/test/qqbot/plugins/bangdream/manifest/command-sql.spec.ts @@ -0,0 +1,120 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { parseQqbotPluginManifest } from '@/modules/qqbot/plugin-platform/domain/manifest'; + +type BangDreamSqlCommandRow = { + aliases: string[]; + cooldownMs: number; + operationKey: string; + remark?: string; +}; + +const BANGDREAM_FULL_SQL_ROW_PATTERN = + /\(\d+, '[^']+', '[^']+', '(\[[^']*\])', '\[[^']*\]', 'bangdream', '([^']+)', 'plain', 'all', '\{\}', '', '[^']*', 1, 0, (\d+), '([^']*)'\)/g; + +const BANGDREAM_REFACTOR_V3_SQL_ROW_PATTERN = + /\(\s*\d+,\s*'([^']+)',\s*'[^']+',\s*'[^']+',\s*'[^']+',\s*'(\[[^']*\])',\s*'bangdream',\s*1,\s*(\d+)\s*\)/g; + +const pluginRoot = join( + process.cwd(), + 'src/modules/qqbot/plugins/bangdream', +); + +const manifest = parseQqbotPluginManifest( + JSON.parse(readFileSync(join(pluginRoot, 'plugin.json'), 'utf8')) as Record< + string, + unknown + >, + { pluginRoot }, +); + +/** + * 从完整初始化 SQL 中提取 BangDream 在线命令行。 + * + * @param sql - qqbot 初始化 SQL。 + */ +function getBangDreamFullSqlCommandRows(sql: string) { + return Array.from(sql.matchAll(BANGDREAM_FULL_SQL_ROW_PATTERN)).map( + (match): BangDreamSqlCommandRow => ({ + aliases: JSON.parse(match[1]) as string[], + cooldownMs: Number(match[3]), + operationKey: match[2], + remark: match[4], + }), + ); +} + +function getBangDreamRefactorV3SqlCommandRows(sql: string) { + return Array.from(sql.matchAll(BANGDREAM_REFACTOR_V3_SQL_ROW_PATTERN)).map( + (match): BangDreamSqlCommandRow => ({ + aliases: JSON.parse(match[2]) as string[], + cooldownMs: Number(match[3]) * 1000, + operationKey: match[1], + }), + ); +} + +function expectSqlRowsMatchManifest( + sqlRows: BangDreamSqlCommandRow[], + options: { + requireRemark?: boolean; + } = {}, +) { + const sqlOperationKeys = sqlRows.map((row) => row.operationKey); + const definedOperationKeys = manifest.operations.map( + (operation) => operation.key, + ); + + expect([...sqlOperationKeys].sort()).toEqual( + [...definedOperationKeys].sort(), + ); + expect(sqlOperationKeys).toHaveLength(15); + + const rowsByKey = new Map( + sqlRows.map((row) => [row.operationKey, row] as const), + ); + + for (const operation of manifest.operations) { + const row = rowsByKey.get(operation.key); + + expect(row).toEqual( + expect.objectContaining({ + aliases: [...operation.aliases], + }), + ); + expect(row?.cooldownMs).toBeGreaterThan(0); + if (options.requireRemark) { + expect(row?.remark).not.toHaveLength(0); + } + } +} + +describe('qqbot BangDream command init SQL', () => { + const sql = readFileSync(join(process.cwd(), 'sql/qqbot-init.sql'), 'utf8'); + const sqlRows = getBangDreamFullSqlCommandRows(sql); + + it('keeps every BangDream manifest operation available as an online command', () => { + expectSqlRowsMatchManifest(sqlRows, { requireRemark: true }); + }); + + it('overwrites dirty online command metadata on duplicate ids', () => { + expect(sql).toContain('`code` = VALUES(`code`)'); + expect(sql).toContain('`is_deleted` = 0'); + }); +}); + +describe('refactor v3 BangDream command seed SQL', () => { + const sql = readFileSync( + join(process.cwd(), 'sql/refactor-v3/01-seed-core.sql'), + 'utf8', + ); + const sqlRows = getBangDreamRefactorV3SqlCommandRows(sql); + + it('keeps fresh-schema BangDream seed aligned with the manifest', () => { + expectSqlRowsMatchManifest(sqlRows); + }); + + it('reactivates dirty command rows on duplicate keys', () => { + expect(sql).toContain('is_deleted = 0'); + }); +}); diff --git a/test/qqbot/plugins/bangdream/manifest/operation-manifest.spec.ts b/test/qqbot/plugins/bangdream/manifest/operation-manifest.spec.ts new file mode 100644 index 0000000..f4d5c4c --- /dev/null +++ b/test/qqbot/plugins/bangdream/manifest/operation-manifest.spec.ts @@ -0,0 +1,55 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { parseQqbotPluginManifest } from '@/modules/qqbot/plugin-platform/domain/manifest'; + +const pluginRoot = join( + process.cwd(), + 'src/modules/qqbot/plugins/bangdream', +); + +const readManifest = () => + parseQqbotPluginManifest( + JSON.parse(readFileSync(join(pluginRoot, 'plugin.json'), 'utf8')) as Record< + string, + unknown + >, + { pluginRoot }, + ); + +describe('BangDream operation manifest', () => { + it('keeps operation keys unique and complete in plugin.json', () => { + const manifest = readManifest(); + const operationKeys = manifest.operations.map((operation) => operation.key); + + expect(new Set(operationKeys).size).toBe(operationKeys.length); + expect(operationKeys).toHaveLength(15); + }); + + it('keeps every operation bound to handler metadata', () => { + const manifest = readManifest(); + + for (const operation of manifest.operations) { + expect(operation.handlerName).toEqual(expect.any(String)); + expect(operation.name).not.toHaveLength(0); + expect(operation.description).not.toHaveLength(0); + expect(operation.aliases.length).toBeGreaterThan(0); + expect(operation.permissions.length).toBeGreaterThan(0); + expect(operation.timeoutMs).toBeGreaterThan(0); + } + }); + + it('finds operations by key', () => { + const manifest = readManifest(); + const byKey = new Map( + manifest.operations.map((operation) => [operation.key, operation]), + ); + + expect( + byKey.get('bangdream.song.search'), + ).toMatchObject({ + handlerName: 'searchSong', + name: '查曲', + }); + expect(byKey.get('bangdream.unknown')).toBeUndefined(); + }); +}); diff --git a/test/qqbot/plugins/bangDream/player/deck-rank.repository.spec.ts b/test/qqbot/plugins/bangdream/player/deck-rank.repository.spec.ts similarity index 76% rename from test/qqbot/plugins/bangDream/player/deck-rank.repository.spec.ts rename to test/qqbot/plugins/bangdream/player/deck-rank.repository.spec.ts index 7b887ce..d7c1693 100644 --- a/test/qqbot/plugins/bangDream/player/deck-rank.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/player/deck-rank.repository.spec.ts @@ -1,9 +1,10 @@ -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { mkdtempSync, promises as fs, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import * as path from 'node:path'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { DeckRankResourceRepository } from '@/modules/qqbot/plugins/bangDream/player/deck-rank.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { DeckRankResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/player/deck-rank.repository'; +import { configureBangDreamRuntimeIo } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io'; function createProviderMock(): jest.Mocked { return { @@ -16,6 +17,12 @@ function createProviderMock(): jest.Mocked { } describe('BangDream deck rank resource repository', () => { + afterEach(() => { + configureBangDreamRuntimeIo({ + readAssetFile: undefined, + }); + }); + it('builds local and remote rank image paths', () => { const repository = new DeckRankResourceRepository( createProviderMock(), @@ -36,6 +43,9 @@ describe('BangDream deck rank resource repository', () => { const localImagePath = path.join(localRootPath, 'rank_1.png'); const localBuffer = Buffer.from('local-rank'); writeFileSync(localImagePath, localBuffer); + configureBangDreamRuntimeIo({ + readAssetFile: async (filePath) => await fs.readFile(filePath), + }); const repository = new DeckRankResourceRepository(provider, localRootPath); try { @@ -52,6 +62,11 @@ describe('BangDream deck rank resource repository', () => { const provider = createProviderMock(); const remoteBuffer = Buffer.from('remote-rank'); provider.getAsset.mockResolvedValue(remoteBuffer); + configureBangDreamRuntimeIo({ + readAssetFile: async () => { + throw new Error('local asset missing'); + }, + }); const repository = new DeckRankResourceRepository( provider, path.join(tmpdir(), 'missing-rank-root'), diff --git a/test/qqbot/plugins/bangDream/player/player-band-detail.layout.spec.ts b/test/qqbot/plugins/bangdream/player/player-band-detail.layout.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/player/player-band-detail.layout.spec.ts rename to test/qqbot/plugins/bangdream/player/player-band-detail.layout.spec.ts index 3ce52df..3426819 100644 --- a/test/qqbot/plugins/bangDream/player/player-band-detail.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-band-detail.layout.spec.ts @@ -8,7 +8,7 @@ import { createDeckRankImageLayout, createDeckRankLevelImageSpec, normalizeDeckRankLevelSpriteRankId, -} from '@/modules/qqbot/plugins/bangDream/player/player-band-detail.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-band-detail.layout'; describe('BangDream band detail list spec', () => { it('keeps band detail item constants stable', () => { diff --git a/test/qqbot/plugins/bangDream/player/player-card-icon.layout.spec.ts b/test/qqbot/plugins/bangdream/player/player-card-icon.layout.spec.ts similarity index 91% rename from test/qqbot/plugins/bangDream/player/player-card-icon.layout.spec.ts rename to test/qqbot/plugins/bangdream/player/player-card-icon.layout.spec.ts index 736eada..b921472 100644 --- a/test/qqbot/plugins/bangDream/player/player-card-icon.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-card-icon.layout.spec.ts @@ -3,7 +3,7 @@ import { getPlayerCardIconListSpacing, getPlayerCardIconListTextSize, sortPlayerMainDeckEntries, -} from '@/modules/qqbot/plugins/bangDream/player/player-card-icon.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-card-icon.layout'; describe('BangDream player card icon list spec', () => { it('keeps player card list text and spacing ratios stable', () => { diff --git a/test/qqbot/plugins/bangDream/player/player-character-detail.layout.spec.ts b/test/qqbot/plugins/bangdream/player/player-character-detail.layout.spec.ts similarity index 93% rename from test/qqbot/plugins/bangDream/player/player-character-detail.layout.spec.ts rename to test/qqbot/plugins/bangdream/player/player-character-detail.layout.spec.ts index b21442f..e2706d5 100644 --- a/test/qqbot/plugins/bangDream/player/player-character-detail.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-character-detail.layout.spec.ts @@ -4,7 +4,7 @@ import { createCharacterDetailItemLayout, createCharacterDetailListFrameSpec, createCharacterDetailTextSpec, -} from '@/modules/qqbot/plugins/bangDream/player/player-character-detail.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-character-detail.layout'; describe('BangDream character detail list spec', () => { it('keeps character detail item constants stable', () => { diff --git a/test/qqbot/plugins/bangDream/player/player-data.repository.spec.ts b/test/qqbot/plugins/bangdream/player/player-data.repository.spec.ts similarity index 90% rename from test/qqbot/plugins/bangDream/player/player-data.repository.spec.ts rename to test/qqbot/plugins/bangdream/player/player-data.repository.spec.ts index df35927..5942a40 100644 --- a/test/qqbot/plugins/bangDream/player/player-data.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-data.repository.spec.ts @@ -1,6 +1,6 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { PlayerDataRepository } from '@/modules/qqbot/plugins/bangDream/player/player-data.repository'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { PlayerDataRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-data.repository'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/player/player-difficulty-detail.layout.spec.ts b/test/qqbot/plugins/bangdream/player/player-difficulty-detail.layout.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/player/player-difficulty-detail.layout.spec.ts rename to test/qqbot/plugins/bangdream/player/player-difficulty-detail.layout.spec.ts index b67c110..3bb3213 100644 --- a/test/qqbot/plugins/bangDream/player/player-difficulty-detail.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-difficulty-detail.layout.spec.ts @@ -4,7 +4,7 @@ import { createDifficultyDetailItemLayout, createDifficultyDetailListFrameSpec, createDifficultyDetailTextSpec, -} from '@/modules/qqbot/plugins/bangDream/player/player-difficulty-detail.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-difficulty-detail.layout'; describe('BangDream difficulty detail list spec', () => { it('keeps difficulty detail item constants stable', () => { diff --git a/test/qqbot/plugins/bangDream/player/player-ranking.layout.spec.ts b/test/qqbot/plugins/bangdream/player/player-ranking.layout.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/player/player-ranking.layout.spec.ts rename to test/qqbot/plugins/bangdream/player/player-ranking.layout.spec.ts index c169526..167d664 100644 --- a/test/qqbot/plugins/bangDream/player/player-ranking.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-ranking.layout.spec.ts @@ -3,7 +3,7 @@ import { createRankingDegreeLayout, isMedalRanking, stripPlayerRankingTextTags, -} from '@/modules/qqbot/plugins/bangDream/player/player-ranking.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.layout'; describe('BangDream player ranking spec', () => { it('keeps the historical ranking row dimensions stable', () => { diff --git a/test/qqbot/plugins/bangDream/player/player-ranking.repository.spec.ts b/test/qqbot/plugins/bangdream/player/player-ranking.repository.spec.ts similarity index 83% rename from test/qqbot/plugins/bangDream/player/player-ranking.repository.spec.ts rename to test/qqbot/plugins/bangdream/player/player-ranking.repository.spec.ts index 339e3b5..ad9a19b 100644 --- a/test/qqbot/plugins/bangDream/player/player-ranking.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/player/player-ranking.repository.spec.ts @@ -1,6 +1,6 @@ -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; -import { PlayerRankingResourceRepository } from '@/modules/qqbot/plugins/bangDream/player/player-ranking.repository'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; +import { PlayerRankingResourceRepository } from '@/modules/qqbot/plugins/bangdream/src/domain/player/player-ranking.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/policy/policy.spec.ts b/test/qqbot/plugins/bangdream/policy/policy.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/policy/policy.spec.ts rename to test/qqbot/plugins/bangdream/policy/policy.spec.ts index 6f64de6..b182fda 100644 --- a/test/qqbot/plugins/bangDream/policy/policy.spec.ts +++ b/test/qqbot/plugins/bangdream/policy/policy.spec.ts @@ -1,4 +1,4 @@ -jest.mock('@/modules/qqbot/plugins/bangDream/shared/main-data-store', () => ({ +jest.mock('@/modules/qqbot/plugins/bangdream/src/application/main-data-store', () => ({ __esModule: true, default: {}, })); @@ -7,7 +7,7 @@ import { calculateCnEventEstimateStartAt, getBangDreamOccupiedDays, type BangDreamEventSchedule, -} from '@/modules/qqbot/plugins/bangDream/policy/cn-event-estimate.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cn-event-estimate.policy'; import { getCutoffDateByServerTimezone, getCutoffDayIndex, @@ -17,11 +17,11 @@ import { isCutoffDailyCheckpoint, isCutoffTierSupported, selectRecentCutoffEventIds, -} from '@/modules/qqbot/plugins/bangDream/policy/cutoff.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/cutoff.policy'; import { getBangDreamServerUtcOffset, normalizeBangDreamTimestamp, -} from '@/modules/qqbot/plugins/bangDream/policy/server.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/server.policy'; import { applyGachaGuaranteedRarity, BANGDREAM_GACHA_MAX_SPIN_COUNT, @@ -31,12 +31,12 @@ import { isPermanentJapaneseGachaPeriod, pickGachaCardIdByWeight, pickGachaRarityByRate, -} from '@/modules/qqbot/plugins/bangDream/policy/gacha.policy'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy'; import { BangDreamEventStatus, BangDreamGachaType, BangDreamServerId as Server, -} from '@/modules/qqbot/plugins/bangDream/shared/bangdream-protocol'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream-protocol'; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/test/qqbot/plugins/bangDream/provider/cache-path.spec.ts b/test/qqbot/plugins/bangdream/provider/cache-path.spec.ts similarity index 91% rename from test/qqbot/plugins/bangDream/provider/cache-path.spec.ts rename to test/qqbot/plugins/bangdream/provider/cache-path.spec.ts index 90fc30d..c63a3e8 100644 --- a/test/qqbot/plugins/bangDream/provider/cache-path.spec.ts +++ b/test/qqbot/plugins/bangdream/provider/cache-path.spec.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import { getCacheDirectory, getFileNameFromUrl, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-path'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-path'; describe('BangDream cache path', () => { it('resolves relative asset paths before creating cache directories', () => { diff --git a/test/qqbot/plugins/bangDream/provider/cache-policy.spec.ts b/test/qqbot/plugins/bangdream/provider/cache-policy.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/provider/cache-policy.spec.ts rename to test/qqbot/plugins/bangdream/provider/cache-policy.spec.ts index e096fb7..c7dd96a 100644 --- a/test/qqbot/plugins/bangDream/provider/cache-policy.spec.ts +++ b/test/qqbot/plugins/bangdream/provider/cache-policy.spec.ts @@ -4,7 +4,7 @@ import { isCacheClientNotFound, normalizeCacheClientRetryCount, runWithCacheClientRetry, -} from '@/modules/qqbot/plugins/bangDream/provider/cache-policy'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/cache-policy'; describe('BangDream cache client policy', () => { it('normalizes retry count and reads http status safely', () => { diff --git a/test/qqbot/plugins/bangDream/provider/data-provider.spec.ts b/test/qqbot/plugins/bangdream/provider/data-provider.spec.ts similarity index 90% rename from test/qqbot/plugins/bangDream/provider/data-provider.spec.ts rename to test/qqbot/plugins/bangdream/provider/data-provider.spec.ts index bf9ff62..3ca0d7e 100644 --- a/test/qqbot/plugins/bangDream/provider/data-provider.spec.ts +++ b/test/qqbot/plugins/bangdream/provider/data-provider.spec.ts @@ -1,16 +1,16 @@ import { createBestdoriProvider, type BangDreamBestdoriProviderOptions, -} from '@/modules/qqbot/plugins/bangDream/provider/bestdori.provider'; -import { createHhwxTrackerProvider } from '@/modules/qqbot/plugins/bangDream/provider/hhwx-tracker.provider'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bestdori.provider'; +import { createHhwxTrackerProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/hhwx-tracker.provider'; import { resolveBangDreamProviderUrl, type BangDreamDataProvider, -} from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; import { withCache, withRetry, -} from '@/modules/qqbot/plugins/bangDream/provider/provider-decorators'; +} from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/provider-decorators'; describe('BangDream data provider', () => { it('resolves relative paths against provider base url', () => { diff --git a/test/qqbot/plugins/bangDream/provider/file-cache.client.spec.ts b/test/qqbot/plugins/bangdream/provider/file-cache.client.spec.ts similarity index 52% rename from test/qqbot/plugins/bangDream/provider/file-cache.client.spec.ts rename to test/qqbot/plugins/bangdream/provider/file-cache.client.spec.ts index 7c0e4e9..ae75447 100644 --- a/test/qqbot/plugins/bangDream/provider/file-cache.client.spec.ts +++ b/test/qqbot/plugins/bangdream/provider/file-cache.client.spec.ts @@ -6,18 +6,17 @@ describe('BangDream file cache client', () => { it('does not poison png url cache after transient network failures', async () => { const imageBuffer = Buffer.from('png-data'); - const get = jest + const requestArrayBuffer = jest .fn() .mockRejectedValueOnce(new Error('timeout of 8000ms exceeded')) - .mockResolvedValueOnce({ data: imageBuffer, headers: {} }); - - jest.doMock('axios', () => ({ - __esModule: true, - default: { get }, - })); + .mockResolvedValueOnce({ body: imageBuffer, headers: {} }); + const { configureBangDreamRuntimeIo } = await import( + '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io' + ); + configureBangDreamRuntimeIo({ requestArrayBuffer }); const { download } = - await import('@/modules/qqbot/plugins/bangDream/provider/file-cache.client'); + await import('@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client'); await expect(download('https://example.com/card.png')).rejects.toThrow( 'timeout of 8000ms exceeded', @@ -25,22 +24,21 @@ describe('BangDream file cache client', () => { await expect(download('https://example.com/card.png')).resolves.toEqual( imageBuffer, ); - expect(get).toHaveBeenCalledTimes(2); + expect(requestArrayBuffer).toHaveBeenCalledTimes(2); }); it('keeps missing png urls in the error cache', async () => { const notFoundError = Object.assign(new Error('not found'), { response: { status: 404 }, }); - const get = jest.fn().mockRejectedValueOnce(notFoundError); - - jest.doMock('axios', () => ({ - __esModule: true, - default: { get }, - })); + const requestArrayBuffer = jest.fn().mockRejectedValueOnce(notFoundError); + const { configureBangDreamRuntimeIo } = await import( + '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io' + ); + configureBangDreamRuntimeIo({ requestArrayBuffer }); const { download } = - await import('@/modules/qqbot/plugins/bangDream/provider/file-cache.client'); + await import('@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client'); await expect(download('https://example.com/missing.png')).rejects.toThrow( 'not found', @@ -48,22 +46,21 @@ describe('BangDream file cache client', () => { await expect(download('https://example.com/missing.png')).rejects.toThrow( 'errorUrlCache includes url', ); - expect(get).toHaveBeenCalledTimes(1); + expect(requestArrayBuffer).toHaveBeenCalledTimes(1); }); it('keeps missing svg urls in the error cache', async () => { const notFoundError = Object.assign(new Error('not found'), { response: { status: 404 }, }); - const get = jest.fn().mockRejectedValueOnce(notFoundError); - - jest.doMock('axios', () => ({ - __esModule: true, - default: { get }, - })); + const requestArrayBuffer = jest.fn().mockRejectedValueOnce(notFoundError); + const { configureBangDreamRuntimeIo } = await import( + '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/runtime-io' + ); + configureBangDreamRuntimeIo({ requestArrayBuffer }); const { download } = - await import('@/modules/qqbot/plugins/bangDream/provider/file-cache.client'); + await import('@/modules/qqbot/plugins/bangdream/src/infrastructure/storage/file-cache.client'); await expect(download('https://example.com/missing.svg')).rejects.toThrow( 'not found', @@ -71,6 +68,6 @@ describe('BangDream file cache client', () => { await expect(download('https://example.com/missing.svg')).rejects.toThrow( 'errorUrlCache includes url', ); - expect(get).toHaveBeenCalledTimes(1); + expect(requestArrayBuffer).toHaveBeenCalledTimes(1); }); }); diff --git a/test/qqbot/plugins/bangDream/search/fuzzy-search.spec.ts b/test/qqbot/plugins/bangdream/search/fuzzy-search.spec.ts similarity index 85% rename from test/qqbot/plugins/bangDream/search/fuzzy-search.spec.ts rename to test/qqbot/plugins/bangdream/search/fuzzy-search.spec.ts index 44d792a..d08128b 100644 --- a/test/qqbot/plugins/bangDream/search/fuzzy-search.spec.ts +++ b/test/qqbot/plugins/bangdream/search/fuzzy-search.spec.ts @@ -2,15 +2,15 @@ import { checkRelationList, fuzzySearch, match, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search'; import { createDefaultFuzzySearchRuleRegistry, createFuzzySearchKeyword, -} from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search-rule.registry'; -import type { FuzzySearchResult } from '@/modules/qqbot/plugins/bangDream/search/fuzzy-search.types'; -import { createTsuguEntityMatcher } from '@/modules/qqbot/plugins/bangDream/search/entity-list-matcher'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search-rule.registry'; +import type { FuzzySearchResult } from '@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search.types'; +import { createBangDreamEntityMatcher } from '@/modules/qqbot/plugins/bangdream/src/domain/search/entity-list-matcher'; -describe('Tsugu fuzzy search helpers', () => { +describe('BangDream fuzzy search helpers', () => { it('parses number, level, relation and quoted fallback keywords', () => { expect(fuzzySearch('136 lv25 >100 "夏祭り"')).toEqual({ _number: [136], @@ -58,7 +58,7 @@ describe('Tsugu fuzzy search helpers', () => { }); }); -describe('Tsugu entity list matcher', () => { +describe('BangDream entity list matcher', () => { const CN_SERVER = 3; interface TestEntity { @@ -79,7 +79,7 @@ describe('Tsugu entity list matcher', () => { releasedAt: id === 2 ? [null, null, null, null, null] : [1, 1, 1, 1, 1], }); - const matchEntity = createTsuguEntityMatcher({ + const matchEntity = createBangDreamEntityMatcher({ source, createEntity, isReleased: (entity, displayedServerList) => @@ -104,7 +104,7 @@ describe('Tsugu entity list matcher', () => { let dynamicSource: Record = { '1': {}, }; - const matchDynamicEntity = createTsuguEntityMatcher({ + const matchDynamicEntity = createBangDreamEntityMatcher({ source: () => dynamicSource, createEntity, isReleased: (entity, displayedServerList) => diff --git a/test/qqbot/plugins/bangDream/shared/data-block.layout.spec.ts b/test/qqbot/plugins/bangdream/shared/data-block.layout.spec.ts similarity index 95% rename from test/qqbot/plugins/bangDream/shared/data-block.layout.spec.ts rename to test/qqbot/plugins/bangdream/shared/data-block.layout.spec.ts index c2feb94..9eacf20 100644 --- a/test/qqbot/plugins/bangDream/shared/data-block.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/shared/data-block.layout.spec.ts @@ -3,7 +3,7 @@ import { calculateHorizontalDataBlockSize, calculateVerticalDataBlockSize, getDataBlockTitleLineHeight, -} from '@/modules/qqbot/plugins/bangDream/shared/data-block.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/data-block.layout'; describe('BangDream data block spec', () => { it('keeps the historical title and banner specs stable', () => { diff --git a/test/qqbot/plugins/bangDream/shared/detail-block.builder.spec.ts b/test/qqbot/plugins/bangdream/shared/detail-block.builder.spec.ts similarity index 85% rename from test/qqbot/plugins/bangDream/shared/detail-block.builder.spec.ts rename to test/qqbot/plugins/bangdream/shared/detail-block.builder.spec.ts index 27d9391..fe1ec29 100644 --- a/test/qqbot/plugins/bangDream/shared/detail-block.builder.spec.ts +++ b/test/qqbot/plugins/bangdream/shared/detail-block.builder.spec.ts @@ -1,6 +1,6 @@ import { Canvas } from 'skia-canvas'; -import { DetailBlockBuilder } from '@/modules/qqbot/plugins/bangDream/shared/detail-block.builder'; -import { line } from '@/modules/qqbot/plugins/bangDream/shared/list-frame.renderer'; +import { DetailBlockBuilder } from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.builder'; +import { line } from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.renderer'; describe('DetailBlockBuilder', () => { it('keeps section separators centralized', () => { diff --git a/test/qqbot/plugins/bangDream/shared/detail-block.layout.spec.ts b/test/qqbot/plugins/bangdream/shared/detail-block.layout.spec.ts similarity index 93% rename from test/qqbot/plugins/bangDream/shared/detail-block.layout.spec.ts rename to test/qqbot/plugins/bangdream/shared/detail-block.layout.spec.ts index a2a94bd..5d90b9a 100644 --- a/test/qqbot/plugins/bangDream/shared/detail-block.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/shared/detail-block.layout.spec.ts @@ -1,7 +1,7 @@ import { BANGDREAM_DETAIL_BLOCK_SPEC, getRelativeMetaPercent, -} from '@/modules/qqbot/plugins/bangDream/shared/detail-block.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/detail-block.layout'; describe('BangDream detail block spec', () => { it('keeps song detail dimensions stable', () => { diff --git a/test/qqbot/plugins/bangDream/shared/list-entity.layout.spec.ts b/test/qqbot/plugins/bangdream/shared/list-entity.layout.spec.ts similarity index 87% rename from test/qqbot/plugins/bangDream/shared/list-entity.layout.spec.ts rename to test/qqbot/plugins/bangdream/shared/list-entity.layout.spec.ts index a470bf9..b692db1 100644 --- a/test/qqbot/plugins/bangDream/shared/list-entity.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/shared/list-entity.layout.spec.ts @@ -1,7 +1,7 @@ import { BANGDREAM_ENTITY_LIST_SPEC, shouldUseSingleEntityLabel, -} from '@/modules/qqbot/plugins/bangDream/shared/list-entity.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-entity.layout'; describe('BangDream entity list spec', () => { it('keeps the historical multi-value spacing stable', () => { diff --git a/test/qqbot/plugins/bangDream/shared/list-frame.layout.spec.ts b/test/qqbot/plugins/bangdream/shared/list-frame.layout.spec.ts similarity index 96% rename from test/qqbot/plugins/bangDream/shared/list-frame.layout.spec.ts rename to test/qqbot/plugins/bangdream/shared/list-frame.layout.spec.ts index b307f2a..8c983ae 100644 --- a/test/qqbot/plugins/bangDream/shared/list-frame.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/shared/list-frame.layout.spec.ts @@ -9,7 +9,7 @@ import { getListFrameSpacing, getListFrameTextMaxWidth, getMergedListColumnWidth, -} from '@/modules/qqbot/plugins/bangDream/shared/list-frame.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/list-frame.layout'; describe('BangDream list frame spec', () => { it('keeps text sizing and content width stable', () => { diff --git a/test/qqbot/plugins/bangDream/shared/title.layout.spec.ts b/test/qqbot/plugins/bangdream/shared/title.layout.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/shared/title.layout.spec.ts rename to test/qqbot/plugins/bangdream/shared/title.layout.spec.ts index 6234264..db2b248 100644 --- a/test/qqbot/plugins/bangDream/shared/title.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/shared/title.layout.spec.ts @@ -2,8 +2,8 @@ import { BANGDREAM_TITLE_SPEC, createTitleTextDrawOptions, getTitleTextPosition, -} from '@/modules/qqbot/plugins/bangDream/shared/title.layout'; -import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangDream/theme/render-theme'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/title.layout'; +import { BANGDREAM_RENDER_THEME } from '@/modules/qqbot/plugins/bangdream/src/theme/render-theme'; describe('BangDream title spec', () => { it('keeps the historical title background offset stable', () => { diff --git a/test/qqbot/plugins/bangDream/song/song-chart-preview.layout.spec.ts b/test/qqbot/plugins/bangdream/song/song-chart-preview.layout.spec.ts similarity index 97% rename from test/qqbot/plugins/bangDream/song/song-chart-preview.layout.spec.ts rename to test/qqbot/plugins/bangdream/song/song-chart-preview.layout.spec.ts index eade3a5..2e46f7d 100644 --- a/test/qqbot/plugins/bangDream/song/song-chart-preview.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/song/song-chart-preview.layout.spec.ts @@ -7,7 +7,7 @@ import { createSongChartPreviewNotes, isSongChartCountLineNoteType, isSongChartDisplayNoteType, -} from '@/modules/qqbot/plugins/bangDream/song/song-chart-preview.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart-preview.layout'; describe('BangDream song chart preview spec', () => { it('assigns note times from BPM changes', () => { diff --git a/test/qqbot/plugins/bangDream/song/song-difficulty.layout.spec.ts b/test/qqbot/plugins/bangdream/song/song-difficulty.layout.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/song/song-difficulty.layout.spec.ts rename to test/qqbot/plugins/bangdream/song/song-difficulty.layout.spec.ts index a32c382..4120246 100644 --- a/test/qqbot/plugins/bangDream/song/song-difficulty.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/song/song-difficulty.layout.spec.ts @@ -6,7 +6,7 @@ import { getDifficultyLevelTextPosition, getDifficultyListCanvasWidth, getDifficultyListItemX, -} from '@/modules/qqbot/plugins/bangDream/song/song-difficulty.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-difficulty.layout'; describe('BangDream difficulty list spec', () => { it('keeps default difficulty list dimensions stable', () => { diff --git a/test/qqbot/plugins/bangDream/song/song-list.layout.spec.ts b/test/qqbot/plugins/bangdream/song/song-list.layout.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/song/song-list.layout.spec.ts rename to test/qqbot/plugins/bangdream/song/song-list.layout.spec.ts index 66584ac..4589b84 100644 --- a/test/qqbot/plugins/bangDream/song/song-list.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/song/song-list.layout.spec.ts @@ -4,7 +4,7 @@ import { getSongListCanvasHeight, getSongListContentWidth, getSongListFrameLineHeight, -} from '@/modules/qqbot/plugins/bangDream/song/song-list.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-list.layout'; describe('BangDream song list spec', () => { it('keeps song list item dimensions stable', () => { diff --git a/test/qqbot/plugins/bangDream/song/song-resource.repository.spec.ts b/test/qqbot/plugins/bangdream/song/song-resource.repository.spec.ts similarity index 92% rename from test/qqbot/plugins/bangDream/song/song-resource.repository.spec.ts rename to test/qqbot/plugins/bangdream/song/song-resource.repository.spec.ts index 1df6a5a..26b36f1 100644 --- a/test/qqbot/plugins/bangDream/song/song-resource.repository.spec.ts +++ b/test/qqbot/plugins/bangdream/song/song-resource.repository.spec.ts @@ -1,10 +1,10 @@ -import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangDream/theme/canvas-image'; -import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangDream/provider/bangdream-data-provider'; -import { Server } from '@/modules/qqbot/plugins/bangDream/catalog/server.model'; +import { assetErrorImageBuffer } from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-image'; +import type { BangDreamDataProvider } from '@/modules/qqbot/plugins/bangdream/src/infrastructure/integration/bangdream-data-provider'; +import { Server } from '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model'; import { SongResourceRepository, type SongJacketSource, -} from '@/modules/qqbot/plugins/bangDream/song/song-resource.repository'; +} from '@/modules/qqbot/plugins/bangdream/src/domain/song/song-resource.repository'; function createProviderMock(): jest.Mocked { return { diff --git a/test/qqbot/plugins/bangDream/theme/canvas-text.layout.spec.ts b/test/qqbot/plugins/bangdream/theme/canvas-text.layout.spec.ts similarity index 94% rename from test/qqbot/plugins/bangDream/theme/canvas-text.layout.spec.ts rename to test/qqbot/plugins/bangdream/theme/canvas-text.layout.spec.ts index ac9d337..008d9c0 100644 --- a/test/qqbot/plugins/bangDream/theme/canvas-text.layout.spec.ts +++ b/test/qqbot/plugins/bangdream/theme/canvas-text.layout.spec.ts @@ -6,7 +6,7 @@ import { getTextBaselineY, getTextInlineSpacing, getTextLineHeight, -} from '@/modules/qqbot/plugins/bangDream/theme/canvas-text.layout'; +} from '@/modules/qqbot/plugins/bangdream/src/theme/canvas-text.layout'; describe('BangDream text spec', () => { it('keeps text line and spacing ratios stable', () => { diff --git a/test/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts b/test/qqbot/plugins/ff14-market/qqbot-ff14-worlds.spec.ts similarity index 97% rename from test/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts rename to test/qqbot/plugins/ff14-market/qqbot-ff14-worlds.spec.ts index 3f0162a..fde0552 100644 --- a/test/qqbot/plugins/ff14Market/qqbot-ff14-worlds.spec.ts +++ b/test/qqbot/plugins/ff14-market/qqbot-ff14-worlds.spec.ts @@ -2,7 +2,7 @@ import { buildQqbotFf14MarketCatalog, buildQqbotFf14MarketCatalogFromTree, resolveQqbotFf14MarketTarget, -} from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds'; +} from '@/modules/qqbot/plugins/ff14-market/src'; describe('qqbot ff14 market worlds', () => { const catalog = buildQqbotFf14MarketCatalog({ diff --git a/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts b/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts index 87c1fb0..9743a19 100644 --- a/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts +++ b/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts @@ -1,35 +1,4 @@ -jest.mock( - '@/common', - () => ({ - ensureSnowflakeId: jest.fn(), - KtCreateDateColumn: () => () => undefined, - KtDateTime: Date, - KtDateTimeColumn: () => () => undefined, - KtDateTimeField: () => () => undefined, - KtUpdateDateColumn: () => () => undefined, - formatKtDateTime: (value: Date | number | string) => { - const date = value instanceof Date ? value : new Date(value); - return [ - `${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart( - 2, - '0', - )}-${`${date.getDate()}`.padStart(2, '0')}`, - `${`${date.getHours()}`.padStart(2, '0')}:${`${date.getMinutes()}`.padStart( - 2, - '0', - )}:${`${date.getSeconds()}`.padStart(2, '0')}`, - ].join(' '); - }, - setDictDecodeCache: jest.fn(), - transformKtDateTimeFields: (value: unknown) => value, - }), - { virtual: true }, -); - -import { ConfigService } from '@nestjs/config'; -import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; -import { QqbotFflogsClientService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service'; +import { FflogsClient } from '@/modules/qqbot/plugins/fflogs/src/infrastructure/integration/fflogs-client'; describe('QqbotFflogsClientService', () => { const dicts = { @@ -42,22 +11,13 @@ describe('QqbotFflogsClientService', () => { FFLOGS_SERVER_REGION_LABEL: [{ label: '国服', value: 'cn' }], }; - const service = new QqbotFflogsClientService( - { - get: jest.fn(), - } as unknown as ConfigService, - { - getDictByKey: jest.fn( - async (dictKey: keyof typeof dicts) => dicts[dictKey] || [], - ), - getDictItemsByKey: jest.fn( - async (dictKey: keyof typeof dicts) => dicts[dictKey] || [], - ), - } as unknown as DictService, - { - requestJson: jest.fn(), - } as unknown as QqbotPluginHttpClientService, - ); + const service = new FflogsClient({ + getConfig: jest.fn(), + getDictByKey: jest.fn( + async (dictKey: keyof typeof dicts) => dicts[dictKey] || [], + ), + requestJson: jest.fn(), + }); it('uses cn FFLogs API endpoints by default', () => { expect((service as any).graphqlUrl).toBe( diff --git a/test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts b/test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts index 6ccf776..78ded9a 100644 --- a/test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts +++ b/test/qqbot/plugins/repeater/qqbot-repeater.plugin.spec.ts @@ -1,6 +1,5 @@ -import { ToolsService } from '@/common'; -import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; import type { QqbotNormalizedMessage } from '@/modules/qqbot/core/contract/qqbot.types'; +import { createPlugin } from '@/modules/qqbot/plugins/repeater/src'; function createMessage(text: string): QqbotNormalizedMessage { return { @@ -21,16 +20,23 @@ function createService(config: Record) { const sendService = { sendText: jest.fn().mockResolvedValue({ status: 'ok' }), }; - const service = new QqbotRepeaterPluginService( - { - get: jest.fn((key: string) => config[key]), - } as any, - { + const service = createPlugin({ + host: { + bindEventPlugin: jest.fn(), getBoundEventPluginKeys: jest.fn().mockResolvedValue(['repeater']), - } as any, - sendService as any, - new ToolsService(), - ); + getConfig: ((key: string) => config[key] as T), + sendText: sendService.sendText, + unbindEventPlugin: jest.fn(), + warn: jest.fn(), + }, + manifest: { + events: [], + name: '复读机', + pluginKey: 'repeater', + version: '1.0.0', + }, + now: () => Date.now(), + }); return { sendService, service }; } @@ -87,7 +93,6 @@ describe('QqbotRepeaterPluginService risk-control defaults', () => { QQBOT_REPEATER_THRESHOLD: 4, }); sendService.sendText.mockRejectedValueOnce(new Error('send failed')); - jest.spyOn((service as any).logger, 'warn').mockImplementation(); for (let index = 0; index < 4; index += 1) { await service.handleMessage(createMessage('哈'));