├── .clang-format ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── tasks.json ├── CMakeLists.txt ├── CMakeSettings.json ├── LICENSE ├── README.md ├── app.json ├── app_id.txt ├── appveyor.yml ├── cmake ├── Modules │ ├── FindVcpkgIncludeDir.cmake │ ├── FixDebugLibraryLookup.cmake │ ├── FixLinkConflict.cmake │ └── cotire.cmake └── x86-windows-static-custom.cmake ├── scripts ├── build.ps1 ├── clean.ps1 ├── generate.ps1 ├── helpers.ps1 ├── post_build.ps1 ├── prepare.ps1 └── vcpkg.ps1 ├── src ├── build_defs.h ├── dice_bot_module.cpp ├── dice_bot_module.h ├── dice_calculator.cpp ├── dice_calculator.h ├── dice_coc_module.cpp ├── dice_coc_module.h ├── dice_db.cpp ├── dice_db.h ├── dice_dismiss_module.cpp ├── dice_dismiss_module.h ├── dice_dnd_module.cpp ├── dice_dnd_module.h ├── dice_draw_module.cpp ├── dice_draw_module.h ├── dice_echo_module.cpp ├── dice_echo_module.h ├── dice_event.cpp ├── dice_exception.h ├── dice_fmt_module.cpp ├── dice_fmt_module.h ├── dice_help_module.cpp ├── dice_help_module.h ├── dice_insane_module.cpp ├── dice_insane_module.h ├── dice_jrrp_module.cpp ├── dice_jrrp_module.h ├── dice_module.cpp ├── dice_module.h ├── dice_msg.cpp ├── dice_msg.h ├── dice_msg_queue.cpp ├── dice_msg_queue.h ├── dice_name_module.cpp ├── dice_name_module.h ├── dice_nickname_module.cpp ├── dice_nickname_module.h ├── dice_r_module.cpp ├── dice_r_module.h ├── dice_rarc_module.cpp ├── dice_rarc_module.h ├── dice_rf_module.cpp ├── dice_rf_module.h ├── dice_rules_module.cpp ├── dice_rules_module.h ├── dice_set_module.cpp ├── dice_set_module.h ├── dice_setcoc_module.cpp ├── dice_setcoc_module.h ├── dice_st_module.cpp ├── dice_st_module.h ├── dice_utils.cpp ├── dice_utils.h ├── fudge_calculator.cpp └── fudge_calculator.h └── vcpkg-requirements.txt /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | Standard: Cpp11 3 | IndentWidth: 4 4 | ColumnLimit: 120 5 | AccessModifierOffset: -4 6 | NamespaceIndentation: All 7 | IndentCaseLabels: false 8 | BinPackArguments: false 9 | SortUsingDeclarations: false 10 | BreakBeforeBinaryOperators: NonAssignment 11 | SpacesBeforeTrailingComments: 1 12 | DerivePointerAlignment: true 13 | PointerAlignment: Right 14 | AlignTrailingComments: false 15 | SpacesInContainerLiterals: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | .vscode/settings.json 3 | .vscode/c_cpp_properties.json 4 | .vscode/.ropeproject 5 | .vscode/ipch 6 | vcpkg 7 | build 8 | !lib/*.lib 9 | scripts/install.ps1 -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/cqsdk"] 2 | path = src/cqsdk 3 | url = https://github.com/richardchien/coolq-cpp-sdk-core.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach", 6 | "type": "cppvsdbg", 7 | "request": "attach", 8 | "processId": "${command:pickProcess}" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "prepare", 6 | "type": "shell", 7 | "command": "powershell", 8 | "args": [ 9 | "-NoProfile", 10 | "-File", 11 | "'${workspaceFolder}/scripts/prepare.ps1'" 12 | ], 13 | "problemMatcher": [], 14 | "group": "build" 15 | }, 16 | { 17 | "label": "generate debug", 18 | "type": "shell", 19 | "command": "powershell", 20 | "args": [ 21 | "-NoProfile", 22 | "-File", 23 | "'${workspaceFolder}/scripts/generate.ps1'", 24 | "Debug" 25 | ], 26 | "problemMatcher": [], 27 | "group": "build" 28 | }, 29 | { 30 | "label": "generate release", 31 | "type": "shell", 32 | "command": "powershell", 33 | "args": [ 34 | "-NoProfile", 35 | "-File", 36 | "'${workspaceFolder}/scripts/generate.ps1'", 37 | "Release" 38 | ], 39 | "problemMatcher": [], 40 | "group": "build" 41 | }, 42 | { 43 | "label": "build debug", 44 | "type": "shell", 45 | "command": "powershell", 46 | "args": [ 47 | "-NoProfile", 48 | "-File", 49 | "'${workspaceFolder}/scripts/build.ps1'", 50 | "Debug" 51 | ], 52 | "problemMatcher": [ 53 | "$msCompile" 54 | ], 55 | "group": "build", 56 | "dependsOn": [ 57 | "generate debug" 58 | ] 59 | }, 60 | { 61 | "label": "build release", 62 | "type": "shell", 63 | "command": "powershell", 64 | "args": [ 65 | "-NoProfile", 66 | "-File", 67 | "'${workspaceFolder}/scripts/build.ps1'", 68 | "Release" 69 | ], 70 | "problemMatcher": [ 71 | "$msCompile" 72 | ], 73 | "group": "build", 74 | "dependsOn": [ 75 | "generate release" 76 | ] 77 | }, 78 | { 79 | "label": "clean all", 80 | "type": "shell", 81 | "command": "powershell", 82 | "args": [ 83 | "-NoProfile", 84 | "-File", 85 | "'${workspaceFolder}/scripts/clean.ps1'" 86 | ], 87 | "problemMatcher": [ 88 | "$msCompile" 89 | ], 90 | "group": "build" 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | set(CMAKE_GENERATOR_TOOLSET "host=x86") 3 | set(CMAKE_GENERATOR_PLATFORM "Win32") 4 | project(coolq-cpp-sdk) 5 | 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_FLAGS "/utf-8 ${CMAKE_CXX_FLAGS}") # use UTF-8 source files 8 | set(CMAKE_CXX_FLAGS "/MP ${CMAKE_CXX_FLAGS}") # build with object level parallelism 9 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 10 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") 11 | 12 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 13 | 14 | include(cotire) 15 | include(FindVcpkgIncludeDir) 16 | include(FixDebugLibraryLookup) 17 | 18 | include_directories(${VCPKG_INCLUDE_DIR}) 19 | include_directories(src) 20 | 21 | add_compile_definitions(BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE 22 | _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS 23 | _WIN32_WINNT=0x0501 24 | WIN32_LEAN_AND_MEAN 25 | NOMINMAX) 26 | 27 | # read app id from app_id.txt 28 | file(READ "app_id.txt" APP_ID) 29 | string(STRIP "${APP_ID}" APP_ID) 30 | set(APP_ID "\"${APP_ID}\"") 31 | add_compile_definitions(APP_ID=${APP_ID}) 32 | 33 | find_package(unofficial-iconv CONFIG REQUIRED) 34 | find_package(SQLiteCpp CONFIG REQUIRED) 35 | find_package(sqlite3 CONFIG REQUIRED) 36 | find_package(cpprestsdk CONFIG REQUIRED) 37 | find_package(unofficial-brotli CONFIG REQUIRED) 38 | find_package(OpenSSL REQUIRED) 39 | find_package(ZLIB REQUIRED) 40 | find_package(nlohmann_json CONFIG REQUIRED) 41 | 42 | file(GLOB_RECURSE SOURCE_FILES src/*.cpp) 43 | set(LIB_NAME "app") 44 | add_library(${LIB_NAME} SHARED ${SOURCE_FILES}) 45 | 46 | target_link_libraries(${LIB_NAME} PRIVATE ZLIB::ZLIB) 47 | target_link_libraries(${LIB_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto) 48 | target_link_libraries(${LIB_NAME} PRIVATE unofficial::brotli::brotlidec-static unofficial::brotli::brotlienc-static unofficial::brotli::brotlicommon-static) 49 | target_link_libraries(${LIB_NAME} PRIVATE cpprestsdk::cpprest) 50 | target_link_libraries(${LIB_NAME} PRIVATE unofficial::iconv::libiconv unofficial::iconv::libcharset) 51 | target_link_libraries(${LIB_NAME} PRIVATE sqlite3) 52 | target_link_libraries(${LIB_NAME} PRIVATE SQLiteCpp) 53 | target_link_libraries(${LIB_NAME} PRIVATE nlohmann_json nlohmann_json::nlohmann_json) 54 | 55 | cotire(${LIB_NAME}) 56 | 57 | add_custom_command(TARGET ${LIB_NAME} 58 | POST_BUILD 59 | COMMAND 60 | powershell -ExecutionPolicy Bypass -NoProfile -File "${PROJECT_SOURCE_DIR}/scripts/post_build.ps1" ${APP_ID} ${LIB_NAME} "$") -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "environments": [ 3 | { 4 | "VCPKG_ROOT": "${projectDir}\\vcpkg", 5 | "VCPKG_CMAKE": "${env.VCPKG_ROOT}\\scripts\\buildsystems\\vcpkg.cmake", 6 | "VCPKG_TRIPLET": "x86-windows-static-custom" 7 | } 8 | ], 9 | "configurations": [ 10 | { 11 | "name": "Debug", 12 | "generator": "Visual Studio 15 2017", 13 | "configurationType": "Debug", 14 | "inheritEnvironments": [ 15 | "msvc_x86" 16 | ], 17 | "buildRoot": "${projectDir}\\build\\${name}", 18 | "cmakeCommandArgs": "-T v141 -DCMAKE_BUILD_TYPE=${name}", 19 | "buildCommandArgs": "", 20 | "ctestCommandArgs": "", 21 | "variables": [ 22 | { 23 | "name": "CMAKE_TOOLCHAIN_FILE", 24 | "value": "${env.VCPKG_CMAKE}" 25 | }, 26 | { 27 | "name": "VCPKG_TARGET_TRIPLET", 28 | "value": "${env.VCPKG_TRIPLET}" 29 | } 30 | ] 31 | }, 32 | { 33 | "name": "Release", 34 | "generator": "Visual Studio 15 2017", 35 | "configurationType": "Release", 36 | "inheritEnvironments": [ 37 | "msvc_x86" 38 | ], 39 | "buildRoot": "${projectDir}\\build\\${name}", 40 | "cmakeCommandArgs": "-T v141 -DCMAKE_BUILD_TYPE=${name}", 41 | "buildCommandArgs": "", 42 | "ctestCommandArgs": "", 43 | "variables": [ 44 | { 45 | "name": "CMAKE_TOOLCHAIN_FILE", 46 | "value": "${env.VCPKG_CMAKE}" 47 | }, 48 | { 49 | "name": "VCPKG_TARGET_TRIPLET", 50 | "value": "${env.VCPKG_TRIPLET}" 51 | } 52 | ] 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2017 Richard Chien 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dice V3 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/ft7x1jvdam3xb8jc?svg=true)](https://ci.appveyor.com/project/w4123/dice3) 4 | [![CodeFactor](https://www.codefactor.io/repository/github/w4123/dice3/badge/master)](https://www.codefactor.io/repository/github/w4123/dice3/overview/master) 5 | 6 | Dice! V3 是重构后的Dice!新版本 7 | 8 | 实现了部分功能,剩余功能还在编写当中。具体信息可以参考: https://forum.kokona.tech/d/183-dice-version-3/2 9 | 10 | 本应用文档: https://docs.kokona.tech (还没写完) 11 | 12 | ## 下载 13 | 14 | 点击[这里](https://github.com/w4123/Dice3/releases)进入Releases下载最新的预览版,或者点击[这里](https://ci.appveyor.com/project/w4123/dice3)进入CI下载最新的nightly版! 15 | 16 | ## 编译方式 17 | 18 | ### 预备 19 | 20 | 本项目使用 [CMake](https://cmake.org/) 构建,依赖项通过 [vcpkg](https://github.com/Microsoft/vcpkg) 管理。如果你没有使用过这两个工具,请先前往它们的官方网站了解基本用法。 21 | 22 | 在开始使用之前,请确保你已经安装了 Git,且 `PATH` 中存在 `git` 命令。不需要安装 vcpkg,后面的脚本中会自动安装。 23 | 24 | 然后确保安装了 **Visual Studio 2017 或 2019**,并勾选「使用 C++ 的桌面开发」,确保安装了 CMake、**MSVC v141 或 v142**、**Windows 10 SDK** 这三个组件。其中,如果系统中已经安装了 CMake,无需再在 VS Installer 中安装,但需要确保命令已添加进 `PATH`。除此之外,vcpkg 还要求安装 VS 的**英文语言包**。 25 | 26 | ### 下载 27 | 28 | ```ps1 29 | git clone https://github.com/w4123/Dice3 Dice 30 | cd Dice 31 | git submodule init 32 | git submodule update 33 | ``` 34 | 35 | ### 准备构建环境 36 | 37 | ```ps1 38 | powershell -ExecutionPolicy Bypass .\scripts\prepare.ps1 39 | ``` 40 | 41 | [`scripts/prepare.ps1`](scripts/prepare.ps1) 脚本会在当前项目目录的 `vcpkg` 子目录中安装 vcpkg,并安装所需依赖。 42 | 43 | *注意,如果 PowerShell 提示不让运行脚本,需要先使用管理员权限打开 PowerShell,运行 `Set-ExecutionPolicy Unrestricted`,然后再重新运行上面的脚本;如果 vcpkg 安装依赖时出错,比较常见的问题是网络超时,请适当设置 `HTTP_PROXY` 和 `HTTPS_PROXY`。* 44 | 45 | ### 构建项目 46 | 47 | ```ps1 48 | powershell -ExecutionPolicy Bypass .\scripts\generate.ps1 Debug 49 | powershell -ExecutionPolicy Bypass .\scripts\build.ps1 Debug 50 | ``` 51 | 52 | 上面两条命令分别生成 build 目录和构建项目,将 `Debug` 改为 `Release` 可以构建 release 版本。如果安装了 CMake 还没支持的较新版本 VS,需要先手动进入 VS 2017 或 2019 的 Developer Command Prompt,再执行上面的命令。 53 | 54 | 如果你使用 VS Code,可以直接运行 task;如果使用 VS,可以直接选择菜单 CMake - 全部生成。 55 | 56 | ### 安装插件到 酷Q 57 | 58 | 复制 `build/Debug/Debug`(如果是 release 编译则是 `build/Release/Release`)中和你的 App Id 名字相同的文件夹到 酷Q 的 `dev` 目录下,在 酷Q 中重载应用即可(注意需要开启 酷Q 的开发模式)。 59 | 60 | 如果不想每次构建后都手动安装插件,可以添加 `scripts/install.ps1` 文件(使用 UTF-16 LE 编码)如下: 61 | 62 | ```ps1 63 | $coolqRoot = "dir\to\CoolQ" # 注意修改 酷Q 目录 64 | 65 | $appId = $args[0] 66 | $libName = $args[1] 67 | $appOutDir = $args[2] 68 | 69 | $coolqAppDevDir = "$coolqRoot\dev\$appId" 70 | $dllName = "$libName.dll" 71 | $dllPath = "$appOutDir\$dllName" 72 | $jsonName = "$libName.json" 73 | $jsonPath = "$appOutDir\$jsonName" 74 | 75 | Write-Host "正在拷贝插件到 酷Q 应用文件夹……" 76 | 77 | New-Item -Path $coolqAppDevDir -ItemType Directory -ErrorAction SilentlyContinue 78 | Copy-Item -Force $dllPath "$coolqAppDevDir\$dllName" 79 | Copy-Item -Force $jsonPath "$coolqAppDevDir\$jsonName" 80 | 81 | Write-Host "拷贝完成" -ForegroundColor Green 82 | ``` 83 | 84 | 后期生成事件脚本 [`scripts/post_build.ps1`](scripts/post_build.ps1) 发现存在 `scripts/install.ps1` 时会自动运行它,从而将构建出的 DLL 和 JSON 文件安装到 酷Q,此时在 酷Q 悬浮窗快速重启即可。 85 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "ret": 1, 3 | "apiver": 9, 4 | "name": "Dice!", 5 | "version": "3.0.0alpha w20200709", 6 | "version_id": 1002, 7 | "author": "w4123溯洄", 8 | "description": "跑团用掷骰机器人", 9 | "event": [ 10 | { 11 | "id": 1003, 12 | "type": 1003, 13 | "name": "插件启用", 14 | "priority": 30000, 15 | "function": "cq_app_enable" 16 | }, 17 | { 18 | "id": 1004, 19 | "type": 1004, 20 | "name": "插件停用", 21 | "priority": 30000, 22 | "function": "cq_app_disable" 23 | }, 24 | { 25 | "id": 1001, 26 | "type": 1001, 27 | "name": "酷Q启动", 28 | "priority": 30000, 29 | "function": "cq_coolq_start" 30 | }, 31 | { 32 | "id": 1002, 33 | "type": 1002, 34 | "name": "酷Q退出", 35 | "priority": 30000, 36 | "function": "cq_coolq_exit" 37 | }, 38 | { 39 | "id": 1, 40 | "type": 21, 41 | "name": "私聊消息", 42 | "function": "cq_event_private_msg", 43 | "priority": 30000 44 | }, 45 | { 46 | "id": 2, 47 | "type": 2, 48 | "name": "群消息", 49 | "function": "cq_event_group_msg", 50 | "priority": 30000 51 | }, 52 | { 53 | "id": 3, 54 | "type": 4, 55 | "name": "讨论组消息", 56 | "function": "cq_event_discuss_msg", 57 | "priority": 30000 58 | }, 59 | { 60 | "id": 4, 61 | "type": 11, 62 | "name": "群文件上传", 63 | "function": "cq_event_group_upload", 64 | "priority": 30000 65 | }, 66 | { 67 | "id": 5, 68 | "type": 101, 69 | "name": "群管理员变动", 70 | "function": "cq_event_group_admin", 71 | "priority": 30000 72 | }, 73 | { 74 | "id": 6, 75 | "type": 102, 76 | "name": "群成员减少", 77 | "function": "cq_event_group_member_decrease", 78 | "priority": 30000 79 | }, 80 | { 81 | "id": 7, 82 | "type": 103, 83 | "name": "群成员增加", 84 | "function": "cq_event_group_member_increase", 85 | "priority": 30000 86 | }, 87 | { 88 | "id": 10, 89 | "type": 201, 90 | "name": "好友添加", 91 | "function": "cq_event_friend_add", 92 | "priority": 30000 93 | }, 94 | { 95 | "id": 8, 96 | "type": 301, 97 | "name": "加好友请求", 98 | "function": "cq_event_add_friend_request", 99 | "priority": 30000 100 | }, 101 | { 102 | "id": 9, 103 | "type": 302, 104 | "name": "加群请求/邀请", 105 | "function": "cq_event_add_group_request", 106 | "priority": 30000 107 | } 108 | ], 109 | "menu": [ 110 | { 111 | "name": "半重置数据库", 112 | "function": "menu_semi_replace_db" 113 | }, 114 | { 115 | "name": "重置数据库", 116 | "function": "menu_replace_db" 117 | } 118 | ], 119 | "status": [], 120 | "auth": [ 121 | // 20, // getCookies / getCsrfToken 122 | // 30, // getRecord 123 | 101, // sendGroupMsg 124 | 103, // sendDiscussMsg 125 | 106, // sendPrivateMsg 126 | // 110, // sendLikeV2 / sendLike 127 | // 120, // setGroupKick 128 | // 121, // setGroupBan 129 | // 122, // setGroupAdmin 130 | // 123, // setGroupWholeBan 131 | // 124, // setGroupAnonymousBan 132 | // 125, // setGroupAnonymous 133 | // 126, // setGroupCard 134 | // 127, // setGroupLeave 135 | // 128, // setGroupSpecialTitle 136 | 130, // getGroupMemberInfoV2 / getGroupMemberInfo 137 | 131, // getStrangerInfo 138 | 140, // setDiscussLeave 139 | // 150, // setFriendAddRequest 140 | // 151, // setGroupAddRequest 141 | 160, // getGroupMemberList 142 | 161 // getGroupList 143 | // 180 // deleteMsg 144 | ] 145 | } -------------------------------------------------------------------------------- /app_id.txt: -------------------------------------------------------------------------------- 1 | tech.kokona.dice -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | except: 3 | - /dev.*/ # do not build dev branch 4 | 5 | image: 6 | - Visual Studio 2017 7 | - Visual Studio 2019 8 | 9 | platform: 10 | - x86 11 | 12 | configuration: 13 | - Debug 14 | - Release 15 | 16 | environment: 17 | VCPKG_ROOT: C:\tools\vcpkg 18 | 19 | install: 20 | - git submodule init 21 | - git submodule update 22 | - powershell .\scripts\prepare.ps1 23 | 24 | build_script: 25 | - powershell .\scripts\generate.ps1 %CONFIGURATION% 26 | - powershell .\scripts\build.ps1 %CONFIGURATION% 27 | 28 | artifacts: 29 | - path: build\$(configuration)\$(configuration)\app.dll 30 | name: App DLL 31 | 32 | - path: app.json 33 | name: App Json 34 | 35 | -------------------------------------------------------------------------------- /cmake/Modules/FindVcpkgIncludeDir.cmake: -------------------------------------------------------------------------------- 1 | find_path(VCPKG_INCLUDE_DIR iconv.h) 2 | -------------------------------------------------------------------------------- /cmake/Modules/FixDebugLibraryLookup.cmake: -------------------------------------------------------------------------------- 1 | if(CMAKE_BUILD_TYPE MATCHES "^Debug$") 2 | # fix Vcpkg debug library lookup bug, see https://github.com/Microsoft/vcpkg/issues/1626 3 | list(APPEND CMAKE_IGNORE_PATH ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib) 4 | endif() 5 | -------------------------------------------------------------------------------- /cmake/Modules/FixLinkConflict.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCD /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:MSVCRT") 2 | set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCD /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:MSVCRTD") 3 | -------------------------------------------------------------------------------- /cmake/x86-windows-static-custom.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x86) 2 | set(VCPKG_CRT_LINKAGE static) 3 | set(VCPKG_LIBRARY_LINKAGE static) -------------------------------------------------------------------------------- /scripts/build.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\helpers.ps1" 2 | 3 | Set-Location $projectDir 4 | 5 | # 检查必要命令 6 | 7 | $cmake = Find-CMakeCommand 8 | 9 | if (-Not $cmake) 10 | { 11 | Write-Failure "请先安装 CMake" 12 | SafeExit 1 13 | } 14 | 15 | Write-Host "CMake 路径:$cmake" 16 | 17 | # 检查是否生成 18 | 19 | $configType = if ($args[0]) { $args[0] } else { "Debug" } 20 | 21 | if (-Not (Test-Path ".\build\$configType\ALL_BUILD.vcxproj")) 22 | { 23 | Write-Failure "请先运行 `"generate.ps1 $configType`" 来生成 CMake 构建目录" 24 | SafeExit 1 25 | } 26 | 27 | # CMake 构建 28 | 29 | Write-Host "正在使用 CMake 构建项目……" 30 | 31 | & $cmake --build .\build\$configType --config $configType -- "-p:Platform=x86" 32 | 33 | if ($?) 34 | { 35 | Write-Success "CMake 构建成功" 36 | } 37 | else 38 | { 39 | Write-Failure "CMake 构建失败" 40 | SafeExit 1 41 | } 42 | 43 | # 退出 44 | 45 | SafeExit 0 46 | -------------------------------------------------------------------------------- /scripts/clean.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\helpers.ps1" 2 | 3 | Set-Location $projectDir 4 | 5 | if (Test-Path .\build) 6 | { 7 | Remove-Item -Recurse -Force -Path .\build 8 | } 9 | 10 | SafeExit 0 11 | -------------------------------------------------------------------------------- /scripts/generate.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\helpers.ps1" 2 | 3 | Set-Location $projectDir 4 | 5 | # 检查必要命令 6 | 7 | $cmake = Find-CMakeCommand 8 | 9 | if (-Not $cmake) 10 | { 11 | Write-Failure "请先安装 CMake" 12 | SafeExit 1 13 | } 14 | 15 | Write-Host "CMake 路径:$cmake" 16 | 17 | # CMake 生成 18 | 19 | $configType = if ($args[0]) { $args[0] } else { "Debug" } 20 | 21 | New-Item -Path .\build\$configType -ItemType Directory -ErrorAction SilentlyContinue 22 | Set-Location .\build\$configType 23 | 24 | $vcpkgRoot = if ($env:VCPKG_ROOT) { $env:VCPKG_ROOT } else { "$projectDir\vcpkg" } 25 | $vcpkgTriplet = if ($env:VCPKG_TRIPLET) { $env:VCPKG_TRIPLET } else { "x86-windows-static-custom" } 26 | 27 | if (-Not (Test-Path "$vcpkgRoot")) 28 | { 29 | Write-Failure "Vcpkg 根目录不存在,请检查 vcpkg 是否正确安装和配置" 30 | SafeExit 1 31 | } 32 | 33 | Write-Host "正在使用 CMake 生成构建目录……" 34 | 35 | & $cmake ` 36 | -DCMAKE_TOOLCHAIN_FILE="$vcpkgRoot\scripts\buildsystems\vcpkg.cmake" ` 37 | -DVCPKG_TARGET_TRIPLET="$vcpkgTriplet" ` 38 | -DCMAKE_CONFIGURATION_TYPES="$configType" ` 39 | -DCMAKE_BUILD_TYPE="$configType" ` 40 | "$projectDir" 41 | 42 | if ($?) 43 | { 44 | Write-Success "CMake 生成成功" 45 | } 46 | else 47 | { 48 | Write-Failure "CMake 生成失败" 49 | SafeExit 1 50 | } 51 | 52 | # 退出 53 | 54 | SafeExit 0 55 | -------------------------------------------------------------------------------- /scripts/helpers.ps1: -------------------------------------------------------------------------------- 1 | $originDir = Get-Location 2 | $projectDir = Split-Path $PSScriptRoot -Parent 3 | 4 | $vcpkgRoot = if ($env:VCPKG_ROOT) { $env:VCPKG_ROOT } else { "$projectDir\vcpkg" } 5 | $vcpkgCmd = "$vcpkgRoot\vcpkg.exe" 6 | $vcpkgTriplet = if ($env:VCPKG_TRIPLET) { $env:VCPKG_TRIPLET } else { "x86-windows-static-custom" } 7 | 8 | # 辅助函数 9 | 10 | function Write-Success 11 | { 12 | param ($InputObject) 13 | Write-Host "$InputObject" -ForegroundColor Green 14 | } 15 | 16 | function Write-Failure 17 | { 18 | param ($InputObject) 19 | Write-Host "$InputObject" -ForegroundColor Red 20 | } 21 | 22 | function SafeExit 23 | { 24 | param ($ExitCode) 25 | Set-Location $originDir 26 | exit $ExitCode 27 | } 28 | 29 | function Find-CMakeCommand 30 | { 31 | $cmd = Get-Command -Name cmake -ErrorAction SilentlyContinue 32 | if ($cmd) { return $cmd } 33 | 34 | # try CMake built in with Visual Studio 35 | $vsInstances = Get-ChildItem "C:\Program Files (x86)\Microsoft Visual Studio" -Filter 20?? | Sort-Object -Descending 36 | foreach ($vs in $vsInstances) 37 | { 38 | $vsPath = $vs.Fullname 39 | $cmd = Get-ChildItem "$vsPath\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -ErrorAction SilentlyContinue 40 | if ($cmd) { return $cmd } 41 | } 42 | 43 | # try CMake downloaded by vcpkg 44 | $cmd = Get-ChildItem "$projectDir\vcpkg\downloads\tools\cmake-*\*\bin\cmake.exe" | Sort-Object -Descending 45 | if ($cmd) { return $cmd[0] } 46 | } 47 | -------------------------------------------------------------------------------- /scripts/post_build.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\helpers.ps1" 2 | 3 | $appId = $args[0] 4 | $libName = $args[1] 5 | $outDir = $args[2] 6 | 7 | $appOutDir = "$outDir\$appId" 8 | New-Item -Path $appOutDir -ItemType Directory -ErrorAction SilentlyContinue 9 | 10 | $dllName = "$libName.dll" 11 | $dllOutPath = "$appOutDir\$libName.dll" 12 | $jsonName = "$libName.json" 13 | $jsonOutPath = "$appOutDir\$jsonName" 14 | 15 | Copy-Item -Force $outDir\$dllName $dllOutPath 16 | Copy-Item -Force $projectDir\$jsonName $jsonOutPath 17 | 18 | if (Test-Path "$PSScriptRoot\install.ps1") 19 | { 20 | Write-Host "正在运行安装脚本 `"install.ps1 $args`"……" 21 | powershell.exe -ExecutionPolicy Bypass -NoProfile -File "$PSScriptRoot\install.ps1" $appId $libName $appOutDir 22 | } 23 | 24 | SafeExit 0 25 | -------------------------------------------------------------------------------- /scripts/prepare.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\helpers.ps1" 2 | 3 | # 切换目录 4 | 5 | Set-Location $projectDir 6 | Write-Host "当前工程目录:$projectDir" 7 | 8 | # 检查必要命令 9 | 10 | if (-Not (Get-Command -Name git -ErrorAction SilentlyContinue)) 11 | { 12 | Write-Failure "请先安装 Git,并确保 git 命令已添加到 PATH 环境变量" 13 | SafeExit 1 14 | } 15 | 16 | # 检查 vcpkg 17 | 18 | if (-Not (Test-Path $vcpkgCmd)) 19 | { 20 | Write-Host "Vcpkg 未安装,即将安装……" 21 | Remove-Item -Recurse -Force $vcpkgRoot -ErrorAction SilentlyContinue 22 | 23 | Write-Host "正在克隆 vcpkg 仓库……" 24 | git clone https://github.com/Microsoft/vcpkg.git "$vcpkgRoot" 25 | 26 | Write-Host "正在构建 vcpkg……" 27 | & "$vcpkgRoot\bootstrap-vcpkg.bat" 28 | if ($?) 29 | { 30 | Write-Success "Vcpkg 构建成功" 31 | } 32 | else 33 | { 34 | Write-Failure "Vcpkg 构建失败" 35 | SafeExit 1 36 | } 37 | } 38 | else 39 | { 40 | Write-Success "Vcpkg 已安装" 41 | } 42 | 43 | $vcpkgTripletPath = "$vcpkgRoot\triplets\$vcpkgTriplet.cmake" 44 | 45 | if (-Not (Test-Path $vcpkgTripletPath)) 46 | { 47 | Write-Host "正在创建 vcpkg triplet……" 48 | Copy-Item "$projectDir\cmake\$vcpkgTriplet.cmake" $vcpkgTripletPath 49 | } 50 | Write-Success "Vcpkg triplet 已创建" 51 | 52 | # 检查依赖 53 | 54 | Write-Host "正在检查依赖……" 55 | Set-Location $vcpkgRoot 56 | 57 | $vcpkgCommit = Get-Content "$projectDir\vcpkg-commit.txt" -ErrorAction SilentlyContinue 58 | if ($vcpkgCommit) 59 | { 60 | git fetch --depth=1 origin $vcpkgCommit 61 | git checkout $vcpkgCommit -- ports 62 | if ($?) 63 | { 64 | Write-Success "Vcpkg 包版本已固定到 $vcpkgCommit" 65 | } 66 | else 67 | { 68 | Write-Failure "固定 vcpkg 包版本失败" 69 | SafeExit 1 70 | } 71 | } 72 | 73 | & "$vcpkgCmd" remove --outdated 74 | if ($?) { Write-Success "已移除过时的依赖" } 75 | 76 | foreach ($package in Get-Content "$projectDir\vcpkg-requirements.txt") 77 | { 78 | $package = $package.trim() 79 | if ($package) 80 | { 81 | Write-Host "正在安装依赖 $package……" 82 | & "$vcpkgCmd" --vcpkg-root "$vcpkgRoot" --triplet $vcpkgTriplet install $package 83 | if ($?) 84 | { 85 | Write-Success "依赖 $package 已安装" 86 | } 87 | else 88 | { 89 | Write-Failure "依赖 $package 安装失败" 90 | SafeExit 1 91 | } 92 | } 93 | } 94 | 95 | Write-Success "构建环境准备完成" 96 | 97 | # 退出 98 | 99 | SafeExit 0 100 | -------------------------------------------------------------------------------- /scripts/vcpkg.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\helpers.ps1" 2 | 3 | & "$vcpkgCmd" --vcpkg-root "$vcpkgRoot" --triplet $vcpkgTriplet $args 4 | -------------------------------------------------------------------------------- /src/build_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef BUILD_DEFS_H 3 | 4 | #define BUILD_DEFS_H 5 | 6 | 7 | // Example of __DATE__ string: "Jul 27 2012" 8 | // Example of __TIME__ string: "21:06:19" 9 | 10 | #define COMPUTE_BUILD_YEAR \ 11 | ( \ 12 | (__DATE__[ 7] - '0') * 1000 + \ 13 | (__DATE__[ 8] - '0') * 100 + \ 14 | (__DATE__[ 9] - '0') * 10 + \ 15 | (__DATE__[10] - '0') \ 16 | ) 17 | 18 | 19 | #define COMPUTE_BUILD_DAY \ 20 | ( \ 21 | ((__DATE__[4] >= '0') ? (__DATE__[4] - '0') * 10 : 0) + \ 22 | (__DATE__[5] - '0') \ 23 | ) 24 | 25 | 26 | #define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') 27 | #define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F') 28 | #define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') 29 | #define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p') 30 | #define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') 31 | #define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') 32 | #define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') 33 | #define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u') 34 | #define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S') 35 | #define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O') 36 | #define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N') 37 | #define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D') 38 | 39 | 40 | #define COMPUTE_BUILD_MONTH \ 41 | ( \ 42 | (BUILD_MONTH_IS_JAN) ? 1 : \ 43 | (BUILD_MONTH_IS_FEB) ? 2 : \ 44 | (BUILD_MONTH_IS_MAR) ? 3 : \ 45 | (BUILD_MONTH_IS_APR) ? 4 : \ 46 | (BUILD_MONTH_IS_MAY) ? 5 : \ 47 | (BUILD_MONTH_IS_JUN) ? 6 : \ 48 | (BUILD_MONTH_IS_JUL) ? 7 : \ 49 | (BUILD_MONTH_IS_AUG) ? 8 : \ 50 | (BUILD_MONTH_IS_SEP) ? 9 : \ 51 | (BUILD_MONTH_IS_OCT) ? 10 : \ 52 | (BUILD_MONTH_IS_NOV) ? 11 : \ 53 | (BUILD_MONTH_IS_DEC) ? 12 : \ 54 | /* error default */ 99 \ 55 | ) 56 | 57 | #define COMPUTE_BUILD_HOUR ((__TIME__[0] - '0') * 10 + __TIME__[1] - '0') 58 | #define COMPUTE_BUILD_MIN ((__TIME__[3] - '0') * 10 + __TIME__[4] - '0') 59 | #define COMPUTE_BUILD_SEC ((__TIME__[6] - '0') * 10 + __TIME__[7] - '0') 60 | 61 | 62 | #define BUILD_DATE_IS_BAD (__DATE__[0] == '?') 63 | 64 | #define BUILD_YEAR ((BUILD_DATE_IS_BAD) ? 99 : COMPUTE_BUILD_YEAR) 65 | #define BUILD_MONTH ((BUILD_DATE_IS_BAD) ? 99 : COMPUTE_BUILD_MONTH) 66 | #define BUILD_DAY ((BUILD_DATE_IS_BAD) ? 99 : COMPUTE_BUILD_DAY) 67 | 68 | #define BUILD_TIME_IS_BAD (__TIME__[0] == '?') 69 | 70 | #define BUILD_HOUR ((BUILD_TIME_IS_BAD) ? 99 : COMPUTE_BUILD_HOUR) 71 | #define BUILD_MIN ((BUILD_TIME_IS_BAD) ? 99 : COMPUTE_BUILD_MIN) 72 | #define BUILD_SEC ((BUILD_TIME_IS_BAD) ? 99 : COMPUTE_BUILD_SEC) 73 | 74 | 75 | #endif // BUILD_DEFS_H -------------------------------------------------------------------------------- /src/dice_bot_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_bot_module.h" 2 | #include "SQLiteCpp/SQLiteCpp.h" 3 | #include "cqsdk/cqsdk.h" 4 | #include "dice_db.h" 5 | #include "dice_msg.h" 6 | #include "dice_utils.h" 7 | #include "dice_msg_queue.h" 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | bool bot_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*bot.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | if (std::regex_match(ws, re)) return true; 17 | return search_db(e); 18 | } 19 | 20 | bool bot_module::search_db(const cq::event::MessageEvent &e) { 21 | if (e.message_type == cq::message::DISCUSS) { 22 | SQLite::Statement statement(*db::db, 23 | "SELECT bot_on, is_ban FROM group_info WHERE group_id = ? AND type = ?"); 24 | statement.bind(1, *e.target.discuss_id); 25 | statement.bind(2, 1); 26 | if (statement.executeStep()) { 27 | return !statement.getColumn(0).getInt() || statement.getColumn(1).getInt(); 28 | } 29 | return false; 30 | } 31 | if (e.message_type == cq::message::GROUP) { 32 | SQLite::Statement statement(*db::db, 33 | "SELECT bot_on, is_ban FROM group_info WHERE group_id = ? AND type = ?"); 34 | statement.bind(1, *e.target.group_id); 35 | statement.bind(2, 0); 36 | if (statement.executeStep()) { 37 | return !statement.getColumn(0).getInt() || statement.getColumn(1).getInt(); 38 | } 39 | return false; 40 | } 41 | SQLite::Statement statement(*db::db, "SELECT bot_on, is_ban FROM qq_info WHERE qq_id = ?"); 42 | statement.bind(1, *e.target.user_id); 43 | if (statement.executeStep()) { 44 | return !statement.getColumn(0).getInt() || statement.getColumn(1).getInt(); 45 | } 46 | return false; 47 | } 48 | 49 | void bot_module::update_db(const cq::event::MessageEvent &e, bool bot_on) { 50 | if (e.message_type == cq::message::DISCUSS) { 51 | SQLite::Statement statement(*db::db, 52 | "REPLACE INTO group_info(group_id, type, bot_on) VALUES(?, ?, ?)"); 53 | statement.bind(1, *e.target.discuss_id); 54 | statement.bind(2, 1); 55 | statement.bind(3, bot_on); 56 | statement.exec(); 57 | } else if (e.message_type == cq::message::GROUP) { 58 | SQLite::Statement statement(*db::db, 59 | "REPLACE INTO group_info(group_id, type, bot_on) VALUES(?, ?, ?)"); 60 | statement.bind(1, *e.target.group_id); 61 | statement.bind(2, 0); 62 | statement.bind(3, bot_on); 63 | statement.exec(); 64 | } else { 65 | SQLite::Statement statement(*db::db, 66 | "REPLACE INTO qq_info(qq_id, bot_on) VALUES(?, ?)"); 67 | statement.bind(1, *e.target.user_id); 68 | statement.bind(2, bot_on); 69 | statement.exec(); 70 | } 71 | } 72 | 73 | void bot_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 74 | std::wregex re(L"[\\s]*[\\.。.][\\s]*bot[\\s]*(on|off)?[\\s]*(.*)", 75 | std::regex_constants::ECMAScript | std::regex_constants::icase); 76 | std::wsmatch m; 77 | if (std::regex_match(ws, m, re)) { 78 | std::wstring command = m[1]; 79 | std::wstring target = m[2]; 80 | std::wstring self_id = std::to_wstring(cq::api::get_login_user_id()); 81 | if (target.empty() || target == self_id || target == self_id.substr(self_id.length() - 4) 82 | || target == cq::utils::s2ws(cq::api::get_login_nickname())) { 83 | if (command == L"on") { 84 | if (e.message_type == cq::message::GROUP && !utils::is_admin_or_owner(e.target)) { 85 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strPermissionDeniedError")); 86 | return; 87 | } 88 | update_db(e, true); 89 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strEnabled")); 90 | } else if (command == L"off") { 91 | if (e.message_type == cq::message::GROUP && !utils::is_admin_or_owner(e.target)) { 92 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strPermissionDeniedError")); 93 | return; 94 | } 95 | update_db(e, false); 96 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strDisabled")); 97 | } else { 98 | dice::msg_queue::MsgQueue.add(e.target, msg::dice_full_info); 99 | } 100 | } 101 | } 102 | } 103 | } // namespace dice 104 | -------------------------------------------------------------------------------- /src/dice_bot_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class bot_module : public dice_module { 12 | bool search_db(const cq::event::MessageEvent& e); 13 | void update_db(const cq::event::MessageEvent& e, bool bot_on); 14 | public: 15 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 16 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 17 | }; 18 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_calculator.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_calculator.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "dice_exception.h" 8 | 9 | namespace dice { 10 | 11 | std::mt19937 dice_calculator::ran(clock()); 12 | 13 | dice_calculator::dice_calculator(std::wstring dice_expression) 14 | : dice_expression(std::move(dice_expression)) { 15 | main_calculate(); 16 | } 17 | 18 | dice_calculator::dice_calculator(std::wstring dice_expression, int default_dice) 19 | : dice_expression(std::move(dice_expression)), 20 | default_dice(default_dice) { 21 | main_calculate(); 22 | } 23 | 24 | wchar_t dice_calculator::operator_stk_top() { 25 | if (operator_stk.empty()) { 26 | throw exception::dice_expression_invalid_error(); 27 | } 28 | return operator_stk.top(); 29 | } 30 | 31 | void dice_calculator::operator_stk_pop() { 32 | if (operator_stk.empty()) { 33 | throw exception::dice_expression_invalid_error(); 34 | } 35 | operator_stk.pop(); 36 | } 37 | 38 | double dice_calculator::num_stk_top() { 39 | if (num_stk.empty()) { 40 | throw exception::dice_expression_invalid_error(); 41 | } 42 | return num_stk.top(); 43 | } 44 | 45 | void dice_calculator::num_stk_pop() { 46 | if (num_stk.empty()) { 47 | throw exception::dice_expression_invalid_error(); 48 | } 49 | num_stk.pop(); 50 | } 51 | 52 | int dice_calculator::priority(wchar_t c) { 53 | switch (c) { 54 | case L'^': 55 | return 0; 56 | case L'*': 57 | case L'/': 58 | case L'x': 59 | case L'X': 60 | case L'×': 61 | case L'÷': 62 | return 1; 63 | case L'+': 64 | case L'-': 65 | return 2; 66 | case L'(': 67 | return 3; 68 | default: 69 | throw exception::dice_expression_invalid_error(); 70 | } 71 | } 72 | 73 | // 替换标准骰子及惩罚奖励骰(3d6, 4d6k3, p3, b3) 74 | void dice_calculator::scan_and_replace_dice() { 75 | // 用于把简写替换成完整拼写,方便阅读 76 | std::wstring replaced_dice_expression; 77 | 78 | // 匹配标准掷骰 79 | std::wregex d(L"(([0-9]*)d([0-9]*)(k([0-9]*))?)|((b|p)([1-3])?)", 80 | std::regex_constants::ECMAScript | std::regex_constants::icase); 81 | auto words_begin = std::wsregex_iterator(dice_expression.begin(), dice_expression.end(), d); 82 | auto words_end = std::wsregex_iterator(); 83 | 84 | // 骰子个数 骰子面数 上一个匹配到的位置的下一位 85 | int left = 0, right = 0, last = 0, keep = 0; 86 | bool EnableKeep = false; 87 | for (auto i = words_begin; i != words_end; ++i) { 88 | // 两个连续且中间无任何运算符的骰子属于错误表达式 89 | if (last && last == i->position()) { 90 | throw exception::dice_expression_invalid_error(); 91 | } 92 | 93 | // 骰子前后有数字或者点属于错误表达式 94 | if ((i->position() && (std::iswdigit(dice_expression[i->position() - 1]) || dice_expression[i->position() - 1] == L'.')) 95 | || (i->position() + i->length() != dice_expression.length() 96 | && (std::iswdigit(dice_expression[i->position() + i->length()]) || dice_expression[i->position() + i->length()] == L'.'))) { 97 | throw exception::dice_expression_invalid_error(); 98 | } 99 | 100 | left = 1; 101 | right = default_dice; 102 | keep = 1; 103 | EnableKeep = false; 104 | 105 | // 把前面的字符串挪过来 106 | replaced_dice_expression += 107 | std::wstring(dice_expression.begin() + last, dice_expression.begin() + i->position()); 108 | res_display += std::wstring(dice_expression.begin() + last, dice_expression.begin() + i->position()); 109 | res_display2 += std::wstring(dice_expression.begin() + last, dice_expression.begin() + i->position()); 110 | last = i->position() + i->length(); 111 | std::wsmatch match = *i; 112 | 113 | // 保存这个骰子的信息并替换缩写 114 | std::wstring dice(match[0]); 115 | 116 | // 字母转换为大写 117 | for (auto &c : dice) { 118 | if (c == L'd') c = L'D'; 119 | if (c == L'k') c = L'K'; 120 | if (c == L'b') c = L'B'; 121 | if (c == L'p') c = L'P'; 122 | } 123 | 124 | // 奖励惩罚骰 125 | if (match[1].first == match[1].second) { 126 | int DiceCount = 1; 127 | if (match[8].first != match[8].second) { 128 | DiceCount = std::stoi(match[8]); 129 | } 130 | std::uniform_int_distribution gen100(1, 100); 131 | std::uniform_int_distribution gen9(0, 9); 132 | std::uniform_int_distribution gen10(1, 10); 133 | int first_res = gen100(ran); 134 | std::vector bpTempStorage; 135 | 136 | if (first_res % 10) { 137 | for (int k = 0; k != DiceCount; ++k) { 138 | bpTempStorage.push_back(gen9(ran)); 139 | } 140 | } else { 141 | for (int k = 0; k != DiceCount; ++k) { 142 | bpTempStorage.push_back(gen10(ran)); 143 | } 144 | } 145 | 146 | std::wstring di; 147 | if (match[7] == L"b") { 148 | di = std::to_wstring(first_res) + L"[奖励骰:"; 149 | first_res = 150 | std::min((first_res / 10), *std::min_element(bpTempStorage.begin(), bpTempStorage.end())) * 10 151 | + (first_res % 10); 152 | } else { 153 | di = std::to_wstring(first_res) + L"[惩罚骰:"; 154 | first_res = 155 | std::max((first_res / 10), *std::max_element(bpTempStorage.begin(), bpTempStorage.end())) * 10 156 | + (first_res % 10); 157 | } 158 | for (const auto &c : bpTempStorage) { 159 | di += std::to_wstring(c); 160 | di += L" "; 161 | } 162 | di[di.length() - 1] = L']'; 163 | res_display += di; 164 | res_display2 += std::to_wstring(first_res); 165 | if (dice.length() == 1) dice += L"1"; 166 | replaced_dice_expression += dice; 167 | continue; 168 | } 169 | 170 | // 普通骰 171 | if (match[2].first != match[2].second) { 172 | left = std::stoi(match[2]); 173 | } else { 174 | dice = std::to_wstring(left) + dice; 175 | } 176 | if (match[3].first != match[3].second) { 177 | right = std::stoi(match[3]); 178 | } else { 179 | dice.insert(dice.find(L'D') + 1, std::to_wstring(right)); 180 | } 181 | 182 | if (match[4].first != match[4].second) { 183 | EnableKeep = true; 184 | if (match[5].first != match[5].second) { 185 | keep = std::stoi(match[5]); 186 | if (keep <= 0 || keep > left) { 187 | throw exception::dice_expression_invalid_error(); 188 | } 189 | } else { 190 | dice += std::to_wstring(keep); 191 | } 192 | } 193 | 194 | if (left <= 0 || right <= 0) { 195 | throw exception::dice_expression_invalid_error(); 196 | } 197 | 198 | std::wstring di; 199 | int temp_total = 0; 200 | std::uniform_int_distribution gen(1, right); 201 | std::vector DiceResults; 202 | for (int k = 0; k != left; k++) { 203 | int temp_res = gen(ran); 204 | if (!EnableKeep || static_cast(DiceResults.size()) < keep) { 205 | DiceResults.push_back(temp_res); 206 | } else { 207 | auto min_ele = std::min_element(DiceResults.begin(), DiceResults.end()); 208 | if (temp_res > *min_ele) *min_ele = temp_res; 209 | } 210 | } 211 | 212 | for (int k = 0; k != DiceResults.size(); k++) { 213 | temp_total += DiceResults[k]; 214 | di += std::to_wstring(DiceResults[k]); 215 | if (k != DiceResults.size() - 1) di += L"+"; 216 | } 217 | 218 | if (!(DiceResults.size() == 1 || i->length() == dice_expression.length() 219 | || (i->position() != 0 && dice_expression[i->position() - 1] == L'(' 220 | && i->position() + i->length() != dice_expression.length() 221 | && dice_expression[i->position() + i->length()] == L')'))) { 222 | di = L"(" + di + L")"; 223 | } 224 | res_display += di; 225 | res_display2 += std::to_wstring(temp_total); 226 | replaced_dice_expression += dice; 227 | } 228 | res_display += std::wstring(dice_expression.begin() + last, dice_expression.end()); 229 | res_display2 += std::wstring(dice_expression.begin() + last, dice_expression.end()); 230 | replaced_dice_expression += std::wstring(dice_expression.begin() + last, dice_expression.end()); 231 | dice_expression = replaced_dice_expression; 232 | } 233 | 234 | void dice_calculator::main_calculate() { 235 | scan_and_replace_dice(); 236 | expression_analyse(); 237 | } 238 | 239 | bool dice_calculator::is_number(wchar_t c) { return std::wstring(L"0123456789").find(c) != std::wstring::npos; } 240 | 241 | size_t dice_calculator::get_number_size(size_t current_pos) { 242 | size_t temp_res = res_display2.find_first_not_of(L"0123456789.", current_pos); 243 | if (temp_res == std::wstring::npos) { 244 | return res_display2.length() - temp_res; 245 | } 246 | return temp_res - current_pos; 247 | } 248 | 249 | void dice_calculator::expression_analyse() { 250 | for (size_t i = 0; i < res_display2.size();) { 251 | if ((res_display2[i] == L'-' && (i == 0 || res_display2[i - 1] == L'(')) || is_number(res_display2[i])) { 252 | if (res_display2[i] == L'-' && i + 1 < res_display2.size() && res_display2[i + 1] == L'(') { 253 | num_stk.push(-1); 254 | i++; 255 | } else { 256 | double number = stod(res_display2.substr(i)); 257 | num_stk.push(number); 258 | if (res_display2[i] == L'-') i++; 259 | i += get_number_size(i); 260 | } 261 | 262 | if (i < res_display2.size() && res_display2[i] == L'(') { 263 | if (!operator_stk.empty() && priority(L'*') >= priority(operator_stk.top()) 264 | ) pop_stack_and_calculate(); 265 | operator_stk.push(L'*'); 266 | } 267 | } else { 268 | if (operator_stk.empty() || res_display2[i] == L'(') { 269 | operator_stk.push(res_display2[i]); 270 | } else if (res_display2[i] == L')') { 271 | while (operator_stk_top() != L'(') { 272 | pop_stack_and_calculate(); 273 | } 274 | operator_stk.pop(); 275 | if (i + 1 < res_display2.size() && res_display2[i + 1] == L'(') { 276 | if (!operator_stk.empty() && priority(L'*') >= priority(operator_stk.top()) 277 | ) pop_stack_and_calculate(); 278 | operator_stk.push(L'*'); 279 | } 280 | } else { 281 | if (priority(res_display2[i]) >= priority(operator_stk_top())) { 282 | pop_stack_and_calculate(); 283 | } 284 | operator_stk.push(res_display2[i]); 285 | } 286 | ++i; 287 | } 288 | } 289 | while (!operator_stk.empty()) { 290 | pop_stack_and_calculate(); 291 | } 292 | if (num_stk.size() != 1) { 293 | throw exception::dice_expression_invalid_error(); 294 | } 295 | result = num_stk.top(); 296 | } 297 | 298 | void dice_calculator::pop_stack_and_calculate() { 299 | double left = 0, right = 0, res = 0; 300 | wchar_t oper = operator_stk_top(); 301 | right = num_stk_top(); 302 | num_stk.pop(); 303 | left = num_stk_top(); 304 | num_stk.pop(); 305 | operator_stk.pop(); 306 | switch (oper) { 307 | case L'+': 308 | res = left + right; 309 | break; 310 | case L'-': 311 | res = left - right; 312 | break; 313 | case L'*': 314 | case L'x': 315 | case L'X': 316 | case L'×': 317 | res = left * right; 318 | break; 319 | case L'/': 320 | case L'÷': 321 | res = left / right; 322 | break; 323 | case L'^': 324 | res = pow(left, right); 325 | break; 326 | default: 327 | throw exception::dice_expression_invalid_error(); 328 | } 329 | num_stk.push(res); 330 | } 331 | 332 | std::wstring dice_calculator::form_string() { 333 | std::wstring str = dice_expression; 334 | if (dice_expression != res_display) { 335 | str += L"=" + res_display; 336 | } 337 | if (res_display != res_display2) { 338 | str += L"=" + res_display2; 339 | } 340 | std::wstring res = std::to_wstring(result); 341 | res = res.substr(0, std::max(res.find(L'.'), std::min(res.find(L'.') + 3, res.find_last_not_of(L"0.") + 1))); 342 | if (res != res_display2) { 343 | str += L"=" + res; 344 | } 345 | if (str.length() > 160) str = dice_expression + L"=" + res; 346 | return str; 347 | } 348 | } // namespace dice 349 | -------------------------------------------------------------------------------- /src/dice_calculator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace dice { 8 | class dice_calculator { 9 | public: 10 | std::wstring dice_expression; 11 | 12 | // 过程显示1 13 | std::wstring res_display; 14 | 15 | // 过程显示2, 这个字符串也被用于计算表达式 16 | std::wstring res_display2; 17 | double result = 0.0; 18 | 19 | std::wstring form_string(); 20 | dice_calculator(std::wstring dice_expression); 21 | dice_calculator(std::wstring dice_expression, int default_dice); 22 | 23 | static std::mt19937 ran; 24 | 25 | private: 26 | std::stack num_stk; 27 | std::stack operator_stk; 28 | const int default_dice = 100; 29 | wchar_t operator_stk_top(); 30 | void operator_stk_pop(); 31 | double num_stk_top(); 32 | void num_stk_pop(); 33 | int priority(wchar_t c); 34 | void main_calculate(); 35 | void scan_and_replace_dice(); 36 | void expression_analyse(); 37 | void pop_stack_and_calculate(); 38 | bool is_number(wchar_t c); 39 | size_t get_number_size(size_t current_pos); 40 | }; 41 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_coc_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_coc_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_calculator.h" 4 | #include "dice_exception.h" 5 | #include "dice_utils.h" 6 | #include "dice_msg_queue.h" 7 | 8 | namespace cq::event { 9 | struct MessageEvent; 10 | } 11 | 12 | namespace dice { 13 | bool coc_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 14 | std::wregex re(L"[\\s]*[\\.。.][\\s]*coc.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 15 | return std::regex_match(ws, re); 16 | } 17 | 18 | void coc_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 19 | std::wregex re(L"[\\s]*[\\.。.][\\s]*coc(6|7)?(d)?[\\s]*([0-9]*).*", 20 | std::regex_constants::ECMAScript | std::regex_constants::icase); 21 | std::wsmatch m; 22 | if (std::regex_match(ws, m, re)) { 23 | bool UseCOC6 = false; 24 | if (m[1].first != m[1].second && std::wstring(m[1].first, m[1].second) == L"6") { 25 | UseCOC6 = true; 26 | } 27 | std::string CharacterCards; 28 | int GenerateCount = 1; 29 | if (m[2].first == m[2].second) { 30 | if (m[3].first != m[3].second) { 31 | auto GenerateCountStr = std::wstring(m[3].first, m[3].second); 32 | if (GenerateCountStr.length() > 2) { 33 | throw exception::exception(msg::GetGlobalMsg("strGenerateCountError")); 34 | } 35 | GenerateCount = std::stoi(std::wstring(m[3].first, m[3].second)); 36 | if (GenerateCount > 10 || GenerateCount <= 0) { 37 | throw exception::exception(msg::GetGlobalMsg("strGenerateCountError")); 38 | } 39 | } 40 | } 41 | if (UseCOC6) { 42 | const std::string strProperty[] = {"力量", "体质", "体型", "敏捷", "外貌", "智力", "意志", "教育", "资产"}; 43 | const std::wstring strRoll[] = {L"3D6", L"3D6", L"2D6+6", L"3D6", L"3D6", L"2D6+6", L"3D6", L"3D6+3", L"1D10"}; 44 | const bool AddSpace = GenerateCount != 1; 45 | int AllTotal = 0; 46 | while (GenerateCount--) { 47 | for (int i = 0; i != 9; i++) { 48 | CharacterCards += strProperty[i]; 49 | CharacterCards += ":"; 50 | int RollRes = static_cast(dice_calculator(strRoll[i]).result); 51 | AllTotal += RollRes; 52 | CharacterCards += std::to_string(RollRes); 53 | CharacterCards += " "; 54 | if (AddSpace && RollRes < 10) { 55 | CharacterCards += " "; 56 | } 57 | } 58 | CharacterCards += "共计:"; 59 | CharacterCards += std::to_string(AllTotal); 60 | if (GenerateCount) CharacterCards += "\n"; 61 | AllTotal = 0; 62 | } 63 | 64 | } else { 65 | const std::string strProperty[] = { 66 | "力量", "体质", "体型", "敏捷", "外貌", "智力", "意志", "教育", "幸运"}; 67 | const std::wstring strRoll[] = {L"3D6*5", 68 | L"3D6*5", 69 | L"(2D6+6)*5", 70 | L"3D6*5", 71 | L"3D6*5", 72 | L"(2D6+6)*5", 73 | L"3D6*5", 74 | L"(2D6+6)*5", 75 | L"(3D6)*5"}; 76 | int AllTotalWithoutLuck = 0; 77 | int AllTotal = 0; 78 | while (GenerateCount--) { 79 | for (int i = 0; i != 9; i++) { 80 | CharacterCards += strProperty[i]; 81 | CharacterCards += ":"; 82 | int RollRes = static_cast(dice_calculator(strRoll[i]).result); 83 | AllTotal += RollRes; 84 | if (i != 8) AllTotalWithoutLuck += RollRes; 85 | CharacterCards += std::to_string(RollRes); 86 | CharacterCards += " "; 87 | } 88 | CharacterCards += "共计:"; 89 | CharacterCards += std::to_string(AllTotalWithoutLuck); 90 | CharacterCards += "/"; 91 | CharacterCards += std::to_string(AllTotal); 92 | if (GenerateCount) CharacterCards += "\n"; 93 | AllTotal = 0; 94 | AllTotalWithoutLuck = 0; 95 | } 96 | } 97 | if (m[2].first != m[2].second) { 98 | CharacterCards += "\n性别: {%性别}\n年龄: {?7d6+8}\n职业: {%调查员职业}\n{%调查员背景}"; 99 | } 100 | dice::msg_queue::MsgQueue.add( 101 | e.target, 102 | utils::format_string(msg::GetGlobalMsg("strCharacterCard"), 103 | std::map{{"nick", utils::get_nickname(e.target)}, 104 | {"version", UseCOC6 ? "COC6" : "COC7"}, 105 | {"character_cards", CharacterCards}})); 106 | 107 | } 108 | } 109 | } // namespace dice 110 | -------------------------------------------------------------------------------- /src/dice_coc_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class coc_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_db.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_db.h" 2 | #include 3 | #include 4 | #include "SQLiteCpp/SQLiteCpp.h" 5 | #include "cqsdk/cqsdk.h" 6 | #include "dice_exception.h" 7 | #include "dice_msg.h" 8 | 9 | namespace dice::db { 10 | std::unique_ptr db; 11 | 12 | void InitialiseDB() { 13 | SQLite::Transaction tran(*db); 14 | db->exec( 15 | "CREATE TABLE IF NOT EXISTS qq_info (qq_id INTEGER PRIMARY KEY NOT NULL, is_ban INTEGER DEFAULT 0, " 16 | "ban_reason " 17 | "TEXT, ban_time INTEGER, " 18 | "is_admin INTEGER DEFAULT 0, admin_time INTEGER, is_white INTEGER DEFAULT 0, white_time INTEGER, " 19 | "jrrp_value " 20 | "INTEGER, jrrp_date " 21 | "TEXT, bot_on INTEGER DEFAULT 1, card_chosen TEXT DEFAULT \"default\", nick_name TEXT, default_dice " 22 | "INTEGER NOT NULL DEFAULT 100, success_rule INTEGER NOT NULL DEFAULT 0 CHECK (success_rule >= 0 AND success_rule <= 5))"); 23 | db->exec( 24 | "CREATE TABLE IF NOT EXISTS character_cards (qq_id INTEGER NOT NULL, card_name TEXT NOT NULL, property " 25 | "TEXT NOT NULL, value INTEGER NOT NULL, " 26 | "PRIMARY KEY (qq_id, card_name, property))"); 27 | db->exec( 28 | "CREATE TABLE IF NOT EXISTS group_info (group_id INTEGER NOT NULL, type INTEGER NOT NULL, bot_on INTEGER " 29 | "DEFAULT 1, help_on INTEGER DEFAULT 1, jrrp_on INTEGER DEFAULT 1, is_ban INTEGER DEFAULT 0, ban_reason " 30 | "TEXT, ban_time INTEGER, " 31 | "is_white " 32 | "INTEGER DEFAULT 0, white_time INTEGER, default_dice INTEGER NOT NULL DEFAULT 100, success_rule INTEGER NOT NULL DEFAULT 0 CHECK (success_rule >= 0 AND success_rule <= 5), PRIMARY KEY(group_id, type))"); 33 | db->exec( 34 | "CREATE TABLE IF NOT EXISTS group_user_info (qq_id INTEGER NOT NULL, group_id INTEGER NOT NULL, type " 35 | "INTEGER " 36 | "NOT NULL, nick_name TEXT, card_chosen TEXT DEFAULT \"default\", PRIMARY KEY(qq_id, group_id, type))"); 37 | if (db->execAndGet("SELECT count(*) FROM sqlite_master WHERE type = \"table\" AND name = \"deck\"").getInt() == 38 | 0) { 39 | db->exec( 40 | "CREATE TABLE deck(name TEXT NOT NULL, content TEXT NOT NULL, origin TEXT NOT NULL " 41 | "DEFAULT " 42 | "\"public\")"); 43 | db->exec("CREATE INDEX idx_name ON deck(name)"); 44 | db->exec("CREATE INDEX idx_ori ON deck(origin)"); 45 | 46 | for (const auto &msg : msg::default_deck) { 47 | for (const auto &item : msg.second) { 48 | SQLite::Statement st(*db, "INSERT INTO deck(name, content) VALUES(?, ?)"); 49 | st.bind(1, msg.first); 50 | st.bind(2, item); 51 | st.exec(); 52 | } 53 | } 54 | } 55 | tran.commit(); 56 | } 57 | 58 | void SemiReplaceDB() { 59 | SQLite::Transaction tran(*db); 60 | db->exec("DROP TABLE deck"); 61 | db->exec( 62 | "CREATE TABLE deck(name TEXT NOT NULL, content TEXT NOT NULL, origin TEXT NOT NULL " 63 | "DEFAULT " 64 | "\"public\")"); 65 | db->exec("CREATE INDEX idx_name ON deck(name)"); 66 | db->exec("CREATE INDEX idx_ori ON deck(origin)"); 67 | 68 | for (const auto &msg : msg::default_deck) { 69 | for (const auto &item : msg.second) { 70 | SQLite::Statement st(*db, "INSERT OR IGNORE INTO deck(name, content) VALUES(?, ?)"); 71 | st.bind(1, msg.first); 72 | st.bind(2, item); 73 | st.exec(); 74 | } 75 | } 76 | tran.commit(); 77 | } 78 | 79 | void ReplaceDB() { 80 | db = nullptr; 81 | if (!std::filesystem::remove(cq::utils::s2ws(cq::api::get_app_directory() + "DiceConfig_" 82 | + std::to_string(cq::api::get_login_user_id()) + ".db"))) { 83 | cq::logging::debug("Dice! V3", "Unable to locate database"); 84 | } 85 | db = std::make_unique( 86 | cq::api::get_app_directory() + "DiceConfig_" + std::to_string(cq::api::get_login_user_id()) + ".db", 87 | SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE, 88 | 3000); 89 | InitialiseDB(); 90 | } 91 | 92 | } // namespace dice::db 93 | -------------------------------------------------------------------------------- /src/dice_db.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "SQLiteCpp/SQLiteCpp.h" 4 | 5 | namespace dice::db 6 | { 7 | extern std::unique_ptr db; 8 | void InitialiseDB(); 9 | void SemiReplaceDB(); 10 | void ReplaceDB(); 11 | } 12 | -------------------------------------------------------------------------------- /src/dice_dismiss_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_dismiss_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_msg.h" 4 | #include "dice_utils.h" 5 | #include "dice_msg_queue.h" 6 | 7 | namespace cq::event { 8 | struct MessageEvent; 9 | } 10 | 11 | namespace dice { 12 | bool dismiss_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 13 | if (e.message_type == cq::message::PRIVATE) { 14 | return false; 15 | } 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*dismiss.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 17 | return std::regex_match(ws, re); 18 | 19 | } 20 | 21 | void dismiss_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 22 | std::wregex re(L"[\\s]*[\\.。.][\\s]*dismiss[\\s](.*)", 23 | std::regex_constants::ECMAScript | std::regex_constants::icase); 24 | std::wsmatch m; 25 | if (std::regex_match(ws, m, re)) { 26 | std::wstring target = m[1]; 27 | std::wstring self_id = std::to_wstring(cq::api::get_login_user_id()); 28 | if (target.empty() || target == self_id || target == self_id.substr(self_id.length() - 4) 29 | || target == cq::utils::s2ws(cq::api::get_login_nickname())) { 30 | 31 | if (e.message_type == cq::message::GROUP) { 32 | if (!utils::is_admin_or_owner(e.target)) { 33 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strPermissionDeniedError")); 34 | return; 35 | } 36 | cq::api::set_group_leave(*e.target.group_id, false); 37 | 38 | } else { 39 | cq::api::set_discuss_leave(*e.target.discuss_id); 40 | } 41 | } 42 | } 43 | } 44 | } // namespace dice 45 | -------------------------------------------------------------------------------- /src/dice_dismiss_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class dismiss_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_dnd_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_dnd_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_calculator.h" 4 | #include "dice_exception.h" 5 | #include "dice_utils.h" 6 | #include "dice_msg_queue.h" 7 | 8 | namespace cq::event { 9 | struct MessageEvent; 10 | } 11 | 12 | namespace dice { 13 | bool dnd_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 14 | std::wregex re(L"[\\s]*[\\.。.][\\s]*dnd.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 15 | return std::regex_match(ws, re); 16 | } 17 | 18 | void dnd_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 19 | std::wregex re(L"[\\s]*[\\.。.][\\s]*dnd[\\s]*([0-9]*).*", 20 | std::regex_constants::ECMAScript | std::regex_constants::icase); 21 | std::wsmatch m; 22 | if (std::regex_match(ws, m, re)) { 23 | int GenerateCount = 1; 24 | if (m[1].first != m[1].second) { 25 | auto GenerateCountStr = std::wstring(m[1].first, m[1].second); 26 | if (GenerateCountStr.length() > 2) { 27 | throw exception::exception(msg::GetGlobalMsg("strGenerateCountError")); 28 | } 29 | GenerateCount = std::stoi(std::wstring(m[1].first, m[1].second)); 30 | if (GenerateCount > 10 || GenerateCount <= 0) { 31 | throw exception::exception(msg::GetGlobalMsg("strGenerateCountError")); 32 | } 33 | } 34 | 35 | std::string CharacterCards; 36 | 37 | const std::string strProperty[] = {"力量", "体质", "敏捷", "智力", "感知", "魅力"}; 38 | const bool AddSpace = GenerateCount != 1; 39 | int AllTotal = 0; 40 | while (GenerateCount--) { 41 | for (int i = 0; i != 6; i++) { 42 | CharacterCards += strProperty[i]; 43 | CharacterCards += ":"; 44 | int RollRes = static_cast(dice_calculator(L"4D6K3").result); 45 | AllTotal += RollRes; 46 | CharacterCards += std::to_string(RollRes); 47 | CharacterCards += " "; 48 | if (AddSpace && RollRes < 10) { 49 | CharacterCards += " "; 50 | } 51 | } 52 | CharacterCards += "共计:"; 53 | CharacterCards += std::to_string(AllTotal); 54 | if (GenerateCount) CharacterCards += "\n"; 55 | AllTotal = 0; 56 | } 57 | dice::msg_queue::MsgQueue.add( 58 | e.target, 59 | utils::format_string(msg::GetGlobalMsg("strCharacterCard"), 60 | std::map{ 61 | {"nick", utils::get_nickname(e.target)}, 62 | {"version", "DND"}, 63 | {"character_cards", CharacterCards} 64 | })); 65 | } 66 | } 67 | } // namespace dice 68 | -------------------------------------------------------------------------------- /src/dice_dnd_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class dnd_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_draw_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_draw_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_calculator.h" 4 | #include "dice_exception.h" 5 | #include "dice_utils.h" 6 | #include "dice_msg_queue.h" 7 | 8 | namespace cq::event { 9 | struct MessageEvent; 10 | } 11 | 12 | namespace dice { 13 | bool draw_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 14 | std::wregex re(L"[\\s]*[\\.。.][\\s]*draw.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 15 | return std::regex_match(ws, re); 16 | } 17 | 18 | void draw_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 19 | std::wregex re(L"[\\s]*[\\.。.][\\s]*draw[\\s]*(.*?)[\\s]*([0-9]*)", 20 | std::regex_constants::ECMAScript | std::regex_constants::icase); 21 | std::wsmatch m; 22 | if (std::regex_match(ws, m, re)) { 23 | if (m[1].first != m[1].second) { 24 | int count = 1; 25 | if (m[2].first != m[2].second) { 26 | count = std::stoi(m[2]); 27 | } 28 | if (count == 0 || count > 10) { 29 | throw exception::exception(msg::GetGlobalMsg("strCountError")); 30 | } 31 | std::string draw_item = cq::utils::ws2s(m[1]); 32 | if (draw_item.find('|') != std::string::npos) { 33 | draw_item = "{#" + draw_item + +":" + std::to_string(count) + "}"; 34 | } else { 35 | std::string single_item = "{%" + draw_item + "}"; 36 | draw_item = single_item; 37 | for (int i = 1; i != count; i++) { 38 | draw_item += '\n'; 39 | draw_item += single_item; 40 | } 41 | } 42 | dice::msg_queue::MsgQueue.add( 43 | e.target, 44 | utils::format_string(msg::GetGlobalMsg("strDraw"), 45 | {{"nick", utils::get_nickname(e.target)}, {"draw_res", draw_item}})); 46 | } 47 | } 48 | } 49 | } // namespace dice 50 | -------------------------------------------------------------------------------- /src/dice_draw_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class draw_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_echo_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_echo_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_msg_queue.h" 4 | 5 | namespace cq::event { 6 | struct MessageEvent; 7 | } 8 | 9 | namespace dice { 10 | bool echo_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { return true; } 11 | 12 | void echo_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 13 | dice::msg_queue::MsgQueue.add(e.target, e.message); 14 | } 15 | } // namespace dice 16 | -------------------------------------------------------------------------------- /src/dice_echo_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class echo_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_event.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * dice_event.cpp 3 | * 主事件处理 4 | */ 5 | 6 | #include "cqsdk/cqsdk.h" 7 | #include "dice_bot_module.h" 8 | #include "dice_coc_module.h" 9 | #include "dice_db.h" 10 | #include "dice_dismiss_module.h" 11 | #include "dice_dnd_module.h" 12 | #include "dice_help_module.h" 13 | #include "dice_jrrp_module.h" 14 | #include "dice_module.h" 15 | #include "dice_nickname_module.h" 16 | #include "dice_r_module.h" 17 | #include "dice_rules_module.h" 18 | #include "dice_set_module.h" 19 | #include "dice_draw_module.h" 20 | #include "dice_insane_module.h" 21 | #include "dice_name_module.h" 22 | #include "dice_st_module.h" 23 | #include "dice_rarc_module.h" 24 | #include "dice_msg_queue.h" 25 | #include "dice_setcoc_module.h" 26 | #include "dice_rf_module.h" 27 | #include "dice_msg.h" 28 | #include "dice_utils.h" 29 | 30 | CQ_MAIN { 31 | cq::config.convert_unicode_emoji = false; 32 | // 应用启用时调用,进行模块启用 33 | cq::app::on_enable = [] 34 | { 35 | std::string app_dir = cq::api::get_app_directory(); 36 | 37 | // 连接数据库 38 | dice::db::db = std::make_unique( 39 | app_dir + "DiceConfig_" + std::to_string(cq::api::get_login_user_id()) + ".db", 40 | SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE, 41 | 3000); 42 | 43 | dice::db::InitialiseDB(); 44 | // 启动异步消息发送队列 45 | dice::msg_queue::MsgQueue.start(); 46 | try { 47 | // 读取自定义回复 48 | auto res = dice::msg::load_custom_msg(cq::utils::string_to_coolq(app_dir) + "CustomMsg.json"); 49 | if (res.first) { 50 | if (res.second != 0) { 51 | dice::msg_queue::MsgQueue.add(cq::Target(cq::api::get_login_user_id()), dice::utils::format_string(dice::msg::GetGlobalMsg("strCustomMsgInvalid"), 52 | std::map{{"invalid_count", std::to_string(res.second)}})); 53 | } 54 | else { 55 | dice::msg_queue::MsgQueue.add(cq::Target(cq::api::get_login_user_id()), dice::msg::GetGlobalMsg("strCustomMsgSuccess")); 56 | } 57 | } 58 | } catch(const std::exception& ex) { 59 | dice::msg_queue::MsgQueue.add(cq::Target(cq::api::get_login_user_id()), dice::utils::format_string(dice::msg::GetGlobalMsg("strCustomMsgError"), 60 | std::map{{"exception", ex.what()}})); 61 | } 62 | // 机器人退群 63 | static dice::dismiss_module DismissModule; 64 | // 机器人控制 65 | static dice::bot_module BotModule; 66 | // 随机抽取 67 | static dice::draw_module DrawModule; 68 | // 疯狂抽取 69 | static dice::insane_module InsaneModule; 70 | // 随机昵称 71 | static dice::name_module NameModule; 72 | // 昵称设置 73 | static dice::nickname_module NicknameModule; 74 | // 人物卡设置 75 | static dice::st_module StModule; 76 | // 帮助获取 77 | static dice::help_module HelpModule; 78 | // 规则获取 79 | static dice::rules_module RulesModule; 80 | // 设置判定规则 81 | static dice::setcoc_module SetCocModule; 82 | // 设置默认骰 83 | static dice::set_module SetModule; 84 | // 今日人品检定 85 | static dice::jrrp_module JrrpModule; 86 | // COC任务生成 87 | static dice::coc_module CocModule; 88 | // DND人物生成 89 | static dice::dnd_module DndModule; 90 | // 判定掷骰 91 | static dice::rarc_module RarcModule; 92 | // Fudge掷骰 93 | static dice::rf_module RfModule; 94 | // 普通掷骰 95 | static dice::r_module RModule; 96 | }; 97 | 98 | // 主消息处理函数 99 | auto main_func = [](const auto &e) 100 | { 101 | bool contain_at = false, at_me = false; 102 | std::string self_qq_str = std::to_string(cq::api::get_login_user_id()); 103 | for (auto i = e.message.begin(); i != e.message.end(); i++) { 104 | if (i->type == "at") { 105 | contain_at = true; 106 | if (i->data.at("qq") == self_qq_str) { 107 | at_me = true; 108 | break; 109 | } 110 | } 111 | } 112 | if (contain_at && !at_me) return; 113 | 114 | // 转换为宽字符串,用于正则匹配 115 | std::wstring ws = cq::utils::s2ws(e.message.extract_plain_text()); 116 | 117 | std::wregex re(L"[\\s]*[\\.。.][^]*"); 118 | 119 | if (!std::regex_match(ws, re)) return; 120 | 121 | // 遍历已启用的模块,对消息进行匹配 122 | dice::dice_module *process_module = nullptr; 123 | for (auto &m : dice::dice_module::enabled_modules) { 124 | if (m->match(e, ws)) { 125 | process_module = m; 126 | break; 127 | } 128 | } 129 | 130 | // 如果匹配到符合的模块,使用此模块进行处理 131 | if (process_module) { 132 | e.block(); 133 | try { 134 | process_module->process(e, ws); 135 | } catch (const cq::exception::ApiError &ex) { 136 | cq::logging::debug("Dice! V3", ex.what()); 137 | } catch (const std::exception &ex) { 138 | cq::logging::debug("Dice! V3", ex.what()); 139 | dice::msg_queue::MsgQueue.add(e.target, ex.what()); 140 | } 141 | } 142 | }; 143 | 144 | // 将主消息处理函数绑定到三种消息事件上 145 | cq::event::on_private_msg = main_func; 146 | cq::event::on_group_msg = main_func; 147 | cq::event::on_discuss_msg = main_func; 148 | 149 | cq::app::on_disable = [] 150 | { 151 | // 断开与数据库的连接 152 | dice::db::db = nullptr; 153 | //停止异步消息发送队列 154 | dice::msg_queue::MsgQueue.stop(); 155 | }; 156 | cq::app::on_coolq_exit = [] { 157 | // 断开与数据库的连接 158 | dice::db::db = nullptr; 159 | //停止异步消息发送队列 160 | dice::msg_queue::MsgQueue.stop(); 161 | }; 162 | } 163 | 164 | CQ_MENU(menu_semi_replace_db) { 165 | dice::db::SemiReplaceDB(); 166 | MessageBoxW(nullptr, L"操作\"半重置数据库\"已完成", L"Dice!", MB_OK | MB_ICONINFORMATION); 167 | } 168 | 169 | CQ_MENU(menu_replace_db) { 170 | dice::db::ReplaceDB(); 171 | MessageBoxW(nullptr, L"操作\"重置数据库\"已完成", L"Dice!", MB_OK | MB_ICONINFORMATION); 172 | } 173 | -------------------------------------------------------------------------------- /src/dice_exception.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "dice_msg.h" 6 | 7 | namespace dice::exception { 8 | struct exception : public std::exception 9 | { 10 | exception(const char* what) : std::exception(what){}; 11 | exception(const std::string& what) : std::exception(what.c_str()){}; 12 | }; 13 | 14 | struct dice_expression_invalid_error : public exception { 15 | dice_expression_invalid_error() : exception(msg::GetGlobalMsg("strInvalidDiceError")){}; 16 | dice_expression_invalid_error(const char* what) = delete; 17 | dice_expression_invalid_error(const std::string& what) = delete; 18 | }; 19 | struct runtime_error : public std::runtime_error { 20 | runtime_error(const char* what) : std::runtime_error(what){}; 21 | runtime_error(const std::string& what) : std::runtime_error(what.c_str()){}; 22 | }; 23 | 24 | } // namespace dice::exception -------------------------------------------------------------------------------- /src/dice_fmt_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_fmt_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_calculator.h" 4 | #include "dice_utils.h" 5 | #include "dice_msg_queue.h" 6 | 7 | namespace cq::event { 8 | struct MessageEvent; 9 | } 10 | 11 | namespace dice { 12 | bool fmt_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 13 | std::wregex re(L"[\\s]*[\\.。.][\\s]*fmt[^]*", std::regex_constants::ECMAScript | std::regex_constants::icase); 14 | return std::regex_match(ws, re); 15 | } 16 | 17 | void fmt_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 18 | std::wregex re(L"[\\s]*[\\.。.][\\s]*fmt[\\s]*([^]*)", 19 | std::regex_constants::ECMAScript | std::regex_constants::icase); 20 | std::wsmatch m; 21 | if (std::regex_match(ws, m, re)) { 22 | if (m[1].first != m[1].second) { 23 | dice::msg_queue::MsgQueue.add(e.target, 24 | utils::format_string(cq::utils::ws2s(m[1]), 25 | {{"nick", utils::get_nickname(e.target)}})); 26 | } 27 | } 28 | } 29 | } // namespace dice 30 | -------------------------------------------------------------------------------- /src/dice_fmt_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class fmt_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_help_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_help_module.h" 2 | #include 3 | #include 4 | #include "cqsdk/cqsdk.h" 5 | #include "dice_exception.h" 6 | #include "dice_utils.h" 7 | #include "dice_msg_queue.h" 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | bool help_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*help.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | return std::regex_match(ws, re); 17 | } 18 | 19 | void help_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 20 | std::wregex re(L"[\\s]*[\\.。.][\\s]*help[\\s]*(.*)", 21 | std::regex_constants::ECMAScript | std::regex_constants::icase); 22 | std::wsmatch m; 23 | if (std::regex_match(ws, m, re)) { 24 | std::wstring origin_query(m[1]); 25 | std::transform(origin_query.begin(), origin_query.end(), origin_query.begin(), std::towlower); 26 | if (origin_query == L"on") { 27 | utils::set_help_enabled(e.target, true); 28 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strSetHelpEnabled")); 29 | } else if (origin_query == L"off") { 30 | utils::set_help_enabled(e.target, false); 31 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strSetHelpDisabled")); 32 | } else { 33 | if (!utils::is_help_enabled(e.target)) { 34 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strHelpDisabled")); 35 | return; 36 | } 37 | std::string query; 38 | if (origin_query.empty()) { 39 | query = "default"; 40 | } else { 41 | query = cq::utils::ws2s(origin_query); 42 | } 43 | dice::msg_queue::MsgQueue.add(e.target, msg::GetHelpMsg(query)); 44 | } 45 | } 46 | } 47 | } // namespace dice 48 | -------------------------------------------------------------------------------- /src/dice_help_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class help_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_insane_module.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cqsdk/cqsdk.h" 4 | #include "dice_calculator.h" 5 | #include "dice_exception.h" 6 | #include "dice_insane_module.h" 7 | #include "dice_utils.h" 8 | #include "dice_msg_queue.h" 9 | 10 | namespace cq::event { 11 | struct MessageEvent; 12 | } 13 | 14 | namespace dice { 15 | bool insane_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*(?:ti|li).*", std::regex_constants::ECMAScript | std::regex_constants::icase); 17 | return std::regex_match(ws, re); 18 | } 19 | 20 | void insane_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 21 | std::wregex re(L"[\\s]*[\\.。.][\\s]*(ti|li).*", std::regex_constants::ECMAScript | std::regex_constants::icase); 22 | std::wsmatch m; 23 | if (std::regex_match(ws, m, re)) { 24 | std::wstring insane_type(m[1]); 25 | std::transform(insane_type.begin(), insane_type.end(), insane_type.begin(), std::towlower); 26 | std::string insane_str; 27 | if (insane_type == L"ti") { 28 | insane_str = "{%即时症状}"; 29 | } else { 30 | insane_str = "{%总结症状}"; 31 | } 32 | dice::msg_queue::MsgQueue.add( 33 | e.target, 34 | utils::format_string(msg::GetGlobalMsg("strInsane"), 35 | { 36 | {"nick", utils::get_nickname(e.target)}, 37 | {"insane_type", insane_type == L"ti" ? "即时" : "总结"}, 38 | {"insane_str", insane_str} 39 | })); 40 | } 41 | } 42 | } // namespace dice 43 | -------------------------------------------------------------------------------- /src/dice_insane_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class insane_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_jrrp_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_jrrp_module.h" 2 | #include "cpprest/http_client.h" 3 | #include "cqsdk/cqsdk.h" 4 | #include "dice_calculator.h" 5 | #include "dice_exception.h" 6 | #include "dice_utils.h" 7 | #include "dice_msg_queue.h" 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | bool jrrp_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*jrrp.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | return std::regex_match(ws, re); 17 | } 18 | 19 | void jrrp_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 20 | std::wregex re(L"[\\s]*[\\.。.][\\s]*jrrp[\\s]*(.*)", 21 | std::regex_constants::ECMAScript | std::regex_constants::icase); 22 | std::wsmatch m; 23 | if (std::regex_match(ws, m, re)) { 24 | std::wstring command(m[1]); 25 | std::transform(command.begin(), command.end(), command.begin(), towlower); 26 | if (command == L"on") { 27 | utils::set_jrrp_enabled(e.target, true); 28 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strSetJrrpEnabled")); 29 | } else if (command == L"off") { 30 | utils::set_jrrp_enabled(e.target, false); 31 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strSetJrrpDisabled")); 32 | } else { 33 | if (!utils::is_jrrp_enabled(e.target)) { 34 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strJrrpDisabled")); 35 | return; 36 | } 37 | auto res = utils::get_jrrp(e.target); 38 | if (std::get<0>(res)) { 39 | dice::msg_queue::MsgQueue.add(e.target, 40 | utils::format_string(msg::GetGlobalMsg("strJrrp"), 41 | { 42 | {"nick", utils::get_nickname(e.target)}, 43 | {"jrrp_val", std::to_string(std::get<1>(res))} 44 | })); 45 | return; 46 | } 47 | 48 | // 请求Client 49 | web::http::client::http_client http_client(U("http://api.kokona.tech:5555/")); 50 | // POST数据 51 | const utf8string data = "QQ=" + std::to_string(cq::api::get_login_user_id()) + "&v=20190114" 52 | + "&QueryQQ=" + std::to_string(*e.target.user_id); 53 | // 设置请求参数 54 | web::http::http_request req(web::http::methods::POST); 55 | req.set_request_uri(U("/jrrp")); 56 | req.set_body(data, "application/x-www-form-urlencoded;charset=utf-8"); 57 | // 设置UA 58 | req.headers().add(U("User-Agent"), msg::dice_user_agent); 59 | // 发起异步请求,堵塞等待结果 60 | web::http::http_response response = http_client.request(req).get(); 61 | // 获取结果 62 | auto jrrp_val_str = response.extract_utf8string(true).get(); 63 | dice::msg_queue::MsgQueue.add( 64 | e.target, 65 | utils::format_string(msg::GetGlobalMsg("strJrrp"), 66 | {{"nick", utils::get_nickname(e.target)}, {"jrrp_val", jrrp_val_str}})); 67 | auto jrrp_val = std::stoi(jrrp_val_str); 68 | utils::set_jrrp(e.target, jrrp_val); 69 | } 70 | } 71 | } 72 | } // namespace dice 73 | -------------------------------------------------------------------------------- /src/dice_jrrp_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class jrrp_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_module.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * dice_module.cpp 3 | * 模块基础定义 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "dice_module.h" 10 | 11 | namespace dice { 12 | // 构造函数,将模块加入启用模块列表 13 | dice_module::dice_module() { 14 | enabled_modules.push_back(this); 15 | } 16 | 17 | // 析构函数,将模块移除启用模块列表 18 | dice_module::~dice_module() { 19 | const auto find_res = std::find(enabled_modules.begin(), enabled_modules.end(), this); 20 | if (find_res != enabled_modules.end()) { 21 | enabled_modules.erase(find_res); 22 | } 23 | } 24 | 25 | std::vector dice_module::enabled_modules; 26 | } // namespace dice 27 | -------------------------------------------------------------------------------- /src/dice_module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dice_module.h 3 | * 模块基础定义 4 | */ 5 | 6 | #pragma once 7 | #include 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | // 模块基类 15 | class dice_module { 16 | public: 17 | dice_module(); 18 | ~dice_module(); 19 | dice_module(const dice_module& m) = default; 20 | dice_module(dice_module&& m) = default; 21 | dice_module& operator=(const dice_module& m) = default; 22 | dice_module& operator=(dice_module&& m) = default; 23 | 24 | // 启用模块列表 25 | static std::vector enabled_modules; 26 | 27 | // 匹配消息 28 | virtual bool match(const cq::event::MessageEvent& e, const std::wstring& ws) = 0; 29 | 30 | // 处理消息 31 | virtual void process(const cq::event::MessageEvent& e, const std::wstring& ws) = 0; 32 | }; 33 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_msg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "cpprest/asyncrt_utils.h" 6 | 7 | namespace dice::msg { 8 | extern const std::string dice_ver; 9 | extern const short dice_build; 10 | extern const std::string dice_info; 11 | extern const std::string dice_full_info; 12 | extern const utility::string_t dice_user_agent; 13 | extern std::map global_msg; 14 | extern std::map help_msg; 15 | extern const std::map> default_deck; 16 | extern const std::map SkillDefaultVal; 17 | extern const std::map SkillNameReplace; 18 | std::string GetGlobalMsg(const std::string& str); 19 | std::string GetHelpMsg(const std::string& str); 20 | std::pair load_custom_msg(const std::string& filePath); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/dice_msg_queue.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_msg_queue.h" 2 | #include "cqsdk/cqsdk.h" 3 | 4 | namespace dice::msg_queue { 5 | void msg_queue::start() { 6 | if (_activated) return; 7 | _activated = true; 8 | _msg_send_thread = std::thread([&] { 9 | _thread_running = true; 10 | while (_activated) { 11 | std::pair msg; 12 | { 13 | std::lock_guard lk(_mutex_send_thread); 14 | if (!_queue.empty()) { 15 | msg = std::move(_queue.front()); 16 | _queue.pop(); 17 | } 18 | } 19 | if (!msg.second.empty()) { 20 | try { 21 | cq::api::send_msg(msg.first, msg.second); 22 | } catch (const std::exception& e) { 23 | cq::logging::debug("Dice!",e.what()); 24 | } 25 | } else { 26 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 27 | } 28 | } 29 | _thread_running = false; 30 | }); 31 | _msg_send_thread.detach(); 32 | } 33 | void msg_queue::stop() { 34 | if (!_activated) return; 35 | _activated = false; 36 | while (_thread_running) std::this_thread::sleep_for(std::chrono::milliseconds(20)); 37 | } 38 | 39 | msg_queue::~msg_queue() { stop(); } 40 | void msg_queue::add(const cq::Target& target, std::string&& msg) { 41 | std::lock_guard lk(_mutex_send_thread); 42 | _queue.push(std::make_pair(target, std::move(msg))); 43 | } 44 | void msg_queue::add(const cq::Target& target, const std::string& msg) { 45 | std::lock_guard lk(_mutex_send_thread); 46 | _queue.push(std::make_pair(target, msg)); 47 | } 48 | msg_queue MsgQueue; 49 | } // namespace dice::msg_queue 50 | -------------------------------------------------------------------------------- /src/dice_msg_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "cqsdk/cqsdk.h" 7 | namespace dice::msg_queue { 8 | 9 | class msg_queue { 10 | public: 11 | std::atomic _activated = false; 12 | std::atomic _thread_running = false; 13 | std::mutex _mutex_send_thread; 14 | std::thread _msg_send_thread; 15 | std::queue> _queue; 16 | void add(const cq::Target& target, std::string&& msg); 17 | void add(const cq::Target& target, const std::string& msg); 18 | void start(); 19 | void stop(); 20 | msg_queue() = default; 21 | ~msg_queue(); 22 | msg_queue(const msg_queue&) = delete; 23 | msg_queue(msg_queue&&) = delete; 24 | }; 25 | extern msg_queue MsgQueue; 26 | } // namespace dice::msg_queue -------------------------------------------------------------------------------- /src/dice_name_module.cpp: -------------------------------------------------------------------------------- 1 | #include "cqsdk/cqsdk.h" 2 | #include "dice_calculator.h" 3 | #include "dice_exception.h" 4 | #include "dice_name_module.h" 5 | #include "dice_utils.h" 6 | #include "dice_msg_queue.h" 7 | #include 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | bool name_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*name.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | return std::regex_match(ws, re); 17 | } 18 | 19 | void name_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 20 | std::wregex re(L"[\\s]*[\\.。.][\\s]*name[\\s]*([a-z]*)[\\s]*([0-9]*).*", 21 | std::regex_constants::ECMAScript | std::regex_constants::icase); 22 | std::wsmatch m; 23 | 24 | if (std::regex_match(ws, m, re)) { 25 | std::string generate_str = "{%name}"; 26 | if (m[1].first != m[1].second) { 27 | generate_str = "{%name@" + cq::utils::ws2s(m[1]) + "}"; 28 | std::transform(generate_str.begin(), generate_str.end(), generate_str.begin(), [](unsigned char c) { 29 | return std::tolower(c); 30 | }); 31 | } 32 | int generate_count = 1; 33 | if (m[2].first != m[2].second) { 34 | generate_count = std::stoi(m[2]); 35 | if (generate_count <= 0 || generate_count > 10) { 36 | throw exception::exception(msg::GetGlobalMsg("strCountError")); 37 | } 38 | } 39 | std::string str = generate_str; 40 | for (int i = 1; i != generate_count; i++) { 41 | str += " "; 42 | str += generate_str; 43 | } 44 | dice::msg_queue::MsgQueue.add(e.target, 45 | utils::format_string(msg::GetGlobalMsg("strNameGenerate"), 46 | {{"nick", utils::get_nickname(e.target)}, {"generate_str", str}})); 47 | } 48 | } 49 | } // namespace dice 50 | -------------------------------------------------------------------------------- /src/dice_name_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class name_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_nickname_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_nickname_module.h" 2 | #include 3 | #include "cqsdk/cqsdk.h" 4 | #include "dice_calculator.h" 5 | #include "dice_exception.h" 6 | #include "dice_utils.h" 7 | #include "dice_msg_queue.h" 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | bool nickname_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*n.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | return std::regex_match(ws, re); 17 | } 18 | 19 | void nickname_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 20 | std::wregex re(L"[\\s]*[\\.。.][\\s]*n(n)?(n)?[\\s]*(.*)", 21 | std::regex_constants::ECMAScript | std::regex_constants::icase); 22 | std::wsmatch m; 23 | 24 | if (std::regex_match(ws, m, re)) { 25 | std::string old_nick = utils::get_nickname(e.target); 26 | std::string nick_name = cq::utils::ws2s(m[3]); 27 | if (m[2].first != m[2].second) { 28 | if (nick_name.empty()) { 29 | nick_name = utils::format_string("{%name}"); 30 | } else { 31 | std::transform(nick_name.begin(), nick_name.end(), nick_name.begin(), [](unsigned char c) { 32 | return std::tolower(c); 33 | }); 34 | nick_name = utils::format_string("{%name@{language}}", {{"language", nick_name}}); 35 | } 36 | } 37 | if (m[1].first == m[1].second || e.message_type == cq::message::PRIVATE) { 38 | utils::set_global_nickname(e.target, nick_name); 39 | } else { 40 | utils::set_group_nickname(e.target, nick_name); 41 | } 42 | if (nick_name.empty()) { 43 | dice::msg_queue::MsgQueue.add( 44 | e.target, 45 | utils::format_string( 46 | msg::GetGlobalMsg("strNickEmpty"), 47 | {{"old_nick", old_nick}, 48 | {"is_global", 49 | (m[1].first == m[1].second || e.message_type == cq::message::PRIVATE) ? "全局" : ""}})); 50 | } else { 51 | if ((m[1].first == m[1].second && e.message_type == cq::message::PRIVATE 52 | || m[1].first != m[1].second && e.message_type != cq::message::PRIVATE) && 53 | utils::if_card_exist(e.target, nick_name)) { 54 | utils::set_chosen_card(e.target, nick_name); 55 | dice::msg_queue::MsgQueue.add( 56 | e.target, 57 | utils::format_string( 58 | msg::GetGlobalMsg("strNickSetWithCardChange"), 59 | {{"old_nick", old_nick}, 60 | {"new_nick", nick_name}, 61 | {"is_global", 62 | (m[1].first == m[1].second || e.message_type == cq::message::PRIVATE) ? "全局" : ""}, {"origin", utils::get_originname(e.target)}})); 63 | 64 | } else { 65 | dice::msg_queue::MsgQueue.add( 66 | e.target, 67 | utils::format_string( 68 | msg::GetGlobalMsg("strNickSet"), 69 | {{"old_nick", old_nick}, 70 | {"new_nick", nick_name}, 71 | {"is_global", 72 | (m[1].first == m[1].second || e.message_type == cq::message::PRIVATE) ? "全局" : ""}})); 73 | 74 | } 75 | } 76 | } 77 | } 78 | } // namespace dice 79 | -------------------------------------------------------------------------------- /src/dice_nickname_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class nickname_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_r_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_r_module.h" 2 | #include 3 | #include 4 | #include "cqsdk/cqsdk.h" 5 | #include "dice_calculator.h" 6 | #include "dice_exception.h" 7 | #include "dice_utils.h" 8 | #include "dice_msg_queue.h" 9 | 10 | namespace cq::event { 11 | struct MessageEvent; 12 | } 13 | 14 | namespace dice { 15 | bool r_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*r[^]*", std::regex_constants::ECMAScript | std::regex_constants::icase); 17 | return std::regex_match(ws, re); 18 | } 19 | 20 | void r_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 21 | std::wregex re(L"[\\s]*[\\.。.][\\s]*r(h)?[\\s]*(([0-9]+)#)?([0-9dk+\\-*x×÷/\\(\\)\\^bp\\.]*)[\\s]*([^]*)", 22 | std::regex_constants::ECMAScript | std::regex_constants::icase); 23 | std::wsmatch m; 24 | if (std::regex_match(ws, m, re)) { 25 | std::wstring res; 26 | int RollCount = 1; 27 | if (m[2].first != m[2].second) { 28 | RollCount = std::stoi(m[3]); 29 | } 30 | 31 | if (RollCount <= 0 || RollCount > 10) { 32 | throw exception::dice_expression_invalid_error(); 33 | } 34 | 35 | std::wstring dice(m[4]); 36 | std::wstring reason(m[5]); 37 | 38 | // 如果骰子全是数字,那就当成原因 39 | if (dice.find_first_not_of(L"0123456789") == std::wstring::npos) { 40 | reason = dice + reason; 41 | dice = L""; 42 | } 43 | if (dice.empty()) dice = L"d"; 44 | int default_dice = utils::get_defaultdice(e.target); 45 | 46 | res = dice_calculator(dice, default_dice).form_string(); 47 | 48 | for (int i = 1; i != RollCount; i++) { 49 | res += '\n'; 50 | res += dice_calculator(dice, default_dice).form_string(); 51 | } 52 | 53 | if (m[1].first == m[1].second) { 54 | dice::msg_queue::MsgQueue.add(e.target, 55 | utils::format_string( 56 | msg::GetGlobalMsg("strRollDice"), 57 | std::map{ 58 | {"nick", utils::get_nickname(e.target)}, 59 | {"reason", cq::utils::ws2s(reason)}, 60 | {"dice_expression", cq::utils::ws2s(res)} 61 | })); 62 | } else { 63 | dice::msg_queue::MsgQueue.add( 64 | e.target, 65 | utils::format_string(msg::GetGlobalMsg("strHiddenDice"), 66 | std::map{{"nick", utils::get_nickname(e.target)}})); 67 | 68 | dice::msg_queue::MsgQueue.add(cq::Target(*e.target.user_id), 69 | utils::format_string(msg::GetGlobalMsg("strRollHiddenDice"), 70 | std::map{ 71 | {"origin", utils::get_originname(e.target)}, 72 | {"nick", utils::get_nickname(e.target)}, 73 | {"reason", cq::utils::ws2s(reason)}, 74 | {"dice_expression", cq::utils::ws2s(res)} 75 | })); 76 | } 77 | } 78 | } 79 | } // namespace dice 80 | -------------------------------------------------------------------------------- /src/dice_r_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class r_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_rarc_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_rarc_module.h" 2 | #include 3 | #include 4 | #include "cqsdk/cqsdk.h" 5 | #include "dice_calculator.h" 6 | #include "dice_exception.h" 7 | #include "dice_utils.h" 8 | #include "dice_msg_queue.h" 9 | 10 | namespace cq::event { 11 | struct MessageEvent; 12 | } 13 | 14 | namespace dice { 15 | bool rarc_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*r(a|c)[^]*", 17 | std::regex_constants::ECMAScript | std::regex_constants::icase); 18 | return std::regex_match(ws, re); 19 | } 20 | 21 | void rarc_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 22 | std::wregex re( 23 | L"[\\s]*[\\.。.][\\s]*r(a|c)(h)?[\\s]*((?:b|p)(?:[1-3])?)?[\\s]*(?:(.*?)(?:--|—))?([^\\s0-9]*)[\\s]*([0-9]*)[\\s]*([^]*)", 24 | std::regex_constants::ECMAScript | std::regex_constants::icase); 25 | std::wsmatch m; 26 | if (std::regex_match(ws, m, re)) { 27 | // 获取骰子 28 | std::wstring dice(m[3]); 29 | if (dice.empty()) dice = L"D100"; 30 | // 原因 31 | std::wstring reason(m[5]); 32 | if (m[7].first != m[7].second) { 33 | if (reason.empty()) 34 | reason = m[7]; 35 | else 36 | reason += L"(" + m[7].str() + L")"; 37 | } 38 | // 人物卡 39 | std::string character_card(cq::utils::ws2s(m[4])); 40 | if (character_card.empty()) { 41 | character_card = utils::get_chosen_card(e.target); 42 | } 43 | else { 44 | reason = m[4].str() + L"--" + reason; 45 | } 46 | // 投掷属性 47 | std::string property(cq::utils::ws2s(m[5])); 48 | 49 | int judge_value; 50 | if (m[6].first != m[6].second) { 51 | judge_value = std::stoi(m[6]); 52 | if (property.empty() && !m[4].str().empty()) { 53 | throw exception::exception(msg::GetGlobalMsg("strPropertyInvalidError")); 54 | } 55 | } else { 56 | if (!property.empty()) { 57 | judge_value = 58 | utils::get_single_card_properties(e.target, character_card, property); 59 | } else { 60 | throw exception::exception(msg::GetGlobalMsg("strPropertyInvalidError")); 61 | } 62 | } 63 | if (judge_value < 0) { 64 | throw exception::exception(msg::GetGlobalMsg("strPropertyInvalidError")); 65 | } 66 | dice_calculator cal(dice); 67 | 68 | std::wstring res; 69 | res = cal.form_string(); 70 | 71 | if (m[2].first == m[2].second) { 72 | dice::msg_queue::MsgQueue.add( 73 | e.target, 74 | utils::format_string( 75 | msg::GetGlobalMsg("strRollDiceWithJudge"), 76 | std::map{ 77 | {"nick", utils::get_nickname(e.target)}, 78 | {"reason", cq::utils::ws2s(reason)}, 79 | {"dice_expression", cq::utils::ws2s(res)}, 80 | {"judge_value", std::to_string(judge_value)}, 81 | {"success_indicator", utils::get_success_indicator(e.target, static_cast(cal.result), judge_value)}})); 82 | } else { 83 | dice::msg_queue::MsgQueue.add( 84 | e.target, 85 | utils::format_string(msg::GetGlobalMsg("strHiddenDice"), 86 | std::map{{"nick", utils::get_nickname(e.target)}})); 87 | dice::msg_queue::MsgQueue.add( 88 | cq::Target(*e.target.user_id), 89 | utils::format_string( 90 | msg::GetGlobalMsg("strRollHiddenDiceWithJudge"), 91 | std::map{ 92 | {"origin", utils::get_originname(e.target)}, 93 | {"nick", utils::get_nickname(e.target)}, 94 | {"reason", cq::utils::ws2s(reason)}, 95 | {"dice_expression", cq::utils::ws2s(res)}, 96 | {"judge_value", std::to_string(judge_value)}, 97 | {"success_indicator", 98 | utils::get_success_indicator(e.target, static_cast(cal.result), judge_value)}})); 99 | } 100 | } 101 | } 102 | } // namespace dice 103 | -------------------------------------------------------------------------------- /src/dice_rarc_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class rarc_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_rf_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_rf_module.h" 2 | #include 3 | #include 4 | #include "cqsdk/cqsdk.h" 5 | #include "fudge_calculator.h" 6 | #include "dice_exception.h" 7 | #include "dice_utils.h" 8 | #include "dice_msg_queue.h" 9 | 10 | namespace cq::event { 11 | struct MessageEvent; 12 | } 13 | 14 | namespace dice { 15 | bool rf_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*rf[^]*", std::regex_constants::ECMAScript | std::regex_constants::icase); 17 | return std::regex_match(ws, re); 18 | } 19 | 20 | void rf_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 21 | std::wregex re(L"[\\s]*[\\.。.][\\s]*rf(h)?[\\s]*(([0-9]+)#)?([0-9]+)?(?:d)?(?:f)?((?:\\+|\\-)[0-9]+)?[\\s]*([^]*)", 22 | std::regex_constants::ECMAScript | std::regex_constants::icase); 23 | std::wsmatch m; 24 | if (std::regex_match(ws, m, re)) { 25 | std::wstring res; 26 | int RollCount = 1; 27 | if (m[2].first != m[2].second) { 28 | RollCount = std::stoi(m[3]); 29 | } 30 | 31 | if (RollCount <= 0 || RollCount > 10) { 32 | throw exception::dice_expression_invalid_error(); 33 | } 34 | 35 | std::wstring dice_count(m[4]); 36 | if (dice_count.empty()) 37 | { 38 | dice_count = L"4"; 39 | } 40 | std::wstring add_value(m[5]); 41 | if (add_value.empty()) 42 | { 43 | add_value = L"0"; 44 | } 45 | 46 | std::wstring reason(m[6]); 47 | 48 | res = fudge_calculator(dice_count, add_value).form_string(); 49 | 50 | for (int i = 1; i != RollCount; i++) { 51 | res += '\n'; 52 | res += fudge_calculator(dice_count, add_value).form_string(); 53 | } 54 | 55 | if (m[1].first == m[1].second) { 56 | dice::msg_queue::MsgQueue.add(e.target, 57 | utils::format_string( 58 | msg::GetGlobalMsg("strRollDice"), 59 | std::map{ 60 | {"nick", utils::get_nickname(e.target)}, 61 | {"reason", cq::utils::ws2s(reason)}, 62 | {"dice_expression", cq::utils::ws2s(res)} 63 | })); 64 | } else { 65 | dice::msg_queue::MsgQueue.add( 66 | e.target, 67 | utils::format_string(msg::GetGlobalMsg("strHiddenDice"), 68 | std::map{{"nick", utils::get_nickname(e.target)}})); 69 | 70 | dice::msg_queue::MsgQueue.add(cq::Target(*e.target.user_id), 71 | utils::format_string(msg::GetGlobalMsg("strRollHiddenDice"), 72 | std::map{ 73 | {"origin", utils::get_originname(e.target)}, 74 | {"nick", utils::get_nickname(e.target)}, 75 | {"reason", cq::utils::ws2s(reason)}, 76 | {"dice_expression", cq::utils::ws2s(res)} 77 | })); 78 | } 79 | } 80 | } 81 | } // namespace dice 82 | -------------------------------------------------------------------------------- /src/dice_rf_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class rf_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_rules_module.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cpprest/http_client.h" 4 | #include "cqsdk/cqsdk.h" 5 | #include "dice_calculator.h" 6 | #include "dice_exception.h" 7 | #include "dice_rules_module.h" 8 | #include "dice_msg_queue.h" 9 | 10 | namespace cq::event { 11 | struct MessageEvent; 12 | } 13 | 14 | namespace dice { 15 | bool rules_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*rules.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 17 | return std::regex_match(ws, re); 18 | } 19 | 20 | void rules_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 21 | std::wregex re(L"[\\s]*[\\.。.][\\s]*rules[\\s]*(?:([a-z0-9]*)(?::|:))?[\\s]*(.*)", 22 | std::regex_constants::ECMAScript | std::regex_constants::icase); 23 | std::wsmatch m; 24 | if (std::regex_match(ws, m, re)) { 25 | if (m[2].first == m[2].second) { 26 | throw exception::exception(msg::GetGlobalMsg("strParaEmptyError")); 27 | } 28 | 29 | // 请求必须大写 30 | std::wstring type(m[1]); 31 | std::wstring item(m[2]); 32 | std::transform(item.begin(), item.end(), item.begin(), std::towupper); 33 | std::transform(type.begin(), type.end(), type.begin(), std::towupper); 34 | 35 | // 请求Client 36 | web::http::client::http_client http_client(U("http://api.kokona.tech:5555/")); 37 | // POST数据 38 | utf8string data = "QQ=" + std::to_string(cq::api::get_login_user_id()) + "&v=20190114" 39 | + "&Name=" + utility::conversions::utf16_to_utf8(web::uri::encode_data_string(item)); 40 | 41 | if (!type.empty()) { 42 | data += "&Type=Rules-" + utility::conversions::utf16_to_utf8(type); 43 | } 44 | 45 | // 设置请求参数 46 | web::http::http_request req(web::http::methods::POST); 47 | req.set_request_uri(U("/rules")); 48 | req.set_body(data, "application/x-www-form-urlencoded;charset=utf-8"); 49 | // 设置UA 50 | req.headers().add(U("User-Agent"), msg::dice_user_agent); 51 | // 发起异步请求,堵塞等待结果 52 | web::http::http_response response = http_client.request(req).get(); 53 | 54 | std::string res = response.extract_utf8string().get(); 55 | if (res.empty()) { 56 | dice::msg_queue::MsgQueue.add(e.target, msg::GetGlobalMsg("strRulesNotFoundError")); 57 | } else { 58 | dice::msg_queue::MsgQueue.add(e.target, res); 59 | } 60 | } 61 | } 62 | } // namespace dice 63 | -------------------------------------------------------------------------------- /src/dice_rules_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class rules_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_set_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_set_module.h" 2 | #include "SQLiteCpp/SQLiteCpp.h" 3 | #include "cqsdk/cqsdk.h" 4 | #include "dice_calculator.h" 5 | #include "dice_db.h" 6 | #include "dice_exception.h" 7 | #include "dice_utils.h" 8 | #include "dice_msg_queue.h" 9 | 10 | namespace cq::event { 11 | struct MessageEvent; 12 | } 13 | 14 | namespace dice { 15 | bool set_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 16 | std::wregex re(L"[\\s]*[\\.。.][\\s]*set.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 17 | return std::regex_match(ws, re); 18 | } 19 | 20 | void set_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 21 | std::wregex re(L"[\\s]*[\\.。.][\\s]*set[\\s]*([0-9]*).*", 22 | std::regex_constants::ECMAScript | std::regex_constants::icase); 23 | std::wsmatch m; 24 | if (std::regex_match(ws, m, re)) { 25 | int SetDice = 100; 26 | if (m[1].first != m[1].second) { 27 | SetDice = std::stoi(m[1]); 28 | } 29 | if (SetDice <= 0) { 30 | throw exception::exception(msg::GetGlobalMsg("strDefaultDiceErr")); 31 | } 32 | if (e.message_type == cq::message::PRIVATE) { 33 | SQLite::Statement st(*db::db, "REPLACE INTO qq_info(qq_id, default_dice) VAlUES(?, ?)"); 34 | st.bind(1, *e.target.user_id); 35 | st.bind(2, SetDice); 36 | st.exec(); 37 | } else { 38 | if (!utils::is_admin_or_owner(e.target)) { 39 | throw exception::exception(msg::GetGlobalMsg("strPermissionDeniedError")); 40 | } 41 | SQLite::Statement st(*db::db, "REPLACE INTO group_info(group_id, type, default_dice) VALUES(?, ?, ?)"); 42 | if (e.message_type == cq::message::GROUP) { 43 | st.bind(1, *e.target.group_id); 44 | st.bind(2, 0); 45 | } else { 46 | st.bind(1, *e.target.discuss_id); 47 | st.bind(2, 1); 48 | } 49 | st.bind(3, SetDice); 50 | st.exec(); 51 | } 52 | dice::msg_queue::MsgQueue.add( 53 | e.target, 54 | utils::format_string(msg::GetGlobalMsg("strDefaultDice"), 55 | {{"origin", utils::get_originname(e.target)}, 56 | {"dice", "D" + std::to_string(SetDice)}})); 57 | } 58 | } 59 | } // namespace dice 60 | -------------------------------------------------------------------------------- /src/dice_set_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class set_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_setcoc_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_setcoc_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_utils.h" 4 | #include "dice_msg_queue.h" 5 | #include "SQLiteCpp/SQLiteCpp.h" 6 | #include "dice_exception.h" 7 | #include "dice_db.h" 8 | 9 | namespace cq::event { 10 | struct MessageEvent; 11 | } 12 | 13 | namespace dice { 14 | bool setcoc_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*setcoc.*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | return std::regex_match(ws, re); 17 | } 18 | 19 | void setcoc_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 20 | std::wregex re(L"[\\s]*[\\.。.][\\s]*setcoc[\\s]*([0-9]*).*", 21 | std::regex_constants::ECMAScript | std::regex_constants::icase); 22 | std::wsmatch m; 23 | if (std::regex_match(ws, m, re)) { 24 | int SuccessRule = 0; 25 | if (m[1].first != m[1].second) { 26 | SuccessRule = std::stoi(m[1]); 27 | } 28 | 29 | if (SuccessRule < 0 || SuccessRule > 5) { 30 | throw exception::exception(msg::GetGlobalMsg("strSuccessRuleErr")); 31 | } 32 | 33 | if (e.message_type == cq::message::PRIVATE) { 34 | SQLite::Statement st(*db::db, "REPLACE INTO qq_info(qq_id, success_rule) VAlUES(?, ?)"); 35 | st.bind(1, *e.target.user_id); 36 | st.bind(2, SuccessRule); 37 | st.exec(); 38 | } else { 39 | if (!utils::is_admin_or_owner(e.target)) { 40 | throw exception::exception(msg::GetGlobalMsg("strPermissionDeniedError")); 41 | } 42 | SQLite::Statement st(*db::db, "REPLACE INTO group_info(group_id, type, success_rule) VALUES(?, ?, ?)"); 43 | if (e.message_type == cq::message::GROUP) { 44 | st.bind(1, *e.target.group_id); 45 | st.bind(2, 0); 46 | } else { 47 | st.bind(1, *e.target.discuss_id); 48 | st.bind(2, 1); 49 | } 50 | st.bind(3, SuccessRule); 51 | st.exec(); 52 | } 53 | 54 | dice::msg_queue::MsgQueue.add( 55 | e.target, 56 | utils::format_string(msg::GetGlobalMsg("strSuccessRule"), 57 | {{"origin", utils::get_originname(e.target)}, 58 | {"rule", std::to_string(SuccessRule)}})); 59 | } 60 | } 61 | } // namespace dice 62 | -------------------------------------------------------------------------------- /src/dice_setcoc_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class setcoc_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_st_module.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_st_module.h" 2 | #include "cqsdk/cqsdk.h" 3 | #include "dice_calculator.h" 4 | #include "dice_exception.h" 5 | #include "dice_msg_queue.h" 6 | #include "dice_utils.h" 7 | 8 | namespace cq::event { 9 | struct MessageEvent; 10 | } 11 | 12 | namespace dice { 13 | 14 | bool st_module::match(const cq::event::MessageEvent &e, const std::wstring &ws) { 15 | std::wregex re(L"[\\s]*[\\.。.][\\s]*st[^]*", std::regex_constants::ECMAScript | std::regex_constants::icase); 16 | return std::regex_match(ws, re); 17 | } 18 | void st_module::process(const cq::event::MessageEvent &e, const std::wstring &ws) { 19 | std::wregex re( 20 | L"[\\s]*[\\.。.][\\s]*st[\\s]*((?:(del[\\s]*(?:(?:(.*?)(?:--|—))?([^]*)))|(clr[\\s]*(.*)))|(?:(show[\\s]*(" 21 | L"?:(?:(." 22 | L"*?)(?:--|—))?([^]*)))|(?:(switch[\\s]*(.*))|((?:(.*?)(?:--|—))?([^]*)))))", 23 | std::regex_constants::ECMAScript | std::regex_constants::icase); 24 | std::wsmatch m; 25 | if (std::regex_match(ws, m, re)) { 26 | // 获取全部人物卡 27 | if (m[1].first == m[1].second) { 28 | dice::msg_queue::MsgQueue.add( 29 | e.target, 30 | utils::format_string(msg::GetGlobalMsg("strStList"), 31 | {{"nick", utils::get_nickname(e.target)}, 32 | {"card_names", utils::get_all_card_name_string(e.target)}})); 33 | return; 34 | } 35 | // 删除属性 36 | if (m[2].first != m[2].second) { 37 | std::string character_card_name = cq::utils::ws2s(m[3]); 38 | if (character_card_name.empty()) { 39 | character_card_name = utils::get_chosen_card(e.target); 40 | } 41 | 42 | std::string properties(cq::utils::ws2s(m[4])); 43 | std::transform(properties.begin(), properties.end(), properties.begin(), [](unsigned char c) { 44 | return std::tolower(c); 45 | }); 46 | if (properties == "all") { 47 | utils::delete_character_card(e.target, character_card_name); 48 | dice::msg_queue::MsgQueue.add( 49 | e.target, 50 | utils::format_string(msg::GetGlobalMsg("strStClr"), {{"card_name", character_card_name}})); 51 | } else { 52 | std::set st_properties; 53 | std::regex property_re("([^\\s\\|0-9]+)"); 54 | auto word_begin = std::sregex_iterator(properties.begin(), properties.end(), property_re); 55 | auto word_end = std::sregex_iterator(); 56 | for (auto word_it = word_begin; word_it != word_end; word_it++) { 57 | std::string property_name = (*word_it)[1]; 58 | if (msg::SkillNameReplace.count(property_name)) { 59 | property_name = msg::SkillNameReplace.at(property_name); 60 | } 61 | if (!st_properties.count(property_name)) { 62 | st_properties.insert(property_name); 63 | } 64 | } 65 | utils::delete_character_properties(e.target, character_card_name, st_properties); 66 | dice::msg_queue::MsgQueue.add( 67 | e.target, 68 | utils::format_string(msg::GetGlobalMsg("strStDel"), 69 | {{"card_name", character_card_name}, 70 | {"num_of_properties", std::to_string(st_properties.size())}})); 71 | } 72 | return; 73 | } 74 | // 删除人物卡 75 | if (m[5].first != m[5].second) { 76 | std::string character_card_name = cq::utils::ws2s(m[6]); 77 | if (character_card_name.empty()) { 78 | character_card_name = utils::get_chosen_card(e.target); 79 | } 80 | utils::delete_character_card(e.target, character_card_name); 81 | dice::msg_queue::MsgQueue.add( 82 | e.target, 83 | utils::format_string(msg::GetGlobalMsg("strStClr"), {{"card_name", character_card_name}})); 84 | return; 85 | } 86 | // 展示人物卡 87 | if (m[7].first != m[7].second) { 88 | std::string character_card_name = cq::utils::ws2s(m[8]); 89 | if (character_card_name.empty()) { 90 | character_card_name = utils::get_chosen_card(e.target); 91 | } 92 | std::string properties(cq::utils::ws2s(m[9])); 93 | std::transform(properties.begin(), properties.end(), properties.begin(), [](unsigned char c) { 94 | return std::tolower(c); 95 | }); 96 | if (properties == "all") { 97 | dice::msg_queue::MsgQueue.add( 98 | e.target, 99 | utils::format_string(msg::GetGlobalMsg("strStShow"), 100 | {{"card_name", character_card_name}, 101 | {"card_properties", 102 | utils::get_all_card_properties_string(e.target, character_card_name)}})); 103 | } else { 104 | std::set st_properties; 105 | std::regex property_re("([^\\s\\|0-9]+)"); 106 | auto word_begin = std::sregex_iterator(properties.begin(), properties.end(), property_re); 107 | auto word_end = std::sregex_iterator(); 108 | for (auto word_it = word_begin; word_it != word_end; word_it++) { 109 | std::string property_name = (*word_it)[1]; 110 | 111 | if (msg::SkillNameReplace.count(property_name)) { 112 | property_name = msg::SkillNameReplace.at(property_name); 113 | } 114 | if (!st_properties.count(property_name)) { 115 | st_properties.insert(property_name); 116 | } 117 | } 118 | if (st_properties.empty()) { 119 | throw exception::exception(msg::GetGlobalMsg("strStShowEmptyError")); 120 | } 121 | dice::msg_queue::MsgQueue.add( 122 | e.target, 123 | utils::format_string( 124 | msg::GetGlobalMsg("strStShow"), 125 | {{"card_name", character_card_name}, 126 | {"card_properties", 127 | utils::get_card_properties_string(e.target, character_card_name, st_properties)}})); 128 | } 129 | return; 130 | } 131 | // 切换人物卡 132 | if (m[10].first != m[10].second) { 133 | std::string character_card_name = cq::utils::ws2s(m[11]); 134 | if (character_card_name.empty()) { 135 | character_card_name = "default"; 136 | } 137 | utils::set_chosen_card(e.target, character_card_name); 138 | dice::msg_queue::MsgQueue.add( 139 | e.target, 140 | utils::format_string(msg::GetGlobalMsg("strStSwitch"), {{"card_name", character_card_name}})); 141 | return; 142 | } 143 | // 人物卡设置部分 144 | if (m[12].first != m[12].second) { 145 | std::string character_card_name = cq::utils::ws2s(m[13]); 146 | if (character_card_name.empty()) { 147 | character_card_name = utils::get_chosen_card(e.target); 148 | } 149 | 150 | std::string character_card(cq::utils::ws2s(m[14])); 151 | std::transform( 152 | character_card.begin(), character_card.end(), character_card.begin(), [](unsigned char c) { 153 | return std::tolower(c); 154 | }); 155 | std::map mp_character_card; 156 | std::map mp_character_card_change; 157 | std::set st_character_card_change; 158 | std::regex card_re( 159 | "([^\\s\\|0-9\\+\\-]+?)[:=\\s]?([0-9]+)|([^\\s\\|0-9]+?)([\\+\\-][0-9d\\+\\-\\(\\)]+)"); 160 | auto word_begin = std::sregex_iterator(character_card.begin(), character_card.end(), card_re); 161 | auto word_end = std::sregex_iterator(); 162 | for (auto word_it = word_begin; word_it != word_end; word_it++) { 163 | std::smatch card_match = *word_it; 164 | if (card_match[1].first != card_match[1].second) { 165 | std::string property_name = card_match[1]; 166 | 167 | if (msg::SkillNameReplace.count(property_name)) { 168 | property_name = msg::SkillNameReplace.at(property_name); 169 | } 170 | if (st_character_card_change.count(property_name)) { 171 | throw dice::exception::exception(msg::GetGlobalMsg("strPropertyRepeatError")); 172 | } 173 | mp_character_card[property_name] = std::stoi(card_match[2]); 174 | } else { 175 | std::string property_name = card_match[3]; 176 | if (msg::SkillNameReplace.count(property_name)) { 177 | property_name = msg::SkillNameReplace.at(property_name); 178 | } 179 | if (st_character_card_change.count(property_name) || mp_character_card.count(property_name)) { 180 | throw dice::exception::exception(msg::GetGlobalMsg("strPropertyRepeatError")); 181 | } 182 | st_character_card_change.insert(property_name); 183 | mp_character_card_change[property_name] = card_match[4]; 184 | } 185 | } 186 | auto old_value = utils::get_card_properties(e.target, character_card_name, st_character_card_change); 187 | std::string change_str; 188 | for (const auto &it_change : mp_character_card_change) { 189 | if (it_change.second.find_first_of("d()+-", 1) == std::string::npos) { 190 | int new_value = old_value[it_change.first] + std::stoi(it_change.second); 191 | mp_character_card[it_change.first] = new_value; 192 | change_str += utils::format_string(msg::GetGlobalMsg("strStPropertyChange"), 193 | {{"property", it_change.first}, 194 | {"change", it_change.second}, 195 | {"value_old", std::to_string(old_value[it_change.first])}, 196 | {"value_new", std::to_string(new_value)}}); 197 | change_str += "\n"; 198 | } else { 199 | std::string dice = it_change.second; 200 | if (!dice.empty() && dice[0] == '+') dice = dice.substr(1); 201 | dice_calculator cal(cq::utils::s2ws(dice), utils::get_defaultdice(e.target)); 202 | int new_value = old_value[it_change.first] + static_cast(cal.result); 203 | mp_character_card[it_change.first] = new_value; 204 | change_str += utils::format_string( 205 | msg::GetGlobalMsg("strStPropertyChange"), 206 | {{"property", it_change.first}, 207 | {"change", it_change.second + "=" + std::to_string(static_cast(cal.result))}, 208 | {"value_old", std::to_string(old_value[it_change.first])}, 209 | {"value_new", std::to_string(new_value)}}); 210 | change_str += "\n"; 211 | } 212 | } 213 | utils::set_character_card(e.target, character_card_name, mp_character_card); 214 | if (st_character_card_change.empty()) { 215 | dice::msg_queue::MsgQueue.add( 216 | e.target, 217 | utils::format_string(msg::GetGlobalMsg("strStSet"), 218 | {{"card_name", character_card_name}, 219 | {"num_of_properties", std::to_string(mp_character_card.size())}})); 220 | } else { 221 | change_str.erase(change_str.end() - 1); 222 | dice::msg_queue::MsgQueue.add( 223 | e.target, 224 | utils::format_string(msg::GetGlobalMsg("strStSetWithChange"), 225 | {{"card_name", character_card_name}, 226 | {"num_of_properties", std::to_string(mp_character_card.size())}, 227 | {"change_str", change_str}})); 228 | } 229 | } 230 | } 231 | } 232 | } // namespace dice 233 | -------------------------------------------------------------------------------- /src/dice_st_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "dice_module.h" 4 | 5 | 6 | namespace cq::event { 7 | struct MessageEvent; 8 | } 9 | 10 | namespace dice { 11 | class st_module : public dice_module { 12 | public: 13 | bool match(const cq::event::MessageEvent& e, const std::wstring& ws) override; 14 | void process(const cq::event::MessageEvent& e, const std::wstring& ws) override; 15 | }; 16 | } // namespace dice -------------------------------------------------------------------------------- /src/dice_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "dice_utils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "SQLiteCpp/SQLiteCpp.h" 10 | #include "cqsdk/cqsdk.h" 11 | #include "cqsdk/types.h" 12 | #include "dice_calculator.h" 13 | #include "dice_db.h" 14 | #include "dice_exception.h" 15 | #include "dice_msg.h" 16 | 17 | 18 | namespace dice::utils { 19 | 20 | int get_success_rule(const cq::Target &target) { 21 | if (target.group_id.has_value()) { 22 | SQLite::Statement st(*db::db, "SELECT success_rule FROM group_info WHERE group_id = ? AND type = ?"); 23 | st.bind(1, *target.group_id); 24 | st.bind(2, 0); 25 | if (st.executeStep()) { 26 | return st.getColumn(0).getInt(); 27 | } 28 | return 0; 29 | } 30 | if (target.discuss_id.has_value()) { 31 | SQLite::Statement st(*db::db, "SELECT success_rule FROM group_info WHERE group_id = ? AND type = ?"); 32 | st.bind(1, *target.discuss_id); 33 | st.bind(2, 1); 34 | if (st.executeStep()) { 35 | return st.getColumn(0).getInt(); 36 | } 37 | return 0; 38 | } 39 | SQLite::Statement st(*db::db, "SELECT success_rule FROM qq_info WHERE qq_id = ?"); 40 | st.bind(1, *target.user_id); 41 | if (st.executeStep()) { 42 | return st.getColumn(0).getInt(); 43 | } 44 | return 0; 45 | } 46 | 47 | success_level get_success_level(const cq::Target &target, const int res, const int rate) { 48 | const int rule = utils::get_success_rule(target); 49 | switch (rule) { 50 | case 0: 51 | if (res == 100) return success_level::Fumble; 52 | if (res == 1) return success_level::CriticalSuccess; 53 | if (res <= rate / 5) return success_level::ExtremeSuccess; 54 | if (res <= rate / 2) return success_level::HardSuccess; 55 | if (res <= rate) return success_level::Success; 56 | if (rate >= 50 || res < 96) return success_level::Failure; 57 | return success_level::Fumble; 58 | case 1: 59 | if (res == 100) return success_level::Fumble; 60 | if (res == 1 || res <= 5 && rate >= 50) return success_level::CriticalSuccess; 61 | if (res <= rate / 5) return success_level::ExtremeSuccess; 62 | if (res <= rate / 2) return success_level::HardSuccess; 63 | if (res <= rate) return success_level::Success; 64 | if (rate >= 50 || res < 96) return success_level::Failure; 65 | return success_level::Fumble; 66 | case 2: 67 | if (res == 100) return success_level::Fumble; 68 | if (res <= 5 && res <= rate) return success_level::CriticalSuccess; 69 | if (res <= rate / 5) return success_level::ExtremeSuccess; 70 | if (res <= rate / 2) return success_level::HardSuccess; 71 | if (res <= rate) return success_level::Success; 72 | if (res < 96) return success_level::Failure; 73 | return success_level::Fumble; 74 | case 3: 75 | if (res >= 96) return success_level::Fumble; 76 | if (res <= 5) return success_level::CriticalSuccess; 77 | if (res <= rate / 5) return success_level::ExtremeSuccess; 78 | if (res <= rate / 2) return success_level::HardSuccess; 79 | if (res <= rate) return success_level::Success; 80 | return success_level::Failure; 81 | case 4: 82 | if (res == 100) return success_level::Fumble; 83 | if (res <= 5 && res <= rate / 10) return success_level::CriticalSuccess; 84 | if (res <= rate / 5) return success_level::ExtremeSuccess; 85 | if (res <= rate / 2) return success_level::HardSuccess; 86 | if (res <= rate) return success_level::Success; 87 | if (rate >= 50 || res < 96 + rate / 10) return success_level::Failure; 88 | return success_level::Fumble; 89 | case 5: 90 | if (res >= 99) return success_level::Fumble; 91 | if (res <= 2 && res < rate / 10) return success_level::CriticalSuccess; 92 | if (res <= rate / 5) return success_level::ExtremeSuccess; 93 | if (res <= rate / 2) return success_level::HardSuccess; 94 | if (res <= rate) return success_level::Success; 95 | if (rate >= 50 || res < 96) return success_level::Failure; 96 | return success_level::Fumble; 97 | default: 98 | throw exception::exception(msg::GetGlobalMsg("strUnknownError")); 99 | } 100 | } 101 | 102 | std::string get_success_indicator(const cq::Target &target, const int value, const int judge_value) { 103 | const success_level sl = get_success_level(target, value, judge_value); 104 | switch (sl) { 105 | case success_level::Fumble: 106 | return msg::GetGlobalMsg("strFumble"); 107 | case success_level::Failure: 108 | return msg::GetGlobalMsg("strFailure"); 109 | case success_level::Success: 110 | return msg::GetGlobalMsg("strSuccess"); 111 | case success_level::HardSuccess: 112 | return msg::GetGlobalMsg("strHardSuccess"); 113 | case success_level::ExtremeSuccess: 114 | return msg::GetGlobalMsg("strExtremeSuccess"); 115 | case success_level::CriticalSuccess: 116 | return msg::GetGlobalMsg("strCriticalSuccess"); 117 | } 118 | throw exception::exception(msg::GetGlobalMsg("strUnknownError")); 119 | } 120 | 121 | bool if_card_exist(const cq::Target &target, const std::string &card_name) { 122 | SQLite::Statement st(*db::db, "SELECT count(*) FROM character_cards WHERE qq_id = ? AND card_name = ?"); 123 | st.bind(1, *target.user_id); 124 | st.bind(2, card_name); 125 | if (st.executeStep()) { 126 | return st.getColumn(0).getInt() > 0; 127 | } 128 | return false; 129 | } 130 | void set_chosen_card(const cq::Target &target, const std::string &card_name) { 131 | if (target.group_id.has_value()) { 132 | SQLite::Statement st( 133 | *db::db, "REPLACE INTO group_user_info(group_id, type, qq_id, card_chosen) VALUES (?, ?, ?, ?)"); 134 | st.bind(1, *target.group_id); 135 | st.bind(2, 0); 136 | st.bind(3, *target.user_id); 137 | st.bind(4, card_name); 138 | st.exec(); 139 | } else if (target.discuss_id.has_value()) { 140 | SQLite::Statement st( 141 | *db::db, "REPLACE INTO group_user_info(group_id, type, qq_id, card_chosen) VALUES (?, ?, ?, ?)"); 142 | st.bind(1, *target.discuss_id); 143 | st.bind(2, 1); 144 | st.bind(3, *target.user_id); 145 | st.bind(4, card_name); 146 | st.exec(); 147 | } else { 148 | SQLite::Statement st(*db::db, "REPLACE INTO qq_info(qq_id, card_chosen) VALUES (?, ?)"); 149 | st.bind(1, *target.user_id); 150 | st.bind(2, card_name); 151 | st.exec(); 152 | } 153 | } 154 | std::string get_chosen_card(const cq::Target &target) { 155 | if (target.group_id.has_value()) { 156 | SQLite::Statement st( 157 | *db::db, "SELECT card_chosen FROM group_user_info WHERE group_id = ? AND type = ? AND qq_id = ?"); 158 | st.bind(1, *target.group_id); 159 | st.bind(2, 0); 160 | st.bind(3, *target.user_id); 161 | if (st.executeStep()) { 162 | return st.getColumn(0).getString(); 163 | } 164 | return "default"; 165 | } 166 | if (target.discuss_id.has_value()) { 167 | SQLite::Statement st( 168 | *db::db, "SELECT card_chosen FROM group_user_info WHERE group_id = ? AND type = ? AND qq_id = ?"); 169 | st.bind(1, *target.discuss_id); 170 | st.bind(2, 1); 171 | st.bind(3, *target.user_id); 172 | if (st.executeStep()) { 173 | return st.getColumn(0).getString(); 174 | } 175 | return "default"; 176 | } 177 | SQLite::Statement st(*db::db, "SELECT card_chosen FROM qq_info WHERE qq_id = ?"); 178 | st.bind(1, *target.user_id); 179 | if (st.executeStep()) { 180 | return st.getColumn(0).getString(); 181 | } 182 | return "default"; 183 | } 184 | std::map get_card_properties(const cq::Target &target, const std::string &character_card_name, 185 | const std::set &st_character_properties) { 186 | std::map ret; 187 | SQLite::Transaction tran(*db::db); 188 | for (const auto &it : st_character_properties) { 189 | SQLite::Statement st( 190 | *db::db, "SELECT value FROM character_cards WHERE qq_id = ? AND card_name = ? AND property = ?"); 191 | st.bind(1, *target.user_id); 192 | st.bind(2, character_card_name); 193 | st.bind(3, it); 194 | if (st.executeStep()) { 195 | ret[it] = st.getColumn(0).getInt(); 196 | } else if (msg::SkillDefaultVal.count(it)) { 197 | ret[it] = msg::SkillDefaultVal.at(it); 198 | } else { 199 | throw exception::exception( 200 | utils::format_string(msg::GetGlobalMsg("strPropertyNotFoundError"), {{"property_name", it}})); 201 | } 202 | } 203 | tran.commit(); 204 | return ret; 205 | } 206 | int get_single_card_properties(const cq::Target &target, const std::string &character_card_name, 207 | std::string &property) { 208 | SQLite::Statement st(*db::db, 209 | "SELECT value FROM character_cards WHERE qq_id = ? AND card_name = ? AND property = ?"); 210 | st.bind(1, *target.user_id); 211 | st.bind(2, character_card_name); 212 | st.bind(3, property); 213 | if (st.executeStep()) { 214 | return st.getColumn(0).getInt(); 215 | } 216 | if (msg::SkillDefaultVal.count(property)) { 217 | return msg::SkillDefaultVal.at(property); 218 | } 219 | throw exception::exception( 220 | utils::format_string(msg::GetGlobalMsg("strPropertyNotFoundError"), {{"property_name", property}})); 221 | } 222 | 223 | std::string get_card_properties_string(const cq::Target &target, const std::string &character_card_name, 224 | const std::set &st_character_properties) { 225 | std::string ret; 226 | std::map card_properties = 227 | get_card_properties(target, character_card_name, st_character_properties); 228 | std::string fmt = msg::GetGlobalMsg("strStProperty"); 229 | int count = 1; 230 | for (const auto &it : card_properties) { 231 | ret += format_string(fmt, {{"property", it.first}, {"value", std::to_string(it.second)}}); 232 | if (count % 3) { 233 | ret += " "; 234 | } else { 235 | ret += "\n"; 236 | } 237 | ++count; 238 | } 239 | ret.erase(ret.end() - 1); 240 | return ret; 241 | } 242 | void delete_character_properties(const cq::Target &target, const std::string &character_card_name, 243 | const std::set &st_character_properties) { 244 | SQLite::Transaction tran(*db::db); 245 | for (const auto &it : st_character_properties) { 246 | SQLite::Statement st(*db::db, 247 | "DELETE FROM character_cards WHERE qq_id = ? AND card_name = ? AND property = ?"); 248 | st.bind(1, *target.user_id); 249 | st.bind(2, character_card_name); 250 | st.bind(3, it); 251 | st.exec(); 252 | } 253 | tran.commit(); 254 | } 255 | void delete_character_card(const cq::Target &target, const std::string &character_card_name) { 256 | SQLite::Statement st(*db::db, "DELETE FROM character_cards WHERE qq_id = ? AND card_name = ?"); 257 | st.bind(1, *target.user_id); 258 | st.bind(2, character_card_name); 259 | st.exec(); 260 | } 261 | 262 | std::set get_all_card_name(const cq::Target &target) { 263 | std::set ret; 264 | SQLite::Transaction tran(*db::db); 265 | SQLite::Statement st(*db::db, "SELECT card_name FROM character_cards WHERE qq_id = ?"); 266 | st.bind(1, *target.user_id); 267 | while (st.executeStep()) { 268 | if (!ret.count(st.getColumn(0).getString())) { 269 | ret.insert(st.getColumn(0).getString()); 270 | } 271 | } 272 | tran.commit(); 273 | if (ret.empty()) { 274 | throw exception::exception(msg::GetGlobalMsg("strDatabaseNotFoundError")); 275 | } 276 | return ret; 277 | } 278 | 279 | std::string get_all_card_name_string(const cq::Target &target) { 280 | std::set card_name = get_all_card_name(target); 281 | std::string ret; 282 | for (const auto &it : card_name) { 283 | ret += it; 284 | ret += "\n"; 285 | } 286 | ret.erase(ret.end() - 1); 287 | return ret; 288 | } 289 | 290 | std::map get_all_card_properties(const cq::Target &target, 291 | const std::string &character_card_name) { 292 | std::map ret; 293 | SQLite::Transaction tran(*db::db); 294 | SQLite::Statement st(*db::db, "SELECT property, value FROM character_cards WHERE qq_id = ? AND card_name = ?"); 295 | st.bind(1, *target.user_id); 296 | st.bind(2, character_card_name); 297 | while (st.executeStep()) { 298 | ret[st.getColumn(0).getString()] = st.getColumn(1).getInt(); 299 | } 300 | tran.commit(); 301 | if (ret.empty()) { 302 | throw exception::exception(msg::GetGlobalMsg("strDatabaseNotFoundError")); 303 | } 304 | return ret; 305 | } 306 | std::string get_all_card_properties_string(const cq::Target &target, const std::string &character_card_name) { 307 | std::string ret; 308 | std::map card_properties = get_all_card_properties(target, character_card_name); 309 | std::string fmt = msg::GetGlobalMsg("strStProperty"); 310 | int count = 1; 311 | for (const auto &it : card_properties) { 312 | ret += format_string(fmt, {{"property", it.first}, {"value", std::to_string(it.second)}}); 313 | if (count % 3) { 314 | ret += " "; 315 | } else { 316 | ret += "\n"; 317 | } 318 | ++count; 319 | } 320 | ret.erase(ret.end() - 1); 321 | return ret; 322 | } 323 | void set_character_card(const cq::Target &target, const std::string &character_card_name, 324 | const std::map &mp_character_card) { 325 | SQLite::Transaction tran(*db::db); 326 | for (const auto &it : mp_character_card) { 327 | SQLite::Statement st(*db::db, 328 | "REPLACE INTO character_cards(qq_id, card_name, property, value) VALUES (?, ?, ?, ?)"); 329 | st.bind(1, *target.user_id); 330 | st.bind(2, character_card_name); 331 | st.bind(3, it.first); 332 | st.bind(4, it.second); 333 | st.exec(); 334 | } 335 | tran.commit(); 336 | } 337 | void set_jrrp_enabled(const cq::Target &target, const bool enabled) { 338 | if (target.group_id.has_value()) { 339 | set_jrrp_enabled(*target.group_id, 0, enabled); 340 | } else if (target.discuss_id.has_value()) { 341 | set_jrrp_enabled(*target.discuss_id, 1, enabled); 342 | } else { 343 | throw exception::exception(msg::GetGlobalMsg("strCommandNotAvailableError")); 344 | } 345 | } 346 | 347 | void set_jrrp_enabled(const int64_t group_id, const int type, const bool enabled) { 348 | SQLite::Statement st(*db::db, "REPLACE INTO group_info(group_id, type, jrrp_on) VALUES(?, ?, ?)"); 349 | st.bind(1, group_id); 350 | st.bind(2, type); 351 | st.bind(3, enabled); 352 | st.exec(); 353 | } 354 | 355 | bool is_jrrp_enabled(const int64_t group_id, const int type) { 356 | SQLite::Statement st(*db::db, "SELECT jrrp_on FROM group_info WHERE group_id=? and type=?"); 357 | st.bind(1, group_id); 358 | st.bind(2, type); 359 | if (st.executeStep()) { 360 | return st.getColumn(0).getInt(); 361 | } 362 | return true; 363 | } 364 | 365 | bool is_jrrp_enabled(const cq::Target &target) { 366 | if (target.group_id.has_value()) { 367 | return is_jrrp_enabled(*target.group_id, 0); 368 | } 369 | if (target.discuss_id.has_value()) { 370 | return is_jrrp_enabled(*target.discuss_id, 1); 371 | } 372 | return true; 373 | } 374 | 375 | void set_help_enabled(const cq::Target &target, const bool enabled) { 376 | if (target.group_id.has_value()) { 377 | set_help_enabled(*target.group_id, 0, enabled); 378 | } else if (target.discuss_id.has_value()) { 379 | set_help_enabled(*target.discuss_id, 1, enabled); 380 | } else { 381 | throw exception::exception(msg::GetGlobalMsg("strCommandNotAvailableError")); 382 | } 383 | } 384 | 385 | void set_help_enabled(const int64_t group_id, const int type, const bool enabled) { 386 | SQLite::Statement st(*db::db, "REPLACE INTO group_info(group_id, type, help_on) VALUES(?, ?, ?)"); 387 | st.bind(1, group_id); 388 | st.bind(2, type); 389 | st.bind(3, enabled); 390 | st.exec(); 391 | } 392 | 393 | bool is_help_enabled(const int64_t group_id, const int type) { 394 | SQLite::Statement st(*db::db, "SELECT help_on FROM group_info WHERE group_id=? and type=?"); 395 | st.bind(1, group_id); 396 | st.bind(2, type); 397 | if (st.executeStep()) { 398 | return st.getColumn(0).getInt(); 399 | } 400 | return true; 401 | } 402 | 403 | bool is_help_enabled(const cq::Target &target) { 404 | if (target.group_id.has_value()) { 405 | return is_help_enabled(*target.group_id, 0); 406 | } 407 | if (target.discuss_id.has_value()) { 408 | return is_help_enabled(*target.discuss_id, 1); 409 | } 410 | return true; 411 | } 412 | 413 | std::string get_date() { 414 | time_t raw_time; 415 | time(&raw_time); 416 | tm time_info{}; 417 | localtime_s(&time_info, &raw_time); 418 | std::stringstream time_format; 419 | time_format << std::put_time(&time_info, "%F"); 420 | return time_format.str(); 421 | } 422 | 423 | void set_jrrp(const cq::Target &target, int jrrp_val) { 424 | SQLite::Statement st(*db::db, "REPLACE INTO qq_info(qq_id, jrrp_value, jrrp_date) VALUES (?, ?, ?)"); 425 | st.bind(1, *target.user_id); 426 | st.bind(2, jrrp_val); 427 | st.bind(3, get_date()); 428 | st.exec(); 429 | } 430 | 431 | std::tuple get_jrrp(const cq::Target &target) { 432 | SQLite::Statement st(*db::db, "SELECT jrrp_value, jrrp_date FROM qq_info WHERE qq_id = ?"); 433 | st.bind(1, *target.user_id); 434 | if (st.executeStep()) { 435 | if (st.getColumn(0).isNull() || st.getColumn(1).isNull()) return std::make_tuple(false, 0); 436 | 437 | if (get_date() == st.getColumn(1).getString()) { 438 | return std::make_tuple(true, st.getColumn(0).getInt()); 439 | } 440 | return std::make_tuple(false, 0); 441 | } 442 | return std::make_tuple(false, 0); 443 | } 444 | 445 | int get_defaultdice(const cq::Target &target) { 446 | if (target.group_id.has_value()) { 447 | SQLite::Statement st(*db::db, "SELECT default_dice FROM group_info WHERE group_id = ? AND type = ?"); 448 | st.bind(1, *target.group_id); 449 | st.bind(2, 0); 450 | if (st.executeStep()) { 451 | return st.getColumn(0).getInt(); 452 | } 453 | return 100; 454 | } 455 | if (target.discuss_id.has_value()) { 456 | SQLite::Statement st(*db::db, "SELECT default_dice FROM group_info WHERE group_id = ? AND type = ?"); 457 | st.bind(1, *target.discuss_id); 458 | st.bind(2, 1); 459 | if (st.executeStep()) { 460 | return st.getColumn(0).getInt(); 461 | } 462 | return 100; 463 | } 464 | SQLite::Statement st(*db::db, "SELECT default_dice FROM qq_info WHERE qq_id = ?"); 465 | st.bind(1, *target.user_id); 466 | if (st.executeStep()) { 467 | return st.getColumn(0).getInt(); 468 | } 469 | return 100; 470 | } 471 | 472 | std::string get_groupname(const int64_t group_id) { 473 | auto list = cq::api::get_group_list(); 474 | for (auto &ele : list) { 475 | if (ele.group_id == group_id) { 476 | return ele.group_name; 477 | } 478 | } 479 | return msg::GetGlobalMsg("strGroupnameError"); 480 | } 481 | 482 | std::string get_originname(const cq::Target &target) { 483 | if (target.group_id.has_value()) { 484 | return "群\"" + get_groupname(*target.group_id) + "\""; 485 | } 486 | if (target.discuss_id.has_value()) { 487 | return "讨论组"; 488 | } 489 | return "私聊会话"; 490 | } 491 | 492 | // 昵称获取 群/讨论组 type=0代表群, type=1代表讨论组 493 | std::string get_nickname(const int64_t group_id, const int64_t user_id, const int type, const bool only_from_db) { 494 | SQLite::Statement st(*db::db, "SELECT nick_name FROM group_user_info WHERE group_id=? AND qq_id=? AND type=?"); 495 | st.bind(1, group_id); 496 | st.bind(2, user_id); 497 | st.bind(3, type); 498 | if (st.executeStep()) { 499 | if (!st.getColumn(0).isNull() && !st.getColumn(0).getString().empty()) { 500 | return st.getColumn(0).getString(); 501 | } 502 | } 503 | SQLite::Statement st1(*db::db, "SELECT nick_name FROM qq_info WHERE qq_id=?"); 504 | st1.bind(1, user_id); 505 | if (st1.executeStep()) { 506 | if (!st1.getColumn(0).isNull() && !st1.getColumn(0).getString().empty()) { 507 | return st1.getColumn(0).getString(); 508 | } 509 | } 510 | if (only_from_db) { 511 | return ""; 512 | } 513 | if (type == 0) { 514 | const cq::GroupMember gm = cq::api::get_group_member_info(group_id, user_id); 515 | if (!gm.card.empty()) { 516 | return gm.card; 517 | } 518 | return gm.nickname; 519 | } 520 | return cq::api::get_stranger_info(user_id).nickname; 521 | } 522 | 523 | // 昵称获取 私聊 524 | std::string get_nickname(const int64_t user_id, const bool only_from_db) { 525 | SQLite::Statement st(*db::db, "SELECT nick_name FROM qq_info WHERE qq_id=?"); 526 | st.bind(1, user_id); 527 | if (st.executeStep()) { 528 | if (!st.getColumn(0).isNull() && !st.getColumn(0).getString().empty()) { 529 | return st.getColumn(0).getString(); 530 | } 531 | } 532 | if (only_from_db) { 533 | return ""; 534 | } 535 | return cq::api::get_stranger_info(user_id).nickname; 536 | } 537 | 538 | // 昵称获取 综合 539 | std::string get_nickname(const cq::Target &target, const bool only_from_db) { 540 | if (target.user_id.has_value()) { 541 | if (target.group_id.has_value()) { 542 | return get_nickname(*target.group_id, *target.user_id, 0, only_from_db); 543 | } 544 | if (target.discuss_id.has_value()) { 545 | return get_nickname(*target.discuss_id, *target.user_id, 1, only_from_db); 546 | } 547 | return get_nickname(*target.user_id, only_from_db); 548 | } 549 | return msg::GetGlobalMsg("strNicknameError"); 550 | } 551 | 552 | // 设置群昵称 553 | void set_group_nickname(const int64_t group_id, const int64_t user_id, const int type, 554 | const std::string &nick_name) { 555 | SQLite::Statement st(*db::db, 556 | "REPLACE INTO group_user_info(group_id, qq_id, type, nick_name) VALUES(?, ?, ?, ?)"); 557 | st.bind(1, group_id); 558 | st.bind(2, user_id); 559 | st.bind(3, type); 560 | st.bind(4, nick_name); 561 | st.exec(); 562 | } 563 | 564 | // 设置群昵称 565 | void set_group_nickname(const cq::Target &target, const std::string &nick_name) { 566 | if (target.user_id.has_value()) { 567 | if (target.group_id.has_value()) { 568 | set_group_nickname(*target.group_id, *target.user_id, 0, nick_name); 569 | } else if (target.discuss_id.has_value()) { 570 | set_group_nickname(*target.discuss_id, *target.user_id, 1, nick_name); 571 | } else { 572 | throw exception::exception(msg::GetGlobalMsg("strCommandNotAvailableError")); 573 | } 574 | 575 | } else { 576 | throw exception::exception(msg::GetGlobalMsg("strSetNicknameError")); 577 | } 578 | } 579 | 580 | // 设置全局昵称 581 | void set_global_nickname(const int64_t user_id, const std::string &nick_name) { 582 | SQLite::Statement st(*db::db, "REPLACE INTO qq_info(qq_id, nick_name) VALUES(?, ?)"); 583 | st.bind(1, user_id); 584 | st.bind(2, nick_name); 585 | st.exec(); 586 | } 587 | 588 | // 设置全局昵称 589 | void set_global_nickname(const cq::Target &target, const std::string &nick_name) { 590 | if (target.user_id.has_value()) { 591 | set_global_nickname(*target.user_id, nick_name); 592 | } else { 593 | throw exception::exception(msg::GetGlobalMsg("strSetNicknameError")); 594 | } 595 | } 596 | 597 | // 格式化字符串 598 | std::string format_string(const std::string &origin_str, const std::map &format_para) { 599 | std::string old_str = origin_str; 600 | bool need_format = true; 601 | std::map> mp; 602 | while (need_format) { 603 | need_format = false; 604 | std::string new_str; 605 | std::regex re("\\{([^\\{\\}]+)\\}"); 606 | auto words_begin = std::sregex_iterator(old_str.begin(), old_str.end(), re); 607 | auto words_end = std::sregex_iterator(); 608 | size_t last = 0; 609 | for (auto it = words_begin; it != words_end; ++it) { 610 | need_format = true; 611 | std::string format_str((*it)[1]); 612 | new_str += old_str.substr(last, it->position() - last); 613 | if (format_str[0] == '!') { 614 | std::string dice = format_str.substr(1); 615 | if (dice.empty()) dice = "d"; 616 | new_str += cq::utils::ws2s(dice_calculator(cq::utils::s2ws(dice)).form_string()); 617 | } else if (format_str[0] == '?') { 618 | std::string dice = format_str.substr(1); 619 | if (dice.empty()) dice = "d"; 620 | std::string res = std::to_string(dice_calculator(cq::utils::s2ws(dice)).result); 621 | res = res.substr( 622 | 0, std::max(res.find('.'), std::min(res.find('.') + 3, res.find_last_not_of("0.") + 1))); 623 | new_str += res; 624 | } else if (format_str[0] == '%') { 625 | std::string deck_name = format_str.substr(1); 626 | SQLite::Statement st(*db::db, 627 | "SELECT content FROM deck WHERE origin=\"public\" AND name=? LIMIT 1 OFFSET " 628 | "ABS(RANDOM()) % MAX((SELECT " 629 | "COUNT(*) FROM deck WHERE origin=\"public\" AND name=?), 1)"); 630 | st.bind(1, deck_name); 631 | st.bind(2, deck_name); 632 | if (st.executeStep()) { 633 | new_str += st.getColumn(0).getText(); 634 | } else { 635 | throw exception::exception(msg::GetGlobalMsg("strDatabaseNotFoundError")); 636 | } 637 | } else if (format_str[0] == '#') { 638 | std::string choose_str = format_str.substr(1); 639 | int count = 1; 640 | std::regex re_count("([^:]*):([0-9]*)"); 641 | std::smatch re_count_match; 642 | if (std::regex_match(choose_str, re_count_match, re_count)) { 643 | if (re_count_match[2].first != re_count_match[2].second) count = std::stoi(re_count_match[2]); 644 | choose_str = re_count_match[1]; 645 | } 646 | if (count == 0) { 647 | throw exception::exception(msg::GetGlobalMsg("strFormatStrInvalidError")); 648 | } 649 | std::regex re("[^|]+"); 650 | std::vector choose_vec; 651 | auto word_begin = std::sregex_iterator(choose_str.begin(), choose_str.end(), re); 652 | auto word_end = std::sregex_iterator(); 653 | for (auto ii = word_begin; ii != word_end; ii++) { 654 | choose_vec.push_back((*ii)[0]); 655 | } 656 | if (choose_vec.empty()) { 657 | throw exception::exception(msg::GetGlobalMsg("strFormatStrInvalidError")); 658 | } 659 | auto choose_vec_copy = choose_vec; 660 | for (int counter = 0; counter != count; counter++) { 661 | int index = std::uniform_int_distribution(0, choose_vec.size() - 1)(dice_calculator::ran); 662 | new_str += choose_vec[index]; 663 | choose_vec.erase(choose_vec.begin() + index); 664 | if (counter != count - 1) new_str += " "; 665 | if (choose_vec.empty()) choose_vec = choose_vec_copy; 666 | } 667 | } else if (format_str[0] == '@') { 668 | int count = 0; 669 | std::string deck_name = format_str.substr(1); 670 | SQLite::Statement st(*db::db, "SELECT COUNT(*) FROM deck WHERE origin=\"public\" AND name=?"); 671 | st.bind(1, deck_name); 672 | if (st.executeStep()) { 673 | count = st.getColumn(0).getInt(); 674 | } 675 | if (count == 0) { 676 | throw exception::exception(msg::GetGlobalMsg("strDatabaseNotFoundError")); 677 | } 678 | int offset_num = 0; 679 | if (mp.count(deck_name)) { 680 | if (count <= static_cast(mp[deck_name].size())) mp[deck_name].clear(); 681 | count -= mp[deck_name].size(); 682 | } else { 683 | mp[deck_name] = std::vector(); 684 | } 685 | offset_num = std::uniform_int_distribution(0, count - 1)(dice_calculator::ran); 686 | std::sort(mp[deck_name].begin(), mp[deck_name].end()); 687 | for (int ele : mp[deck_name]) { 688 | if (offset_num >= ele) offset_num++; 689 | } 690 | 691 | SQLite::Statement st1( 692 | *db::db, "SELECT content FROM deck WHERE origin=\"public\" AND name=? LIMIT 1 OFFSET ?"); 693 | 694 | st1.bind(1, deck_name); 695 | st1.bind(2, offset_num); 696 | if (st1.executeStep()) { 697 | new_str += st1.getColumn(0).getText(); 698 | } 699 | mp[deck_name].push_back(offset_num); 700 | } else { 701 | if (format_para.count(format_str)) 702 | new_str += format_para.at(format_str); 703 | else if (msg::global_msg.count(format_str)) 704 | new_str += msg::global_msg.at(format_str); 705 | } 706 | last = it->position() + it->length(); 707 | } 708 | new_str += old_str.substr(last); 709 | old_str = new_str; 710 | } 711 | return old_str; 712 | } 713 | 714 | // 获取权限 群 715 | bool is_admin_or_owner(const int64_t group_id, const int64_t user_id) { 716 | auto role = cq::api::get_group_member_info(group_id, user_id).role; 717 | return (role == cq::GroupRole::ADMIN || role == cq::GroupRole::OWNER); 718 | } 719 | 720 | // 获取权限 综合 (非群返回永远为真) 721 | bool is_admin_or_owner(const cq::Target &target) { 722 | if (target.group_id.has_value() && target.user_id.has_value()) { 723 | return is_admin_or_owner(*target.group_id, *target.user_id); 724 | } 725 | return true; 726 | } 727 | 728 | } // namespace dice::utils 729 | -------------------------------------------------------------------------------- /src/dice_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "cqsdk/cqsdk.h" 5 | #include "cqsdk/types.h" 6 | 7 | namespace dice::utils { 8 | // 成功等级枚举类 9 | enum class success_level : int { 10 | Fumble = 0, 11 | Failure = 1, 12 | Success = 2, 13 | HardSuccess = 3, 14 | ExtremeSuccess = 4, 15 | CriticalSuccess = 5 16 | }; 17 | 18 | // 获取当前COC判定规则 19 | int get_success_rule(const cq::Target& target); 20 | 21 | // 获取判定成功等级 22 | success_level get_success_level(const cq::Target& target, const int res, const int rate); 23 | 24 | // 获取判定成功等级字符串 25 | std::string get_success_indicator(const cq::Target& target, const int value, const int judge_value); 26 | 27 | // 查询某人物卡是否存在 28 | bool if_card_exist(const cq::Target& target, const std::string& card_name); 29 | 30 | // 设置当前绑定的人物卡 31 | void set_chosen_card(const cq::Target& target, const std::string& card_name); 32 | 33 | // 获取当前绑定的人物卡 34 | std::string get_chosen_card(const cq::Target& target); 35 | 36 | // 获取某人物卡中的单个属性 37 | int get_single_card_properties(const cq::Target& target, const std::string& character_card_name, 38 | std::string& property); 39 | 40 | // 获取某人物卡中的某(些)属性 41 | std::map get_card_properties(const cq::Target& target, const std::string& character_card_name, 42 | const std::set& st_character_properties); 43 | // 获取某人物卡中的某(些)属性 44 | std::string get_card_properties_string(const cq::Target& target, const std::string& character_card_name, 45 | const std::set& st_character_properties); 46 | // 删除某人物卡中的某(些)属性 47 | void delete_character_properties(const cq::Target& target, const std::string& character_card_name, 48 | const std::set& st_character_properties); 49 | // 删除某人物卡 50 | void delete_character_card(const cq::Target& target, const std::string& character_card_name); 51 | 52 | // 获取某人的所有人物卡名称 53 | std::set get_all_card_name(const cq::Target& target); 54 | 55 | // 获取某人的所有人物卡名称 56 | std::string get_all_card_name_string(const cq::Target& target); 57 | 58 | // 获取某个人物卡的全部属性 59 | std::map get_all_card_properties(const cq::Target& target, 60 | const std::string& character_card_name); 61 | 62 | // 获取某个人物卡的全部属性 63 | std::string get_all_card_properties_string(const cq::Target& target, const std::string& character_card_name); 64 | 65 | // 设置用户人物卡 66 | void set_character_card(const cq::Target& target, const std::string& character_card_name, 67 | const std::map& mp_character_card); 68 | // 设置Jrrp开启情况 69 | void set_jrrp_enabled(const int64_t group_id, const int type, const bool enabled); 70 | 71 | // 设置Jrrp开启情况 72 | void set_jrrp_enabled(const cq::Target& target, const bool enabled); 73 | 74 | // 获取Jrrp是否开启 75 | bool is_jrrp_enabled(const cq::Target& target); 76 | 77 | // 获取Jrrp是否开启 78 | bool is_jrrp_enabled(const int64_t group_id, const int type); 79 | 80 | 81 | // 设置Help开启情况 82 | void set_help_enabled(const int64_t group_id, const int type, const bool enabled); 83 | 84 | // 设置Help开启情况 85 | void set_help_enabled(const cq::Target& target, const bool enabled); 86 | 87 | // 获取Help是否开启 88 | bool is_help_enabled(const cq::Target& target); 89 | 90 | // 获取Help是否开启 91 | bool is_help_enabled(const int64_t group_id, const int type); 92 | 93 | // 设置jrrp 94 | void set_jrrp(const cq::Target& target, int jrrp_val); 95 | 96 | // 获取jrrp 97 | std::tuple get_jrrp(const cq::Target& target); 98 | 99 | // 获取今日日期 100 | std::string get_date(); 101 | 102 | // 默认面数获取 103 | int get_defaultdice(const cq::Target& target); 104 | 105 | // 群名获取 106 | std::string get_groupname(const int64_t group_id); 107 | 108 | // 来源获取 109 | std::string get_originname(const cq::Target& target); 110 | 111 | // 昵称获取 群/讨论组 type=0为群, type=1为讨论组 112 | std::string get_nickname(const int64_t group_id, const int64_t user_id, const int type, const bool only_from_db = false); 113 | 114 | // 昵称获取 私聊 115 | std::string get_nickname(const int64_t user_id, const bool only_from_db = false); 116 | 117 | // 昵称获取 综合 118 | std::string get_nickname(const cq::Target& target, const bool only_from_db = false); 119 | 120 | // 设置群昵称 121 | void set_group_nickname(const cq::Target& target, const std::string& nick_name); 122 | 123 | // 设置群昵称 124 | void set_group_nickname(const int64_t group_id, const int64_t user_id, const int type, 125 | const std::string& nick_name); 126 | 127 | // 设置全局昵称 128 | void set_global_nickname(const cq::Target& target, const std::string& nick_name); 129 | 130 | // 设置全局昵称 131 | void set_global_nickname(const int64_t user_id, const std::string& nick_name); 132 | 133 | // 格式化字符串 134 | std::string format_string( 135 | const std::string& origin_str, 136 | const std::map& format_para = std::map()); 137 | 138 | // 获取权限 群 139 | bool is_admin_or_owner(const int64_t group_id, const int64_t user_id); 140 | 141 | // 获取权限 综合 (非群返回永远为真) 142 | bool is_admin_or_owner(const cq::Target& target); 143 | } 144 | -------------------------------------------------------------------------------- /src/fudge_calculator.cpp: -------------------------------------------------------------------------------- 1 | #include "fudge_calculator.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "dice_exception.h" 8 | 9 | namespace dice { 10 | 11 | std::mt19937 fudge_calculator::ran(clock()); 12 | 13 | fudge_calculator::fudge_calculator(std::wstring dice_count, std::wstring add_value) 14 | : dice_count(std::stoi(dice_count)), add_value(std::stoi(add_value)) { 15 | main_calculate(); 16 | } 17 | 18 | fudge_calculator::fudge_calculator(int dice_count, int default_dice) 19 | : dice_count(dice_count), add_value(add_value) { 20 | main_calculate(); 21 | } 22 | 23 | 24 | 25 | void fudge_calculator::main_calculate() { 26 | std::uniform_int_distribution gen(-1, 1); 27 | res_display = L"["; 28 | for (int counter = 0; counter != dice_count; counter++) 29 | { 30 | if (counter != 0) res_display.append(L", "); 31 | int temp_res = gen(ran); 32 | if (temp_res == -1) { 33 | res_display.append(L"-"); 34 | result -= 1; 35 | } 36 | else if(temp_res == 0) { 37 | res_display.append(L"0"); 38 | } 39 | else { 40 | res_display.append(L"+"); 41 | result += 1; 42 | } 43 | } 44 | res_display.append(L"]"); 45 | if (add_value) 46 | { 47 | if(add_value >= 0) res_display.append(L"+"); 48 | res_display.append(std::to_wstring(add_value)); 49 | result += add_value; 50 | } 51 | } 52 | 53 | std::wstring fudge_calculator::form_string() { 54 | std::wstring str; 55 | std::wstring dice_expression; 56 | dice_expression.append(std::to_wstring(dice_count)); 57 | dice_expression.append(L"DF"); 58 | if (add_value) 59 | { 60 | if(add_value >= 0) dice_expression.append(L"+"); 61 | dice_expression.append(std::to_wstring(add_value)); 62 | } 63 | str.append(dice_expression); 64 | if (dice_expression != res_display) { 65 | str += L"=" + res_display; 66 | } 67 | std::wstring res = std::to_wstring(result); 68 | res = res.substr(0, std::max(res.find(L'.'), std::min(res.find(L'.') + 3, res.find_last_not_of(L"0.") + 1))); 69 | if (res != res_display) { 70 | str += L"=" + res; 71 | } 72 | if (str.length() > 160) str = dice_expression + L"=" + res; 73 | if (dice_count == 4 && !add_value) 74 | { 75 | str.append(L" "); 76 | switch(int(result)) 77 | { 78 | case -3: 79 | str.append(L"{strFudgeTerrible}"); 80 | break; 81 | case -2: 82 | str.append(L"{strFudgePoor}"); 83 | break; 84 | case -1: 85 | str.append(L"{strFudgeMediocre}"); 86 | break; 87 | case 0: 88 | str.append(L"{strFudgeFair}"); 89 | break; 90 | case 1: 91 | str.append(L"{strFudgeGood}"); 92 | break; 93 | case 2: 94 | str.append(L"{strFudgeGreat}"); 95 | break; 96 | case 3: 97 | str.append(L"{strFudgeSuperb}"); 98 | break; 99 | } 100 | } 101 | return str; 102 | } 103 | } // namespace dice 104 | -------------------------------------------------------------------------------- /src/fudge_calculator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace dice { 8 | class fudge_calculator { 9 | public: 10 | int dice_count = 4; 11 | int add_value = 0; 12 | 13 | // 过程显示1 14 | std::wstring res_display; 15 | 16 | double result = 0.0; 17 | 18 | std::wstring form_string(); 19 | fudge_calculator(std::wstring dice_count, std::wstring add_value); 20 | fudge_calculator(int dice_count, int add_value); 21 | 22 | static std::mt19937 ran; 23 | 24 | private: 25 | void main_calculate(); 26 | size_t get_number_size(size_t current_pos); 27 | }; 28 | } // namespace dice -------------------------------------------------------------------------------- /vcpkg-requirements.txt: -------------------------------------------------------------------------------- 1 | sqlite3 2 | sqlitecpp 3 | boost-algorithm 4 | libiconv 5 | openssl 6 | brotli 7 | zlib 8 | cpprestsdk 9 | nlohmann-json --------------------------------------------------------------------------------