├── .gitignore ├── LICENSE ├── README.md ├── include ├── CQSDK │ ├── CQAPI.h │ ├── CQAPI_EX.h │ ├── CQEVE.h │ ├── CQEVEBasic.h │ ├── CQEVEMsg.h │ ├── CQEVERequest.h │ ├── CQEVE_ALL.h │ ├── CQEVE_DiscussMsg.h │ ├── CQEVE_FriendAdd.h │ ├── CQEVE_GroupMsg.h │ ├── CQEVE_PrivateMsg.h │ ├── CQEVE_RequestAddFriend.h │ ├── CQEVE_RequestAddGroup.h │ ├── CQEVE_Status.h │ ├── CQLogger.h │ ├── CQMsgCode.h │ ├── CQMsgSend.h │ ├── CQTools.h │ ├── CQconstant.h │ ├── CQface.h │ ├── Unpack.h │ ├── bufstream.h │ └── cqdefine.h ├── SQLite │ └── sqlite3.h └── SQLiteCpp │ ├── Assertion.h │ ├── Backup.h │ ├── Column.h │ ├── Database.h │ ├── Exception.h │ ├── ExecuteMany.h │ ├── SQLiteCpp.h │ ├── Statement.h │ ├── Transaction.h │ ├── Utils.h │ └── VariadicBind.h ├── src ├── CQSDK │ ├── CQAPI.cpp │ ├── CQAPI_EX.cpp │ ├── CQEVE.cpp │ ├── CQTools.cpp │ ├── CQstream.cpp │ └── Unpack.cpp ├── SQLite │ └── sqlite3.c └── SQLiteCpp │ ├── Backup.cpp │ ├── Column.cpp │ ├── Database.cpp │ ├── Exception.cpp │ ├── Statement.cpp │ └── Transaction.cpp ├── trpglogger.sln ├── trpglogger.sln.DotSettings.user └── trpglogger ├── APPINFO.h ├── CQP.lib ├── EncodingConvert.cpp ├── EncodingConvert.h ├── GlobalVar.cpp ├── GlobalVar.h ├── MsgType.h ├── S3PutObject.cpp ├── S3PutObject.h ├── SaveLog.cpp ├── SaveLog.h ├── com.w4123.trpglogger.json ├── dllmain.cpp ├── main.cpp ├── packages.config ├── trpglogger.vcxproj ├── trpglogger.vcxproj.filters └── trpglogger.vcxproj.user /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | /trpglogger/x64/Debug 34 | /Release 35 | /trpglogger/Release 36 | /.vs/trpglogger/v15 37 | /packages 38 | /Debug 39 | /trpglogger/Debug 40 | /.vs/trpglogger/v16 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrpgLogger 2 | QQ跑团记录记录器 3 | 4 | 用来记录跑团记录的酷Q插件 与 https://logpainter.kokona.tech 配合使用 5 | 6 | # 编译 7 | 请使用 Visual Studio 2015及以上进行编译, 编译前需要先还原nuget软件包 8 | 9 | # Q&A 10 | Q: 上传时遇到网络错误? 11 | A: 因为使用的是AWS S3存储,在墙外,所以服务器在墙内的话可能有时候连接不太稳定 12 | 13 | # 协议 14 | 本软件采用AGPLv3协议 15 | 16 | TrpgLogger Logger for TRPG 17 | Copyright (C) 2019 w4123溯洄 18 | 19 | This program is free software: you can redistribute it and/or modify 20 | it under the terms of the GNU Affero General Public License as 21 | published by the Free Software Foundation, either version 3 of the 22 | License, or (at your option) any later version. 23 | 24 | This program is distributed in the hope that it will be useful, 25 | but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | GNU Affero General Public License for more details. 28 | 29 | You should have received a copy of the GNU Affero General Public License 30 | along with this program. If not, see . 31 | 32 | # 开源软件许可协议 33 | 本软件中使用了以下开源软件包: 34 | 35 | 1. SQLite https://www.sqlite.org Public Domain 36 | 37 | 2. SQLitecpp https://github.com/SRombauts/SQLiteCpp MIT 38 | 39 | Copyright (c) 2012-2019 Sébastien Rombauts (sebastien.rombauts@gmail.com) 40 | 41 | 3. CQSDK https://github.com/MikuPy2001/CQSDK GPLv3 42 | 43 | Copyright (c) Mikupy2001 2018-2019 44 | 45 | 4. aws-sdk-cpp https://github.com/aws/aws-sdk-cpp Apache 2.0 46 | 47 | Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. 48 | -------------------------------------------------------------------------------- /include/CQSDK/CQAPI.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoolQ SDK for VS2017 3 | Api Version 9.13 4 | Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs 5 | */ 6 | #pragma once 7 | #include "cqdefine.h" 8 | 9 | #ifdef _MSC_VER 10 | #define CQAPI(NAME,ReturnType) extern "C" __declspec(dllimport) ReturnType __stdcall NAME // NOLINT 11 | #else 12 | #define CQAPI(NAME,ReturnType) extern "C" __attribute__((dllimport)) ReturnType __attribute__((__stdcall__)) NAME // NOLINT 13 | #endif /*_MSC_VER*/ 14 | 15 | 16 | namespace CQ 17 | { 18 | // 获取调用api所需的AuthCode 19 | int getAuthCode(); 20 | //发送好友消息 21 | //Auth=106 失败返回负值,成功返回消息ID 22 | CQAPI(CQ_sendPrivateMsg, int)( 23 | int AuthCode, // 24 | long long QQID, // 目标QQ 25 | const char* msg // 消息内容 26 | ); 27 | //发送群消息 28 | //Auth=101 失败返回负值,成功返回消息ID 29 | CQAPI(CQ_sendGroupMsg, int)( 30 | int AuthCode, // 31 | long long GroupID, // 目标群 32 | const char* msg // 消息内容 33 | ); 34 | //发送讨论组消息 35 | //Auth=103 失败返回负值,成功返回消息ID 36 | CQAPI(CQ_sendDiscussMsg, int)( 37 | int AuthCode, // 38 | long long DiscussID, // 目标讨论组 39 | const char* msg // 消息内容 40 | ); 41 | //发送赞 Auth=110 42 | CQAPI(CQ_sendLike, int)( 43 | int AuthCode, // 44 | long long QQID // 目标QQ 45 | ); 46 | //发送赞V2 Auth=110 47 | CQAPI(CQ_sendLikeV2, int)( 48 | int AuthCode, // 49 | long long QQID, // 目标QQ 50 | int times // 赞的次数,最多10次 51 | ); 52 | //接收语音 53 | CQAPI(CQ_getRecord, const char *)( 54 | int AuthCode, // 55 | const char* file, // 收到消息中的语音文件名 (file) 56 | const char* outformat // 应用所需的格式 mp3,amr,wma,m4a,spx,ogg,wav,flac 57 | ); 58 | //取CsrfToken (慎用,此接口需要严格授权) 59 | //Auth=20 即QQ网页用到的bkn/g_tk等 慎用,此接口需要严格授权 60 | CQAPI(CQ_getCsrfToken, int)( 61 | int AuthCode // 62 | ); 63 | //取应用目录 64 | //返回的路径末尾带"\" 65 | CQAPI(CQ_getAppDirectory, const char *)( 66 | int AuthCode // 67 | ); 68 | //取登录QQ 69 | CQAPI(CQ_getLoginQQ, long long)( 70 | int AuthCode // 71 | ); 72 | //取登录昵称 73 | CQAPI(CQ_getLoginNick, const char *)( 74 | int AuthCode // 75 | ); 76 | //置群员移除 Auth=120 77 | CQAPI(CQ_setGroupKick, int)( 78 | int AuthCode, // 79 | long long GroupID, // 目标群 80 | long long QQID, // 目标QQ 81 | CQBOOL RefuseForever // 如果为真,则“不再接收此人加群申请”,请慎用 82 | ); 83 | //置群员禁言 Auth=121 84 | CQAPI(CQ_setGroupBan, int)( 85 | int AuthCode, // 86 | long long GroupID, // 目标群 87 | long long QQID, // 目标QQ 88 | long long Time // 禁言的时间,单位为秒。如果要解禁,这里填写0 89 | ); 90 | //置群管理员 Auth=122 91 | CQAPI(CQ_setGroupAdmin, int)( 92 | int AuthCode, // 93 | long long GroupID, // 目标群 94 | long long QQID, // 被设置的QQ 95 | CQBOOL setAdmin // 真/设置管理员 假/取消管理员 96 | ); 97 | //置群成员专属头衔 Auth=128 需群主权限 98 | CQAPI(CQ_setGroupSpecialTitle, int)( 99 | int AuthCode, // 100 | long long GroupID, // 目标群 101 | long long QQID, // 目标QQ 102 | const char* Title, // 如果要删除,这里填空 103 | long long ExpireTime // 专属头衔有效期,单位为秒。如果永久有效,这里填写-1 104 | ); 105 | //置全群禁言 Auth=123 106 | CQAPI(CQ_setGroupWholeBan, int)( 107 | int AuthCode, // 108 | long long GroupID, // 目标群 109 | CQBOOL EnableWholeBan // 真/开启 假/关闭 110 | ); 111 | //置匿名群员禁言 Auth=124 112 | CQAPI(CQ_setGroupAnonymousBan, int)( 113 | int AuthCode, // 114 | long long GroupID, // 目标群 115 | const char* AnonymousID, // 群消息事件收到的“匿名”参数 116 | long long time // 禁言的时间,单位为秒。不支持解禁 117 | ); 118 | //置群匿名设置 Auth=125 119 | CQAPI(CQ_setGroupAnonymous, int)( 120 | int AuthCode, // 121 | long long GroupID, // 122 | CQBOOL EnableAnonymous // 123 | ); 124 | //置群成员名片 Auth=126 125 | CQAPI(CQ_setGroupCard, int)( 126 | int AuthCode, // 127 | long long GroupID, // 目标群 128 | long long QQID, // 被设置的QQ 129 | const char* NewGroupCarkNick // 130 | ); 131 | //置群退出 Auth=127 慎用,此接口需要严格授权 132 | CQAPI(CQ_setGroupLeave, int)( 133 | int AuthCode, // 134 | long long GroupID, // 目标群 135 | CQBOOL isDismiss // 真/解散本群 (群主) 假/退出本群 (管理、群成员) 136 | ); 137 | //置讨论组退出 Auth=140 138 | CQAPI(CQ_setDiscussLeave, int)( 139 | int AuthCode, // 140 | long long DiscussID // 目标讨论组 141 | ); 142 | //置好友添加请求 Auth=150 143 | CQAPI(CQ_setFriendAddRequest, int)( 144 | int AuthCode, // 145 | const char* ResponseToken, // 请求事件收到的“反馈标识”参数 146 | int ResponseType, // #请求_通过 或 #请求_拒绝 147 | const char* Remarks // 添加后的好友备注 148 | ); 149 | //置群添加请求 Auth=151 150 | CQAPI(CQ_setGroupAddRequest, int)( 151 | int AuthCode, // 152 | const char* ResponseToken, // 请求事件收到的“反馈标识”参数 153 | int RequestType, // 根据请求事件的子类型区分 #请求_群添加 或 #请求_群邀请 154 | int ResponseType // #请求_通过 或 #请求_拒绝 155 | ); 156 | //置群添加请求 Auth=151 157 | CQAPI(CQ_setGroupAddRequestV2, int)( 158 | int AuthCode, // 159 | const char* RequestToken, // 请求事件收到的“反馈标识”参数 160 | int RequestType, // 根据请求事件的子类型区分 #请求_群添加 或 #请求_群邀请 161 | int ResponseType, // #请求_通过 或 #请求_拒绝 162 | const char* Reason // 操作理由,仅 #请求_群添加 且 #请求_拒绝 时可用 163 | ); 164 | //增加运行日志 165 | CQAPI(CQ_addLog, int)( 166 | int AuthCode, // 167 | int Priorty, // #Log_ 开头常量 168 | const char* Type, // 169 | const char* Content // 170 | ); 171 | //置致命错误提示 172 | CQAPI(CQ_setFatal, int)( 173 | int AuthCode, // 174 | const char* ErrorMsg // 175 | ); 176 | //取群成员信息 (旧版,请用CQ_getGroupMemberInfoV2) Auth=130 177 | CQAPI(CQ_getGroupMemberInfo, const char *)( 178 | int AuthCode, // 179 | long long GroupID, // 目标QQ所在群 180 | long long QQID // 目标QQ 181 | ); 182 | //取群成员信息 (支持缓存) Auth=130 183 | CQAPI(CQ_getGroupMemberInfoV2, const char *)( 184 | int AuthCode, // 185 | long long GroupID, // 目标QQ所在群 186 | long long QQID, // 目标QQ 187 | CQBOOL DisableCache 188 | ); 189 | //取陌生人信息 (支持缓存) Auth=131 190 | CQAPI(CQ_getStrangerInfo, const char *)( 191 | int AuthCode, // 192 | long long QQID, // 目标QQ 193 | CQBOOL DisableCache 194 | ); 195 | //取群成员列表 Auth=160 196 | CQAPI(CQ_getGroupMemberList, const char *)( 197 | int AuthCode, // 198 | long long GroupID // 目标QQ所在群 199 | ); 200 | //取群列表 Auth=161 201 | CQAPI(CQ_getGroupList, const char *)( 202 | int AuthCode 203 | ); 204 | //撤回消息 Auth=180 205 | CQAPI(CQ_deleteMsg, int)( 206 | int AuthCode, 207 | long long MsgId 208 | ); 209 | } 210 | -------------------------------------------------------------------------------- /include/CQSDK/CQAPI_EX.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cqdefine.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class Unpack; 10 | 11 | namespace CQ 12 | { 13 | //增加运行日志 14 | int addLog(int Priorty, const char* Type, const char* Content); 15 | 16 | //发送好友消息 17 | //Auth=106 失败返回负值,成功返回消息ID 18 | int sendPrivateMsg(long long QQ, const char* msg); 19 | //发送好友消息 20 | //Auth=106 失败返回负值,成功返回消息ID 21 | int sendPrivateMsg(long long QQ, const std::string& msg); 22 | 23 | //发送群消息 24 | //Auth=101 失败返回负值,成功返回消息ID 25 | int sendGroupMsg(long long GroupID, const char* msg); 26 | //发送群消息 27 | //Auth=101 失败返回负值,成功返回消息ID 28 | int sendGroupMsg(long long GroupID, const std::string& msg); 29 | 30 | 31 | //发送讨论组消息 32 | //Auth=103 失败返回负值,成功返回消息ID 33 | int sendDiscussMsg(long long DiscussID, const char* msg); 34 | //发送讨论组消息 35 | //Auth=103 失败返回负值,成功返回消息ID 36 | int sendDiscussMsg(long long DiscussID, const std::string& msg); 37 | 38 | //发送赞 Auth=110 39 | int sendLike(long long QQID, int times); 40 | 41 | //取Cookies (慎用,此接口需要严格授权) 42 | //Auth=20 43 | const char* getCookies(); 44 | 45 | //接收语音 46 | const char* getRecord( 47 | const char* file, // 收到消息中的语音文件名 (file) 48 | const char* outformat // 应用所需的格式 mp3,amr,wma,m4a,spx,ogg,wav,flac 49 | ); 50 | //接收语音 51 | std::string getRecord( 52 | const std::string& file, // 收到消息中的语音文件名 (file) 53 | const std::string& outformat // 应用所需的格式 mp3,amr,wma,m4a,spx,ogg,wav,flac 54 | ); 55 | 56 | //取CsrfToken (慎用,此接口需要严格授权) 57 | //Auth=20 即QQ网页用到的bkn/g_tk等 58 | int getCsrfToken(); 59 | 60 | //取应用目录 61 | //返回的路径末尾带"\" 62 | const char* getAppDirectory(); 63 | 64 | //取登录QQ 65 | long long getLoginQQ(); 66 | 67 | //取登录昵称 68 | const char* getLoginNick(); 69 | 70 | //置群员移除 Auth=120 71 | int setGroupKick( 72 | long long GroupID, long long QQID, 73 | CQBOOL RefuseForever = false // 如果为真,则“不再接收此人加群申请”,请慎用 74 | ); 75 | 76 | //置群员禁言 Auth=121 77 | int setGroupBan( 78 | long long GroupID, long long QQID, 79 | long long Time = 60 // 禁言的时间,单位为秒。如果要解禁,这里填写0 80 | ); 81 | 82 | //置群管理员 Auth=122 83 | int setGroupAdmin( 84 | long long GroupID, long long QQID, 85 | CQBOOL isAdmin = true // 真/设置管理员 假/取消管理员 86 | ); 87 | 88 | //置群成员专属头衔 Auth=128 需群主权限 89 | int setGroupSpecialTitle( 90 | long long GroupID, long long QQID, 91 | const char* Title, // 如果要删除,这里填空 92 | long long ExpireTime = -1 // 专属头衔有效期,单位为秒。如果永久有效,这里填写-1 93 | ); 94 | //置群成员专属头衔 Auth=128 需群主权限 95 | int setGroupSpecialTitle( 96 | long long GroupID, long long QQID, 97 | const std::string& Title, // 如果要删除,这里填空 98 | long long ExpireTime = -1 // 专属头衔有效期,单位为秒。如果永久有效,这里填写-1 99 | ); 100 | 101 | //置全群禁言 Auth=123 102 | int setGroupWholeBan( 103 | long long GroupID, 104 | CQBOOL isBan = true // 真/开启 假/关闭 105 | ); 106 | 107 | //置匿名群员禁言 Auth=124 108 | int setGroupAnonymousBan( 109 | long long GroupID, 110 | const char* AnonymousToken, // 群消息事件收到的“匿名”参数 111 | long long banTime = 60 // 禁言的时间,单位为秒。不支持解禁 112 | ); 113 | 114 | //置群匿名设置 Auth=125 115 | int setGroupAnonymous(long long GroupID, CQBOOL enableAnonymous = true); 116 | 117 | //置群成员名片 Auth=126 118 | int setGroupCard(long long GroupID, long long QQID, const char* newGroupNick); 119 | 120 | //置群成员名片 Auth=126 121 | int setGroupCard(long long GroupID, long long QQID, const std::string& newGroupNick); 122 | 123 | //置群退出 Auth=127 慎用,此接口需要严格授权 124 | int setGroupLeave( 125 | long long GroupID, 126 | CQBOOL isDismiss = false // 真/解散本群 (群主) 假/退出本群 (管理、群成员) 127 | ); 128 | 129 | //置讨论组退出 Auth=140 130 | int setDiscussLeave( 131 | long long DiscussID 132 | ); 133 | 134 | //置好友添加请求 Auth=150 135 | int setFriendAddRequest( 136 | const char* RequestToken, // 请求事件收到的“反馈标识”参数 137 | int ReturnType, // #请求_通过 或 #请求_拒绝 138 | const char* Remarks // 添加后的好友备注 139 | ); 140 | 141 | //置群添加请求 Auth=151 142 | int setGroupAddRequest( 143 | const char* RequestToken, // 请求事件收到的“反馈标识”参数 144 | int RequestType, // 根据请求事件的子类型区分 #请求_群添加 或 #请求_群邀请 145 | int ReturnType, // #请求_通过 或 #请求_拒绝 146 | const char* Reason // 操作理由,仅 #请求_群添加 且 #请求_拒绝 时可用 147 | ); 148 | 149 | //置致命错误提示,暂时不知道干什么用的 150 | int setFatal(const char* ErrorMsg); 151 | 152 | 153 | class GroupMemberInfo; 154 | //取群成员信息 (支持缓存) Auth=130 155 | GroupMemberInfo getGroupMemberInfo(long long GroupID, long long QQID, CQBOOL DisableCache = false); 156 | 157 | class StrangerInfo; 158 | //取陌生人信息 (支持缓存) Auth=131 159 | StrangerInfo getStrangerInfo(long long QQID, CQBOOL DisableCache = false); 160 | 161 | //取群成员列表 Auth=160 162 | std::vector getGroupMemberList(long long GroupID); 163 | 164 | //取群列表 Auth=161 165 | std::map getGroupList(); 166 | 167 | //撤回消息 Auth=180 168 | int deleteMsg(long long MsgId); 169 | 170 | const char* getlasterrmsg(); 171 | 172 | // 群成员信息 173 | class GroupMemberInfo final 174 | { 175 | void setdata(Unpack& u); 176 | public: 177 | long long Group{}; 178 | long long QQID{}; 179 | std::string Nick{}; 180 | std::string GroupNick{}; 181 | int Gender{}; // 0/男性 1/女性 182 | int Age{}; 183 | std::string Region{}; 184 | int AddGroupTime{}; 185 | int LastMsgTime{}; 186 | std::string LevelName{}; 187 | int permissions{}; //1/成员 2/管理员 3/群主 188 | std::string Title{}; 189 | int ExpireTime{}; // -1 代表不过期 190 | CQBOOL NaughtyRecord{}; 191 | CQBOOL canEditGroupNick{}; 192 | 193 | explicit GroupMemberInfo(Unpack& msg); 194 | explicit GroupMemberInfo(const char* msg); //从API解码 195 | explicit GroupMemberInfo(std::vector data); //从Unpack解码 196 | GroupMemberInfo() = default; 197 | 198 | std::string tostring() const; 199 | }; 200 | 201 | // 陌生人信息 202 | class StrangerInfo final 203 | { 204 | public: 205 | long long QQID = 0; 206 | std::string nick = ""; //昵称 207 | int sex = 255; //0/男性 1/女性 255/未知 208 | int age = -1; //年龄 209 | 210 | explicit StrangerInfo(const char* msg); 211 | StrangerInfo() = default; 212 | 213 | std::string tostring() const; 214 | }; 215 | } 216 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoolQ SDK for VS2017 3 | Api Version 9.10 4 | Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs 5 | */ 6 | #pragma once 7 | #ifdef _MSC_VER 8 | #define CQEVENT(ReturnType, Name, Size) __pragma(comment(linker, "/EXPORT:" #Name "=_" #Name "@" #Size))\ 9 | extern "C" __declspec(dllexport) ReturnType __stdcall Name 10 | #else 11 | #define CQEVENT(ReturnType, Name, Size)\ 12 | extern "C" __attribute__((dllexport)) ReturnType __attribute__((__stdcall__)) Name 13 | #endif /*_MSC_VER*/ 14 | /* 15 | 返回应用的ApiVer、Appid,打包后将不会调用 16 | */ 17 | #define MUST_AppInfo CQEVENT(const char*, AppInfo, 0)() 18 | 19 | /* 20 | 返回应用的ApiVer、Appid,打包后将不会调用 21 | */ 22 | #define MUST_AppInfo_RETURN(CQAPPID) MUST_AppInfo{return CQAPIVERTEXT "," CQAPPID;} 23 | 24 | /* 25 | 本宏已失效,请使用 getAuthCode(); 直接获取, 此函数在CQAPI.h中 26 | 27 | 应用AuthCode接收 28 | 29 | 请保存 AuthCode ,此值是调用CQAPI的凭证 30 | 31 | 请不要在本函数添加其他代码 32 | */ 33 | //#define MUST_Initialize CQEVENT(int, Initialize, 4)(int AuthCode) 34 | 35 | 36 | ///////////////////////////////// 事件 相关内容 ///////////////////////////////// 37 | //extern "C" __declspec(dllexport) void __stdcall Int32(int a){}//@4 38 | //extern "C" __declspec(dllexport) void __stdcall Char(const char* a){}//@4 39 | //extern "C" __declspec(dllexport) void __stdcall Int64(long long a){}//@8 40 | 41 | /* 42 | 酷Q启动(Type=1001) 43 | 44 | 本子程序会在酷Q【主线程】中被调用。 45 | 无论本应用是否被启用,本函数都会在酷Q启动后执行一次,请在这里执行插件初始化代码。 46 | 请务必尽快返回本子程序,否则会卡住其他插件以及主程序的加载。 47 | 请固定返回 0 48 | 49 | 名字如果使用下划线开头需要改成双下划线 50 | */ 51 | #define EVE_Startup(Name) CQEVENT(int, Name, 0)() 52 | 53 | /* 54 | 酷Q退出(Type=1002) 55 | 56 | 本子程序会在酷Q【主线程】中被调用。 57 | 无论本应用是否被启用,本函数都会在酷Q退出前执行一次,请在这里执行插件关闭代码。 58 | 本函数调用完毕后,酷Q将很快关闭,请不要再通过线程等方式执行其他代码 59 | 60 | 名字如果使用下划线开头需要改成双下划线 61 | 返回非零值,消息将被拦截,最高优先不可拦截 62 | */ 63 | #define EVE_Exit(Name) CQEVENT(int, Name, 0)() 64 | 65 | /* 66 | 应用已被启用(Type=1003) 67 | 68 | 当应用被启用后,将收到此事件。 69 | 如果酷Q载入时应用已被启用,则在 EVE_Startup(Type=1001,酷Q启动) 被调用后,本函数也将被调用一次。 70 | 如非必要,不建议在这里加载窗口。(可以添加菜单,让用户手动打开窗口) 71 | 72 | 名字如果使用下划线开头需要改成双下划线 73 | 返回非零值,消息将被拦截,最高优先不可拦截 74 | */ 75 | #define EVE_Enable(Name) CQEVENT(int, Name, 0)() 76 | 77 | /* 78 | 应用将被停用(Type=1004) 79 | 80 | 当应用被停用前,将收到此事件。 81 | 如果酷Q载入时应用已被停用,则本函数【不会】被调用。 82 | 无论本应用是否被启用,酷Q关闭前本函数都【不会】被调用。 83 | 84 | 名字如果使用下划线开头需要改成双下划线 85 | 返回非零值,消息将被拦截,最高优先不可拦截 86 | */ 87 | #define EVE_Disable(Name) CQEVENT(int, Name, 0)() 88 | 89 | /* 90 | 私聊消息(Type=21) 91 | 92 | 此函数具有以下参数 93 | subType 子类型,11/来自好友 1/来自在线状态 2/来自群 3/来自讨论组 94 | msgId 消息ID 95 | fromQQ 来源QQ 96 | msg 消息内容 97 | font 字体 98 | 99 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 100 | 名字如果使用下划线开头需要改成双下划线 101 | 返回非零值,消息将被拦截,最高优先不可拦截 102 | */ 103 | #define EVE_PrivateMsg(Name) CQEVENT(int, Name, 24)(int subType, int msgId, long long fromQQ, const char* msg, int font) 104 | 105 | /* 106 | 群消息(Type=2) 107 | 108 | subType 子类型,目前固定为1 109 | msgId 消息ID 110 | fromGroup 来源群号 111 | fromQQ 来源QQ号 112 | fromAnonymous 来源匿名者 113 | msg 消息内容 114 | font 字体 115 | 116 | 如果消息来自匿名者,fromQQ 固定为 80000000,可使用工具将 fromAnonymous 转换为 匿名者信息 117 | 118 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 119 | 名字如果使用下划线开头需要改成双下划线 120 | 返回非零值,消息将被拦截,最高优先不可拦截 121 | */ 122 | #define EVE_GroupMsg(Name) CQEVENT(int, Name, 36)(int subType, int msgId, long long fromGroup, long long fromQQ, const char* fromAnonymous, const char* msg, int font) 123 | 124 | /* 125 | 讨论组消息(Type=4) 126 | 127 | subtype 子类型,目前固定为1 128 | msgId 消息ID 129 | fromDiscuss 来源讨论组 130 | fromQQ 来源QQ号 131 | msg 消息内容 132 | font 字体 133 | 134 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 135 | 名字如果使用下划线开头需要改成双下划线 136 | 返回非零值,消息将被拦截,最高优先不可拦截 137 | */ 138 | #define EVE_DiscussMsg(Name) CQEVENT(int, Name, 32)(int subType, int msgId, long long fromDiscuss, long long fromQQ, const char* msg, int font) 139 | 140 | /* 141 | 群文件上传事件(Type=11) 142 | 143 | subType 子类型,目前固定为1 144 | sendTime 发送时间(时间戳) 145 | fromGroup 来源群号 146 | fromQQ 来源QQ号 147 | file 上传文件信息,使用 <其他_转换_文本到群文件> 将本参数转换为有效数据,待编辑 148 | 149 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 150 | 名字如果使用下划线开头需要改成双下划线 151 | 返回非零值,消息将被拦截,最高优先不可拦截 152 | */ 153 | #define EVE_GroupUpload(Name) CQEVENT(int, Name, 28)(int subType, int sendTime, long long fromGroup,long long fromQQ, const char* file) 154 | /*增强的版本,待补充 155 | #define EVE_GroupUpload_EX(Name) CQEVENT(int, Name, 28)(int subType, int sendTime, long long fromGroup,long long fromQQ, const char* file)\ 156 | {\ 157 | int Name(int subType, int sendTime, long long fromGroup,long long fromQQ, File file);\ 158 | return Name(subType,sendTime,fromGroup,fromQQ,ToFile(file));\ 159 | }\ 160 | int Name(int subType, int sendTime, long long fromGroup,long long fromQQ, File file) 161 | */ 162 | 163 | /* 164 | 群事件-管理员变动(Type=101) 165 | 166 | subtype 子类型,1/被取消管理员 2/被设置管理员 167 | sendTime 发送时间(时间戳) 168 | fromGroup 来源群号 169 | beingOperateQQ 被操作QQ 170 | 171 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 172 | 名字如果使用下划线开头需要改成双下划线 173 | 返回非零值,消息将被拦截,最高优先不可拦截 174 | */ 175 | #define EVE_System_GroupAdmin(Name) CQEVENT(int, Name, 24)(int subType, int msgId, long long fromGroup, long long beingOperateQQ) 176 | 177 | /* 178 | 群事件-群成员减少(Type=102) 179 | 180 | subtype 子类型,1/群员离开 2/群员被踢 3/自己(即登录号)被踢 181 | sendTime 发送时间(时间戳) 182 | fromGroup 来源群号 183 | fromQQ 操作者QQ(仅子类型为2、3时存在) 184 | beingOperateQQ 被操作QQ 185 | 186 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 187 | 名字如果使用下划线开头需要改成双下划线 188 | 返回非零值,消息将被拦截,最高优先不可拦截 189 | */ 190 | #define EVE_System_GroupMemberDecrease(Name) CQEVENT(int, Name, 32)(int subType, int msgId, long long fromGroup, long long fromQQ, long long beingOperateQQ) 191 | 192 | /* 193 | 群事件-群成员增加(Type=103) 194 | 195 | subtype 子类型,1/管理员已同意 2/管理员邀请 196 | sendTime 发送时间(时间戳) 197 | fromGroup 来源群号 198 | fromQQ 操作者QQ(即管理员QQ) 199 | beingOperateQQ 被操作QQ(即加群的QQ) 200 | 201 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 202 | 名字如果使用下划线开头需要改成双下划线 203 | 返回非零值,消息将被拦截,最高优先不可拦截 204 | */ 205 | #define EVE_System_GroupMemberIncrease(Name) CQEVENT(int, Name, 32)(int subType, int msgId, long long fromGroup, long long fromQQ, long long beingOperateQQ) 206 | 207 | /* 208 | 好友事件-好友已添加(Type=201) 209 | 210 | subtype 子类型,目前固定为1 211 | sendTime 发送时间(时间戳) 212 | fromQQ 来源QQ 213 | 214 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 215 | 名字如果使用下划线开头需要改成双下划线 216 | 返回非零值,消息将被拦截,最高优先不可拦截 217 | */ 218 | #define EVE_Friend_Add(Name) CQEVENT(int, Name, 16)(int subType, int msgId, long long fromQQ) 219 | 220 | /* 221 | 请求-好友添加(Type=301) 222 | 223 | subtype 子类型,目前固定为1 224 | sendTime 发送时间(时间戳) 225 | fromQQ 来源QQ 226 | msg 附言 227 | responseFlag 反馈标识(处理请求用) 228 | 229 | 置好友添加请求 (responseFlag, #请求_通过) 230 | 231 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 232 | 名字如果使用下划线开头需要改成双下划线 233 | 返回非零值,消息将被拦截,最高优先不可拦截 234 | */ 235 | #define EVE_Request_AddFriend(Name) CQEVENT(int, Name, 24)(int subType, int msgId, long long fromQQ, const char* msg, const char* responseFlag) 236 | 237 | /* 238 | 请求-群添加(Type=302) 239 | 240 | subtype 子类型,1/他人申请入群 2/自己(即登录号)受邀入群 241 | sendTime 发送时间(时间戳) 242 | fromGroup 来源群号 243 | fromQQ 来源QQ 244 | msg 附言 245 | responseFlag 反馈标识(处理请求用) 246 | 247 | 如果 subtype = 1 248 | 置群添加请求 (responseFlag, #请求_群添加, #请求_通过) 249 | 如果 subtype = 2 250 | 置群添加请求 (responseFlag, #请求_群邀请, #请求_通过) 251 | 252 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 253 | 名字如果使用下划线开头需要改成双下划线 254 | 返回非零值,消息将被拦截,最高优先不可拦截 255 | */ 256 | #define EVE_Request_AddGroup(Name) CQEVENT(int, Name, 32)(int subType, int sendTime, long long fromGroup, long long fromQQ, const char* msg, const char* responseFlag) 257 | 258 | /* 259 | 菜单 260 | 261 | 可在 .json 文件中设置菜单数目、函数名 262 | 如果不使用菜单,请在 .json 及此处删除无用菜单 263 | 名字如果使用下划线开头需要改成双下划线 264 | 返回非零值,消息将被拦截,最高优先不可拦截 265 | */ 266 | #define EVE_Menu(Name) CQEVENT(int, Name, 0)() 267 | 268 | /* 269 | 悬浮窗 270 | 271 | 请使用EX版本 272 | emmm,因为一些原因,悬浮窗暂时不可用... 273 | 274 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize)。 275 | 名字如果使用下划线开头需要改成双下划线 276 | */ 277 | #define EVE_Status(Name) CQEVENT(const char*, Name, 0)() 278 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVEBasic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CQconstant.h" 4 | 5 | namespace CQ 6 | { 7 | // 事件基类 8 | struct EVE 9 | { 10 | //不对消息做任何动作 11 | //如果之前拦截了消息,这里将重新放行本条消息 12 | void message_ignore(); 13 | //拦截本条消息 14 | void message_block(); 15 | 16 | int _EVEret = Msg_Ignored; 17 | 18 | virtual ~EVE() 19 | { 20 | } 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVEMsg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CQEVEBasic.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace CQ 8 | { 9 | // 字体 10 | struct Font final 11 | { 12 | const char* Name{}; 13 | int Size{}, 14 | Color{}, 15 | /*粗体:1 斜体:2 下划线:4*/ 16 | Style{}, 17 | Bubble{}; 18 | 19 | explicit Font(int); 20 | }; 21 | 22 | //正则消息 23 | class regexMsg final 24 | { 25 | //消息 26 | std::map regexMap{}; 27 | public: 28 | regexMsg(const std::string& msg); 29 | std::string get(const std::string&); 30 | std::string operator [](const std::string&); 31 | }; 32 | 33 | class msg; 34 | 35 | //消息事件基类 36 | struct EVEMsg : EVE 37 | { 38 | //子类型 39 | int subType; 40 | //消息ID 41 | int msgId; 42 | //来源QQ 43 | long long fromQQ; 44 | //消息 45 | std::string message; 46 | //字体 47 | Font font; 48 | 49 | EVEMsg(int subType, int msgId, long long fromQQ, std::string message, int font); 50 | 51 | //真实用户 52 | bool isUser() const; 53 | //是否是系统用户 54 | bool isSystem() const; 55 | 56 | virtual int sendMsg(const char*) const = 0; 57 | virtual int sendMsg(const std::string&) const = 0; 58 | virtual msg sendMsg() const = 0; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVERequest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "CQEVEBasic.h" 4 | 5 | namespace CQ 6 | { 7 | struct EVERequest : EVE 8 | { 9 | int sendTime; // 发送时间(时间戳) 10 | long long fromQQ; // 来源QQ 11 | const char* msg; // 附言 12 | const char* responseFlag; // 反馈标识(处理请求用) 13 | 14 | EVERequest(int sendTime, long long fromQQ, const char* msg, const char* responseFlag); 15 | virtual void pass(const std::string& msg) const = 0; //通过此请求 16 | virtual void fail(const std::string& msg) const = 0; //拒绝此请求 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_ALL.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CQEVE.h" 4 | #include "CQEVEBasic.h" 5 | 6 | #include "CQEVEMsg.h" 7 | #include "CQEVERequest.h" 8 | #include "CQAPI_EX.h" 9 | #include "CQEVE_Status.h" 10 | #include "CQEVE_GroupMsg.h" 11 | #include "CQEVE_PrivateMsg.h" 12 | #include "CQEVE_DiscussMsg.h" 13 | #include "CQEVE_RequestAddFriend.h" 14 | #include "CQEVE_RequestAddGroup.h" 15 | #include "CQEVE_FriendAdd.h" 16 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_DiscussMsg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CQMsgSend.h" 4 | 5 | /* 6 | 讨论组消息(Type=4) 7 | 8 | subtype 子类型,目前固定为1 9 | msgId 消息ID 10 | fromDiscuss 来源讨论组 11 | fromQQ 来源QQ号 12 | msg 消息内容 13 | font 字体 14 | 15 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 16 | 名字如果使用下划线开头需要改成双下划线 17 | 返回非零值,消息将被拦截,最高优先不可拦截 18 | */ 19 | #define EVE_DiscussMsg_EX(Name) \ 20 | void Name(CQ::EVEDiscussMsg & eve); \ 21 | EVE_DiscussMsg(Name) \ 22 | { \ 23 | CQ::EVEDiscussMsg tep(subType, msgId, fromDiscuss, fromQQ, msg, font); \ 24 | Name(tep);\ 25 | return tep._EVEret; \ 26 | } \ 27 | void Name(CQ::EVEDiscussMsg & eve) 28 | 29 | 30 | namespace CQ 31 | { 32 | struct EVEDiscussMsg : EVEMsg 33 | { 34 | long long fromDiscuss; //讨论组号 35 | 36 | EVEDiscussMsg(int subType, int msgId, long long fromDiscuss, long long fromQQ, const char* msg, int font); 37 | 38 | bool leave() const; //退出讨论组 39 | 40 | // 通过 EVEMsg 继承 41 | msg sendMsg() const override; 42 | int sendMsg(const char*) const override; 43 | int sendMsg(const std::string&) const override; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_FriendAdd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CQEVEBasic.h" 3 | 4 | /* 5 | 好友事件-好友已添加(Type=201) 6 | 7 | subtype 子类型,目前固定为1 8 | msgId 消息ID 9 | fromQQ 来源QQ 10 | 11 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 12 | 名字如果使用下划线开头需要改成双下划线 13 | 返回非零值,消息将被拦截,最高优先不可拦截 14 | */ 15 | #define EVE_Friend_Add_EX(Name) \ 16 | void Name(CQ::EVERequestAddFriend & eve);\ 17 | EVE_Friend_Add(Name)\ 18 | {\ 19 | CQ::EVEFriendAdd tep(subType, msgId, fromGroup, fromQQ, msg, responseFlag);\ 20 | Name(tep);\ 21 | return tep._EVEret;\ 22 | }\ 23 | void Name(CQ::EVEFriendAdd & eve) 24 | 25 | namespace CQ 26 | { 27 | struct EVEFriendAdd final : EVE 28 | { 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_GroupMsg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CQEVEMsg.h" 3 | 4 | #include 5 | /* 6 | 群消息(Type=2) 7 | 8 | subType 子类型,目前固定为1 9 | msgId 消息ID 10 | fromGroup 来源群号 11 | fromQQ 来源QQ号 12 | fromAnonymous 来源匿名者 13 | msg 消息内容 14 | font 字体 15 | 16 | 如果消息来自匿名者,isAnonymous() 返回 true, 可使用 getFromAnonymousInfo() 获取 匿名者信息 17 | 18 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 19 | 名字如果使用下划线开头需要改成双下划线 20 | 返回非零值,消息将被拦截,最高优先不可拦截 21 | */ 22 | #define EVE_GroupMsg_EX(Name) \ 23 | void Name(CQ::EVEGroupMsg & eve); \ 24 | EVE_GroupMsg(Name) \ 25 | { \ 26 | CQ::EVEGroupMsg tep(subType, msgId, fromGroup, fromQQ, fromAnonymous, msg, font); \ 27 | Name(tep); \ 28 | return tep._EVEret; \ 29 | } \ 30 | void Name(CQ::EVEGroupMsg & eve) 31 | 32 | namespace CQ 33 | { 34 | class GroupMemberInfo; 35 | 36 | // 群匿名信息 37 | struct AnonymousInfo final 38 | { 39 | long long AID = 0; 40 | std::string AnonymousNick = ""; 41 | 42 | explicit AnonymousInfo(const char* msg); 43 | AnonymousInfo() = default; 44 | }; 45 | 46 | //群事件 47 | struct EVEGroupMsg final : EVEMsg 48 | { 49 | private: 50 | AnonymousInfo* fromAnonymousInfo; 51 | public: 52 | //群号 53 | long long fromGroup; 54 | //禁言用的令牌 55 | const char* fromAnonymousToken; 56 | EVEGroupMsg(int subType, int msgId, long long fromGroup, long long fromQQ, const char* fromAnonymous, 57 | const char* msg, int font); 58 | 59 | virtual ~EVEGroupMsg(); 60 | 61 | bool isAnonymous() const; 62 | 63 | // 通过 EVEMsg 继承 64 | int sendMsg(const char*) const override; 65 | int sendMsg(const std::string&) const override; 66 | msg sendMsg() const override; 67 | 68 | //获取匿名者信息 69 | AnonymousInfo& getFromAnonymousInfo() /*throw(std::exception_ptr)*/; 70 | 71 | //置群员移除 72 | bool setGroupKick(bool refusedAddAgain = false) const; 73 | //置群员禁言 74 | //自动判断是否是匿名 75 | bool setGroupBan(long long banTime = 60) const; 76 | //置群管理员 77 | bool setGroupAdmin(bool isAdmin) const; 78 | //置群成员专属头衔 79 | bool setGroupSpecialTitle(const std::string& Title, long long ExpireTime = -1) const; 80 | 81 | //置全群禁言 82 | bool setGroupWholeBan(bool enableBan = true) const; 83 | //置群匿名设置 84 | bool setGroupAnonymous(bool enableAnonymous) const; 85 | //置群成员名片 86 | bool setGroupCard(const std::string& newGroupNick) const; 87 | //置群退出 88 | bool setGroupLeave(bool isDismiss) const; 89 | //取群成员信息 (支持缓存) 90 | GroupMemberInfo getGroupMemberInfo(bool disableCache = false) const; 91 | //取群成员列表 92 | std::vector getGroupMemberList() const; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_PrivateMsg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CQMsgSend.h" 3 | 4 | /* 5 | 私聊消息(Type=21) 6 | 7 | 此函数具有以下参数 8 | subType 子类型,11/来自好友 1/来自在线状态 2/来自群 3/来自讨论组 9 | msgId 消息ID 10 | fromQQ 来源QQ 11 | msg 消息内容 12 | font 字体 13 | 14 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 15 | 名字如果使用下划线开头需要改成双下划线 16 | 返回非零值,消息将被拦截,最高优先不可拦截 17 | */ 18 | #define EVE_PrivateMsg_EX(Name) \ 19 | void Name(CQ::EVEPrivateMsg & eve); \ 20 | EVE_PrivateMsg(Name) \ 21 | { \ 22 | CQ::EVEPrivateMsg tep(subType, msgId, fromQQ, msg, font); \ 23 | Name(tep); \ 24 | return tep._EVEret; \ 25 | } \ 26 | void Name(CQ::EVEPrivateMsg & eve) 27 | 28 | 29 | namespace CQ 30 | { 31 | struct EVEPrivateMsg final : EVEMsg 32 | { 33 | EVEPrivateMsg(int subType, int msgId, long long fromQQ, const char* msg, int font); 34 | 35 | //来自好友 36 | bool fromPrivate() const; 37 | 38 | //来自在线状态 39 | bool fromOnlineStatus() const; 40 | 41 | //来自群临时 42 | bool fromGroup() const; 43 | 44 | //来自讨论组临时 45 | bool fromDiscuss() const; 46 | 47 | // 通过 EVEMsg 继承 48 | msg sendMsg() const override; 49 | 50 | int sendMsg(const char*) const override; 51 | 52 | int sendMsg(const std::string&) const override; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_RequestAddFriend.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "CQEVERequest.h" 4 | 5 | /* 6 | 请求-好友添加(Type=301) 7 | 8 | subtype 子类型,目前固定为1 9 | sendTime 发送时间(时间戳) 10 | fromQQ 来源QQ 11 | msg 附言 12 | responseFlag 反馈标识(处理请求用) 13 | 14 | 置好友添加请求 (responseFlag, #请求_通过) 15 | 16 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 17 | 名字如果使用下划线开头需要改成双下划线 18 | 返回非零值,消息将被拦截,最高优先不可拦截 19 | */ 20 | #define EVE_Request_AddFriend_EX(Name) \ 21 | void Name(CQ::EVERequestAddFriend & eve);\ 22 | EVE_Request_AddFriend(Name)\ 23 | {\ 24 | CQ::EVERequestAddFriend tep(subType, sendTime, fromGroup, fromQQ, msg, responseFlag);\ 25 | Name(tep);\ 26 | return tep._EVEret;\ 27 | }\ 28 | void Name(CQ::EVERequestAddFriend & eve) 29 | 30 | namespace CQ 31 | { 32 | struct EVERequestAddFriend final : EVERequest 33 | { 34 | //子类型 35 | //1:固定为1 36 | int subType; 37 | long long fromGroup; // 来源群号 38 | EVERequestAddFriend(int subType, int sendTime, long long fromQQ, const char* msg, const char* responseFlag); 39 | void pass(const std::string& msg = "") const override; //通过此请求 40 | void fail(const std::string& msg = "您由于不满足某些要求被拒绝!") const override; //拒绝此请求 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_RequestAddGroup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "CQEVERequest.h" 4 | 5 | /* 6 | 请求-群添加(Type=302) 7 | 8 | subtype 子类型,1/他人申请入群 2/自己(即登录号)受邀入群 9 | sendTime 发送时间(时间戳) 10 | fromGroup 来源群号 11 | fromQQ 来源QQ 12 | msg 附言 13 | responseFlag 反馈标识(处理请求用) 14 | 15 | 如果 subtype = 1 16 | 置群添加请求 (responseFlag, #请求_群添加, #请求_通过) 17 | 如果 subtype = 2 18 | 置群添加请求 (responseFlag, #请求_群邀请, #请求_通过) 19 | 20 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize) 21 | 名字如果使用下划线开头需要改成双下划线 22 | 返回非零值,消息将被拦截,最高优先不可拦截 23 | */ 24 | #define EVE_Request_AddGroup_EX(Name) \ 25 | void Name(CQ::EVERequestAddGroup & eve);\ 26 | EVE_Request_AddGroup(Name)\ 27 | {\ 28 | CQ::EVERequestAddGroup tep(subType, sendTime, fromGroup, fromQQ, msg, responseFlag);\ 29 | Name(tep);\ 30 | return tep._EVEret;\ 31 | }\ 32 | void Name(CQ::EVERequestAddGroup & eve) 33 | 34 | namespace CQ 35 | { 36 | struct EVERequestAddGroup final : EVERequest 37 | { 38 | //子类型 39 | //1:他人申请入群 40 | //2:自己(即登录号)受邀入群 41 | int subType; 42 | long long fromGroup; // 来源群号 43 | EVERequestAddGroup(int subType, int sendTime, long long fromGroup, long long fromQQ, const char* msg, 44 | const char* responseFlag); 45 | void pass(const std::string& msg = "") const override; //通过此请求 46 | void fail(const std::string& msg = "您由于不满足某些要求被拒绝!") const override; //拒绝此请求 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /include/CQSDK/CQEVE_Status.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | /* 5 | 悬浮窗 6 | 7 | 请设置 eve.data eve.dataf eve.color 其他函数保持不动即可 8 | 9 | 本子程序会在酷Q【线程】中被调用,请注意使用对象等需要初始化(CoInitialize,CoUninitialize)。 10 | 名字如果使用下划线开头需要改成双下划线 11 | */ 12 | 13 | #define EVE_Status_EX(Name) \ 14 | void Name(CQ::EVEStatus & eve);\ 15 | EVE_Status(Name)\ 16 | {\ 17 | CQ::EVEStatus tep;\ 18 | Name(tep);\ 19 | static std::string ret;\ 20 | ret = CQ::statusEVEreturn(tep);\ 21 | return ret.c_str();\ 22 | }\ 23 | void Name(CQ::EVEStatus & eve) 24 | 25 | namespace CQ 26 | { 27 | struct EVEStatus final 28 | { 29 | std::string 30 | //数据 31 | data, 32 | //数据单位 33 | dataf; 34 | int 35 | // 1 : 绿 36 | // 2 : 橙 37 | // 3 : 红 38 | // 4 : 深红 39 | // 5 : 黑 40 | // 6 : 灰 41 | color; 42 | // 1 : 绿 43 | void color_green(); 44 | // 2 : 橙 45 | void color_orange(); 46 | // 3 : 红 47 | void color_red(); 48 | // 4 : 深红 49 | void color_crimson(); 50 | // 5 : 黑 51 | void color_black(); 52 | // 6 : 灰 53 | void color_gray(); 54 | }; 55 | 56 | std::string statusEVEreturn(EVEStatus& eve); 57 | } 58 | -------------------------------------------------------------------------------- /include/CQSDK/CQLogger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bufstream.h" 3 | #include 4 | 5 | #define DEBUGINFO "文件:" << __FILE__ << ",行数:" << __LINE__ << ",输出:" 6 | 7 | namespace CQ 8 | { 9 | class logstream : public CQstream 10 | { 11 | int flag; 12 | std::string title; 13 | public: 14 | logstream(std::string title, int Log_flag); 15 | 16 | // 通过 CQstream 继承 17 | void send() override; 18 | }; 19 | 20 | class logger 21 | { 22 | std::string title; 23 | public: 24 | logger(std::string title); 25 | void setTitle(std::string title); 26 | 27 | void Debug(const std::string& msg) const; 28 | void Info(const std::string& msg) const; 29 | void InfoSuccess(const std::string& msg) const; 30 | void InfoRecv(const std::string& msg) const; 31 | void InfoSend(const std::string& msg) const; 32 | void Warning(const std::string& msg) const; 33 | void Error(const std::string& msg) const; 34 | void Fatal(const std::string& msg) const; 35 | 36 | void Debug(const char* msg) const; 37 | void Info(const char* msg) const; 38 | void InfoSuccess(const char* msg) const; 39 | void InfoRecv(const char* msg) const; 40 | void InfoSend(const char* msg) const; 41 | void Warning(const char* msg) const; 42 | void Error(const char* msg) const; 43 | void Fatal(const char* msg) const; 44 | 45 | logstream Debug() const; 46 | logstream Info() const; 47 | logstream InfoSuccess() const; 48 | logstream InfoRecv() const; 49 | logstream InfoSend() const; 50 | logstream Warning() const; 51 | logstream Error() const; 52 | logstream Fatal() const; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /include/CQSDK/CQMsgCode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CQface.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | //#include 9 | 10 | namespace CQ { 11 | //cq消息参数 12 | struct OneCodeMsg { size_t key, keylen = 0, value = 0; OneCodeMsg(size_t key); }; 13 | //一条cq消息或者普通消息 14 | struct CodeMsg : std::vector { public: bool isCode; size_t key, keylen = 0; CodeMsg(bool isCode, size_t key); }; 15 | 16 | class CodeMsgs; 17 | struct CodeMsgsFor { 18 | CodeMsgs&t; 19 | size_t pos; 20 | CQ::CodeMsgs&operator*(); 21 | CQ::CodeMsgsFor&operator++(); 22 | bool operator!=(CQ::CodeMsgsFor&); 23 | CodeMsgsFor(CodeMsgs&t, int pos); 24 | }; 25 | //消息解析 26 | class CodeMsgs { 27 | std::vector msglist; 28 | std::string txt; 29 | size_t thismsg=0;//指针 30 | void decod();//解码 31 | bool find(std::string &s,int); 32 | bool is(std::string &s, int); 33 | public: 34 | CodeMsgs(std::string); 35 | 36 | //char* at(int); 37 | 38 | //定位到指定段 39 | CQ::CodeMsgs&operator[](size_t); 40 | CQ::CodeMsgs&operator++(int); 41 | CQ::CodeMsgs&operator++(); 42 | CQ::CodeMsgs&operator--(int); 43 | CQ::CodeMsgs&operator--(); 44 | CQ::CodeMsgs&operator-(size_t); 45 | CQ::CodeMsgs&operator+(size_t); 46 | //返回指针当前位置 47 | int pos(); 48 | 49 | //从当前位置开始搜索指定cq码 50 | //如果存在,定位到指定段 51 | //否则返回null,并且不会移动指针 52 | bool find(std::string s); 53 | 54 | //从当前位置开始反向搜索指定cq码 55 | //如果存在,定位到指定段 56 | //否则返回null,并且不会移动指针 57 | bool lastfind(std::string); 58 | 59 | 60 | //判断是否是CQ码 61 | bool isCQcode(); 62 | 63 | //判断是否为指定CQ码 64 | bool is(std::string); 65 | 66 | //如果是CQ码,返回CQ码类型 67 | //如果不是,返回消息 68 | std::string get(); 69 | 70 | //如果是CQ码,返回键对应的值 71 | //如果找不到键,则返回空字符 72 | //如果不是,返回空字符 73 | std::string get(std::string key); 74 | std::vector keys(); 75 | 76 | CQ::CodeMsgsFor begin(); 77 | CQ::CodeMsgsFor end(); 78 | 79 | void debug(); 80 | 81 | }; 82 | 83 | struct code { 84 | //[CQ:image,file={1}] - 发送自定义图片 85 | //文件以 酷Q目录\data\image\ 为基础 86 | static std::string image(std::string fileurl); 87 | 88 | //[CQ:record,file={1},magic={2}] - 发送语音 89 | //文件以 酷Q目录\data\record\ 为基础 90 | static std::string record(std::string fileurl, bool magic); 91 | 92 | //[CQ:face,id={1}] - QQ表情 93 | static std::string face(int faceid); 94 | 95 | //[CQ:face,id={1}] - QQ表情 96 | //static std::string face(CQ::face face); 97 | 98 | //[CQ:at,qq={1}] - @某人 99 | static std::string at(long long QQ); 100 | 101 | //[CQ:effect,type=art,id=2003,content=小吉] - 魔法字体 102 | static std::string effect(std::string type, int id, std::string content); 103 | 104 | //[CQ:sign,title=晒幸福,image=http://pub.idqqimg.com/pc/misc/files/20170825/cc9103d0db0b4dcbb7a17554d227f4d7.jpg] - 签到 105 | 106 | //[CQ:hb, title = 恭喜发财] - 红包(只限收,不能发) 107 | 108 | //[CQ:shake, id = 1] - 戳一戳(原窗口抖动,仅支持好友消息使用) 109 | 110 | //[CQ:sface,id={1}] - 小表情 111 | 112 | //[CQ:bface,id={1}] - 原创表情 113 | 114 | //[CQ:emoji,id={1}] - emoji表情 115 | 116 | //[CQ:rps,type={1}] - 发送猜拳魔法表情 117 | //发送不支持自定义type 118 | //1 为 石头 119 | //2 为 剪刀 120 | //3 为 布 121 | 122 | //[CQ:dice,type={1}] - 发送掷骰子魔法表情 123 | //发送不支持自定义type 124 | //type为骰子点数 125 | 126 | //[CQ:anonymous,ignore={1}] - 匿名发消息(仅支持群消息使用) 127 | //必须在消息最开头 128 | //ignore为true时,如果发送失败则转为普通消息 129 | 130 | //[CQ:music,type={1},id={2}] - 发送音乐 131 | //type为音乐平台,支持qq、163、xiami 132 | //id即为音乐id 133 | 134 | //[CQ:music,type=custom,url={1},audio={2},title={3},content={4},image={5}] - 发送音乐自定义分享 135 | //url为分享链接,即点击分享后进入的音乐页面(如歌曲介绍页) 136 | //audio为音频链接(如mp3链接) 137 | //title为音乐的标题,建议12字以内 138 | //content为音乐的简介,建议30字以内。该参数可被忽略 139 | //image为音乐的封面图片链接。若参数为空或被忽略,则显示默认图片 140 | //!音乐自定义分享只能作为单独的一条消息发送 141 | 142 | //[CQ:share,url={1},title={2},content={3},image={4}] - 发送链接分享 143 | //url为分享链接。 144 | //title为分享的标题,建议12字以内。 145 | //content为分享的简介,建议30字以内。该参数可被忽略。 146 | //image为分享的图片链接。若参数为空或被忽略,则显示默认图片。 147 | //!链接分享只能作为单独的一条消息发送 148 | 149 | }; 150 | } -------------------------------------------------------------------------------- /include/CQSDK/CQMsgSend.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bufstream.h" 3 | 4 | namespace CQ 5 | { 6 | enum msgtype { Friend, Group, Discuss }; 7 | 8 | class msg final : public CQstream 9 | { 10 | long long ID; 11 | int subType = 0; 12 | 13 | public: 14 | /* 15 | Type: 16 | 0=msgtype::好友 17 | 1=msgtype::群 18 | 2=msgtype::讨论组 19 | */ 20 | msg(long long GroupID_Or_QQID, msgtype Type); 21 | /* 22 | Type: 23 | 0=好友 24 | 1=群 25 | 2=讨论组 26 | */ 27 | msg(long long GroupID_Or_QQID, int Type); 28 | 29 | void send() override; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /include/CQSDK/CQTools.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | //base64编码 5 | std::string base64_encode(const std::string& decode_string); 6 | 7 | //base64解码 8 | std::string base64_decode(const std::string& encoded_string); 9 | 10 | //替换 11 | std::string& msg_replace(std::string& s, const std::string& old, const std::string& n); 12 | 13 | //CQcode编码 14 | std::string& msg_encode(std::string& s, bool isCQ = false); 15 | 16 | //CQcode解码 17 | std::string& msg_decode(std::string& s, bool isCQ = false); 18 | 19 | -------------------------------------------------------------------------------- /include/CQSDK/CQconstant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 调试 灰色 4 | #define Log_Debug 0 5 | // 信息 黑色 6 | #define Log_Info 10 7 | // 信息(成功) 紫色 8 | #define Log_InfoSuccess 11 9 | // 信息(接收) 蓝色 10 | #define Log_InfoRecv 12 11 | // 信息(发送) 绿色 12 | #define Log_InfoSend 13 13 | // 警告 橙色 14 | #define Log_Warning 20 15 | // 错误 红色 16 | #define Log_Error 30 17 | // 致命错误 深红 18 | #define Log_Fatal 40 19 | 20 | // 拦截此条消息,不再传递给其他应用 21 | //注意:应用优先级设置为最高(10000)时,不得使用本返回值 22 | #define Msg_Blocked 1 23 | // 将此消息继续传递给其他应用 24 | #define Msg_Ignored 0 25 | 26 | #define FloatingWindows_Green 1 27 | #define FloatingWindows_Orange 2 28 | #define FloatingWindows_Red 3 29 | #define FloatingWindows_DeepRed 4 30 | #define FloatingWindows_Black 5 31 | #define FloatingWindows_Grey 6 32 | 33 | #define RequestAccepted 1 34 | #define RequestRefused 2 35 | 36 | #define RequestGroupAdd 1 37 | #define RequestGroupInvite 2 38 | -------------------------------------------------------------------------------- /include/CQSDK/CQface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | namespace CQ { 4 | enum face { 5 | 惊讶 = 0, 6 | 撇嘴 = 1, 7 | 色 = 2, 8 | 发呆 = 3, 9 | 得意 = 4, 10 | 流泪 = 5, 11 | 害羞 = 6, 12 | 闭嘴 = 7, 13 | 睡 = 8, 14 | 大哭 = 9, 15 | 尴尬 = 10, 16 | 发怒 = 11, 17 | 调皮 = 12, 18 | 呲牙 = 13, 19 | 微笑 = 14, 20 | 难过 = 15, 21 | 酷 = 16, 22 | 抓狂 = 18, 23 | 吐 = 19, 24 | 偷笑 = 20, 25 | 可爱 = 21, 26 | 白眼 = 22, 27 | 傲慢 = 23, 28 | 饥饿 = 24, 29 | 困 = 25, 30 | 惊恐 = 26, 31 | 流汗 = 27, 32 | 憨笑 = 28, 33 | 大兵 = 29, 34 | 奋斗 = 30, 35 | 咒骂 = 31, 36 | 疑问 = 32, 37 | 晕 = 34, 38 | 折磨 = 35, 39 | 衰 = 36, 40 | 骷髅 = 37, 41 | 敲打 = 38, 42 | 再见 = 39, 43 | 发抖 = 41, 44 | 爱情 = 42, 45 | 跳跳 = 43, 46 | 猪头 = 46, 47 | 拥抱 = 49, 48 | 蛋糕 = 53, 49 | 闪电 = 54, 50 | 炸弹 = 55, 51 | 刀 = 56, 52 | 足球 = 57, 53 | 便便 = 59, 54 | 咖啡 = 60, 55 | 饭 = 61, 56 | 玫瑰 = 63, 57 | 凋谢 = 64, 58 | 爱心 = 66, 59 | 心碎 = 67, 60 | 礼物 = 69, 61 | 太阳 = 74, 62 | 月亮 = 75, 63 | 强 = 76, 64 | 弱 = 77, 65 | 握手 = 78, 66 | 胜利 = 79, 67 | 飞吻 = 85, 68 | 怄火 = 86, 69 | 西瓜 = 89, 70 | 冷汗 = 96, 71 | 擦汗 = 97, 72 | 抠鼻 = 98, 73 | 鼓掌 = 99, 74 | 糗大了 = 100, 75 | 坏笑 = 101, 76 | 左哼哼 = 102, 77 | 右哼哼 = 103, 78 | 哈欠 = 104, 79 | 鄙视 = 105, 80 | 委屈 = 106, 81 | 快哭了 = 107, 82 | 阴险 = 108, 83 | 亲亲 = 109, 84 | 吓 = 110, 85 | 可怜 = 111, 86 | 菜刀 = 112, 87 | 啤酒 = 113, 88 | 篮球 = 114, 89 | 乒乓 = 115, 90 | 示爱 = 116, 91 | 瓢虫 = 117, 92 | 抱拳 = 118, 93 | 勾引 = 119, 94 | 拳头 = 120, 95 | 差劲 = 121, 96 | 爱你 = 122, 97 | 不 = 123, 98 | 好 = 124, 99 | 转圈 = 125, 100 | 磕头 = 126, 101 | 回头 = 127, 102 | 跳绳 = 128, 103 | 挥手 = 129, 104 | 激动 = 130, 105 | 街舞 = 131, 106 | 献吻 = 132, 107 | 左太极 = 133, 108 | 右太极 = 134, 109 | 双喜 = 136, 110 | 鞭炮 = 137, 111 | 灯笼 = 138, 112 | 发财 = 139, 113 | K歌 = 140, 114 | 购物 = 141, 115 | 邮件 = 142, 116 | 帅 = 143, 117 | 喝彩 = 144, 118 | 祈祷 = 145, 119 | 爆筋 = 146, 120 | 棒棒糖 = 147, 121 | 喝奶 = 148, 122 | 下面 = 149, 123 | 香蕉 = 150, 124 | 飞机 = 151, 125 | 开车 = 152, 126 | 高铁左车头 = 153, 127 | 车厢 = 154, 128 | 高铁右车头 = 155, 129 | 多云 = 156, 130 | 下雨 = 157, 131 | 钞票 = 158, 132 | 熊猫 = 159, 133 | 灯泡 = 160, 134 | 风车 = 161, 135 | 闹钟 = 162, 136 | 打伞 = 163, 137 | 彩球 = 164, 138 | 钻戒 = 165, 139 | 沙发 = 166, 140 | 纸巾 = 167, 141 | 药 = 168, 142 | 手枪 = 169, 143 | 青蛙 = 170, 144 | }; 145 | } 146 | */ -------------------------------------------------------------------------------- /include/CQSDK/Unpack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | void show(void* t, int len); 6 | 7 | class Unpack final 8 | { 9 | std::vector buff; 10 | public: 11 | Unpack(); 12 | explicit Unpack(const char*); 13 | explicit Unpack(std::vector); 14 | explicit Unpack(const std::string&); 15 | 16 | Unpack& setData(const char* i, int len); 17 | Unpack& clear(); 18 | int len() const; 19 | 20 | Unpack& add(int i); //添加一个整数 21 | int getInt(); //弹出一个整数 22 | 23 | Unpack& add(long long i); //添加一个长整数 24 | long long getLong(); //弹出一个长整数 25 | 26 | Unpack& add(short i); //添加一个短整数 27 | short getshort(); //弹出一个短整数 28 | 29 | Unpack& add(const unsigned char* i, short len); //添加一个字节集(请用add(std::string i);) 30 | std::vector getchars(); //弹出一个字节集(请用getstring();) 31 | 32 | Unpack& add(std::string i); //添加一个字符串 33 | std::string getstring(); //弹出一个字符串 34 | 35 | Unpack& add(Unpack& i); //添加一个Unpack 36 | Unpack getUnpack(); //弹出一个Unpack 37 | 38 | std::string getAll(); //返回本包数据 39 | 40 | void show(); 41 | }; 42 | -------------------------------------------------------------------------------- /include/CQSDK/bufstream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace CQ 5 | { 6 | class CQstream 7 | { 8 | protected: 9 | std::string buf; 10 | public: 11 | virtual void clear(); 12 | 13 | //字符串 14 | virtual CQstream& append(const std::string& s); 15 | virtual CQstream& operator <<(const std::string& s); 16 | // 17 | virtual CQstream& append(const char* c); 18 | virtual CQstream& operator <<(const char* c); 19 | 20 | //整数 21 | virtual CQstream& append(const int& i); 22 | virtual CQstream& operator <<(const int& i); 23 | virtual CQstream& append(const std::size_t& i); 24 | virtual CQstream& operator <<(const std::size_t& i); 25 | 26 | //长整数(Q号什么的) 27 | virtual CQstream& append(const long long& l); 28 | virtual CQstream& operator <<(const long long& l); 29 | 30 | 31 | //特殊控制符 32 | virtual CQstream& operator <<(void (*control)(CQstream&)); 33 | virtual void send() = 0; 34 | virtual void flush(); 35 | 36 | virtual ~CQstream(); 37 | }; 38 | 39 | //发送并清除缓冲区 40 | void send(CQstream& log); 41 | //只发送,保留缓冲区,下次发送时将发送重复内容 42 | void flush(CQstream& log); 43 | void endl(CQstream& log); 44 | } 45 | -------------------------------------------------------------------------------- /include/CQSDK/cqdefine.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoolQ SDK for VS2017 3 | Api Version 9.10 4 | Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs 5 | */ 6 | #pragma once 7 | 8 | 9 | #define CQAPIVER 9 10 | #define CQAPIVERTEXT "9" 11 | 12 | typedef int CQBOOL; 13 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Assertion.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Assertion.h 3 | * @ingroup SQLiteCpp 4 | * @brief Definition of the SQLITECPP_ASSERT() macro. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | 15 | 16 | /** 17 | * SQLITECPP_ASSERT SQLITECPP_ASSERT() is used in destructors, where exceptions shall not be thrown 18 | * 19 | * Define SQLITECPP_ENABLE_ASSERT_HANDLER at the project level 20 | * and define a SQLite::assertion_failed() assertion handler 21 | * to tell SQLiteC++ to use it instead of assert() when an assertion fail. 22 | */ 23 | #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER 24 | 25 | // if an assert handler is provided by user code, use it instead of assert() 26 | namespace SQLite 27 | { 28 | // declaration of the assert handler to define in user code 29 | void assertion_failed(const char* apFile, const long apLine, const char* apFunc, 30 | const char* apExpr, const char* apMsg); 31 | 32 | #ifdef _MSC_VER 33 | #define __func__ __FUNCTION__ 34 | #endif 35 | // call the assert handler provided by user code 36 | #define SQLITECPP_ASSERT(expression, message) \ 37 | if (!(expression)) SQLite::assertion_failed(__FILE__, __LINE__, __func__, #expression, message) 38 | } // namespace SQLite 39 | 40 | #else 41 | 42 | // if no assert handler provided by user code, use standard assert() 43 | // (note: in release mode assert() does nothing) 44 | #define SQLITECPP_ASSERT(expression, message) assert(expression && message) 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Backup.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Backup.h 3 | * @ingroup SQLiteCpp 4 | * @brief Backup is used to backup a database file in a safe and online way. 5 | * 6 | * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) 7 | * Copyright (c) 2015-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | #pragma once 13 | 14 | #include 15 | 16 | #include 17 | 18 | // Forward declaration to avoid inclusion of in a header 19 | struct sqlite3_backup; 20 | 21 | namespace SQLite 22 | { 23 | 24 | /** 25 | * @brief RAII encapsulation of a SQLite Database Backup process. 26 | * 27 | * A Backup object is used to backup a source database file to a destination database file 28 | * in a safe and online way. 29 | * 30 | * See also the a reference implementation of live backup taken from the official site: 31 | * https://www.sqlite.org/backup.html 32 | */ 33 | class Backup 34 | { 35 | public: 36 | /** 37 | * @brief Initialize a SQLite Backup object. 38 | * 39 | * Initialize a SQLite Backup object for the source database and destination database. 40 | * The database name is "main" for the main database, "temp" for the temporary database, 41 | * or the name specified after the AS keyword in an ATTACH statement for an attached database. 42 | * 43 | * Exception is thrown in case of error, then the Backup object is NOT constructed. 44 | * 45 | * @param[in] aDestDatabase Destination database connection 46 | * @param[in] apDestDatabaseName Destination database name 47 | * @param[in] aSrcDatabase Source database connection 48 | * @param[in] apSrcDatabaseName Source database name 49 | * 50 | * @throw SQLite::Exception in case of error 51 | */ 52 | Backup(Database& aDestDatabase, 53 | const char* apDestDatabaseName, 54 | Database& aSrcDatabase, 55 | const char* apSrcDatabaseName); 56 | 57 | /** 58 | * @brief Initialize a SQLite Backup object. 59 | * 60 | * Initialize a SQLite Backup object for source database and destination database. 61 | * The database name is "main" for the main database, "temp" for the temporary database, 62 | * or the name specified after the AS keyword in an ATTACH statement for an attached database. 63 | * 64 | * Exception is thrown in case of error, then the Backup object is NOT constructed. 65 | * 66 | * @param[in] aDestDatabase Destination database connection 67 | * @param[in] aDestDatabaseName Destination database name 68 | * @param[in] aSrcDatabase Source database connection 69 | * @param[in] aSrcDatabaseName Source database name 70 | * 71 | * @throw SQLite::Exception in case of error 72 | */ 73 | Backup(Database& aDestDatabase, 74 | const std::string& aDestDatabaseName, 75 | Database& aSrcDatabase, 76 | const std::string& aSrcDatabaseName); 77 | 78 | /** 79 | * @brief Initialize a SQLite Backup object for main databases. 80 | * 81 | * Initialize a SQLite Backup object for source database and destination database. 82 | * Backup the main databases between the source and the destination. 83 | * 84 | * Exception is thrown in case of error, then the Backup object is NOT constructed. 85 | * 86 | * @param[in] aDestDatabase Destination database connection 87 | * @param[in] aSrcDatabase Source database connection 88 | * 89 | * @throw SQLite::Exception in case of error 90 | */ 91 | Backup(Database& aDestDatabase, 92 | Database& aSrcDatabase); 93 | 94 | // Backup is non-copyable 95 | Backup(const Backup&) = delete; 96 | Backup& operator=(const Backup&) = delete; 97 | 98 | /// Release the SQLite Backup resource. 99 | ~Backup(); 100 | 101 | /** 102 | * @brief Execute a step of backup with a given number of source pages to be copied 103 | * 104 | * Exception is thrown when SQLITE_IOERR_XXX, SQLITE_NOMEM, or SQLITE_READONLY is returned 105 | * in sqlite3_backup_step(). These errors are considered fatal, so there is no point 106 | * in retrying the call to executeStep(). 107 | * 108 | * @param[in] aNumPage The number of source pages to be copied, with a negative value meaning all remaining source pages 109 | * 110 | * @return SQLITE_OK/SQLITE_DONE/SQLITE_BUSY/SQLITE_LOCKED 111 | * 112 | * @throw SQLite::Exception in case of error 113 | */ 114 | int executeStep(const int aNumPage = -1); 115 | 116 | /// Return the number of source pages still to be backed up as of the most recent call to executeStep(). 117 | int getRemainingPageCount(); 118 | 119 | /// Return the total number of pages in the source database as of the most recent call to executeStep(). 120 | int getTotalPageCount(); 121 | 122 | private: 123 | // TODO: use std::unique_ptr with a custom deleter to call sqlite3_backup_finish() 124 | sqlite3_backup* mpSQLiteBackup = nullptr; ///< Pointer to SQLite Database Backup Handle 125 | }; 126 | 127 | } // namespace SQLite 128 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Column.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Column.h 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include // For INT_MAX 18 | 19 | 20 | namespace SQLite 21 | { 22 | 23 | extern const int INTEGER; ///< SQLITE_INTEGER 24 | extern const int FLOAT; ///< SQLITE_FLOAT 25 | extern const int TEXT; ///< SQLITE_TEXT 26 | extern const int BLOB; ///< SQLITE_BLOB 27 | extern const int Null; ///< SQLITE_NULL 28 | 29 | 30 | /** 31 | * @brief Encapsulation of a Column in a row of the result pointed by the prepared Statement. 32 | * 33 | * A Column is a particular field of SQLite data in the current row of result 34 | * of the Statement : it points to a single cell. 35 | * 36 | * Its value can be expressed as a text, and, when applicable, as a numeric 37 | * (integer or floating point) or a binary blob. 38 | * 39 | * Thread-safety: a Column object shall not be shared by multiple threads, because : 40 | * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 41 | * provided that no single database connection is used simultaneously in two or more threads." 42 | * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 43 | * because of the way it shares the underling SQLite precompiled statement 44 | * in a custom shared pointer (See the inner class "Statement::Ptr"). 45 | */ 46 | class Column 47 | { 48 | public: 49 | /** 50 | * @brief Encapsulation of a Column in a Row of the result. 51 | * 52 | * @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object. 53 | * @param[in] aIndex Index of the column in the row of result, starting at 0 54 | */ 55 | Column(Statement::Ptr& aStmtPtr, int aIndex) noexcept; 56 | 57 | // default destructor: the finalization will be done by the destructor of the last shared pointer 58 | // default copy constructor and assignment operator are perfectly suited : 59 | // they copy the Statement::Ptr which in turn increments the reference counter. 60 | 61 | /** 62 | * @brief Return a pointer to the named assigned to this result column (potentially aliased) 63 | * 64 | * @see getOriginName() to get original column name (not aliased) 65 | */ 66 | const char* getName() const noexcept; 67 | 68 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 69 | /** 70 | * @brief Return a pointer to the table column name that is the origin of this result column 71 | * 72 | * Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro : 73 | * - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance), 74 | * - and also when compiling this wrapper. 75 | */ 76 | const char* getOriginName() const noexcept; 77 | #endif 78 | 79 | /// Return the integer value of the column. 80 | int getInt() const noexcept; 81 | /// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits). 82 | unsigned getUInt() const noexcept; 83 | /// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits). 84 | long long getInt64() const noexcept; 85 | /// Return the double (64bits float) value of the column 86 | double getDouble() const noexcept; 87 | /** 88 | * @brief Return a pointer to the text value (NULL terminated string) of the column. 89 | * 90 | * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), 91 | * thus you must copy it before using it beyond its scope (to a std::string for instance). 92 | */ 93 | const char* getText(const char* apDefaultValue = "") const noexcept; 94 | /** 95 | * @brief Return a pointer to the binary blob value of the column. 96 | * 97 | * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), 98 | * thus you must copy it before using it beyond its scope (to a std::string for instance). 99 | */ 100 | const void* getBlob() const noexcept; 101 | /** 102 | * @brief Return a std::string for a TEXT or BLOB column. 103 | * 104 | * Note this correctly handles strings that contain null bytes. 105 | */ 106 | std::string getString() const; 107 | 108 | /** 109 | * @brief Return the type of the value of the column 110 | * 111 | * Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null. 112 | * 113 | * @warning After a type conversion (by a call to a getXxx on a Column of a Yyy type), 114 | * the value returned by sqlite3_column_type() is undefined. 115 | */ 116 | int getType() const noexcept; 117 | 118 | /// Test if the column is an integer type value (meaningful only before any conversion) 119 | bool isInteger() const noexcept 120 | { 121 | return (SQLite::INTEGER == getType()); 122 | } 123 | /// Test if the column is a floating point type value (meaningful only before any conversion) 124 | bool isFloat() const noexcept 125 | { 126 | return (SQLite::FLOAT == getType()); 127 | } 128 | /// Test if the column is a text type value (meaningful only before any conversion) 129 | bool isText() const noexcept 130 | { 131 | return (SQLite::TEXT == getType()); 132 | } 133 | /// Test if the column is a binary blob type value (meaningful only before any conversion) 134 | bool isBlob() const noexcept 135 | { 136 | return (SQLite::BLOB == getType()); 137 | } 138 | /// Test if the column is NULL (meaningful only before any conversion) 139 | bool isNull() const noexcept 140 | { 141 | return (SQLite::Null == getType()); 142 | } 143 | 144 | /** 145 | * @brief Return the number of bytes used by the text (or blob) value of the column 146 | * 147 | * Return either : 148 | * - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator 149 | * - size in bytes of the string representation of the numerical value (integer or double) 150 | * - size in bytes of the binary blob returned by getBlob() 151 | * - 0 for a NULL value 152 | */ 153 | int getBytes() const noexcept; 154 | 155 | /// Alias returning the number of bytes used by the text (or blob) value of the column 156 | int size() const noexcept 157 | { 158 | return getBytes (); 159 | } 160 | 161 | /// Inline cast operator to char 162 | operator char() const 163 | { 164 | return static_cast(getInt()); 165 | } 166 | /// Inline cast operator to unsigned char 167 | operator unsigned char() const 168 | { 169 | return static_cast(getInt()); 170 | } 171 | /// Inline cast operator to short 172 | operator short() const 173 | { 174 | return static_cast(getInt()); 175 | } 176 | /// Inline cast operator to unsigned short 177 | operator unsigned short() const 178 | { 179 | return static_cast(getInt()); 180 | } 181 | 182 | /// Inline cast operator to int 183 | operator int() const 184 | { 185 | return getInt(); 186 | } 187 | /// Inline cast operator to 32bits unsigned integer 188 | operator unsigned int() const 189 | { 190 | return getUInt(); 191 | } 192 | #if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW) 193 | /// Inline cast operator to 32bits long 194 | operator long() const 195 | { 196 | return getInt(); 197 | } 198 | /// Inline cast operator to 32bits unsigned long 199 | operator unsigned long() const 200 | { 201 | return getUInt(); 202 | } 203 | #else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS) 204 | /// Inline cast operator to 64bits long when the data model of the system is LP64 (Linux 64 bits...) 205 | operator long() const 206 | { 207 | return getInt64(); 208 | } 209 | #endif 210 | 211 | /// Inline cast operator to 64bits integer 212 | operator long long() const 213 | { 214 | return getInt64(); 215 | } 216 | /// Inline cast operator to double 217 | operator double() const 218 | { 219 | return getDouble(); 220 | } 221 | /** 222 | * @brief Inline cast operator to char* 223 | * 224 | * @see getText 225 | */ 226 | operator const char*() const 227 | { 228 | return getText(); 229 | } 230 | /** 231 | * @brief Inline cast operator to void* 232 | * 233 | * @see getBlob 234 | */ 235 | operator const void*() const 236 | { 237 | return getBlob(); 238 | } 239 | 240 | /** 241 | * @brief Inline cast operator to std::string 242 | * 243 | * Handles BLOB or TEXT, which may contain null bytes within 244 | * 245 | * @see getString 246 | */ 247 | operator std::string() const 248 | { 249 | return getString(); 250 | } 251 | 252 | private: 253 | Statement::Ptr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object 254 | int mIndex; ///< Index of the column in the row of result, starting at 0 255 | }; 256 | 257 | /** 258 | * @brief Standard std::ostream text inserter 259 | * 260 | * Insert the text value of the Column object, using getText(), into the provided stream. 261 | * 262 | * @param[in] aStream Stream to use 263 | * @param[in] aColumn Column object to insert into the provided stream 264 | * 265 | * @return Reference to the stream used 266 | */ 267 | std::ostream& operator<<(std::ostream& aStream, const Column& aColumn); 268 | 269 | #if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015 270 | 271 | // Create an instance of T from the first N columns, see declaration in Statement.h for full details 272 | template 273 | T Statement::getColumns() 274 | { 275 | checkRow(); 276 | checkIndex(N - 1); 277 | return getColumns(std::make_integer_sequence{}); 278 | } 279 | 280 | // Helper function called by getColums 281 | template 282 | T Statement::getColumns(const std::integer_sequence) 283 | { 284 | return T{Column(mStmtPtr, Is)...}; 285 | } 286 | 287 | #endif 288 | 289 | } // namespace SQLite 290 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Database.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Database.h 3 | * @ingroup SQLiteCpp 4 | * @brief Management of a SQLite Database Connection. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // Forward declarations to avoid inclusion of in a header 18 | struct sqlite3; 19 | struct sqlite3_context; 20 | 21 | #ifndef SQLITE_USE_LEGACY_STRUCT // Since SQLITE 3.19 (used by default since SQLiteCpp 2.1.0) 22 | typedef struct sqlite3_value sqlite3_value; 23 | #else // Before SQLite 3.19 (legacy struct forward declaration can be activated with CMake SQLITECPP_LEGACY_STRUCT var) 24 | struct Mem; 25 | typedef struct Mem sqlite3_value; 26 | #endif 27 | 28 | 29 | namespace SQLite 30 | { 31 | 32 | // Those public constants enable most usages of SQLiteCpp without including in the client application. 33 | 34 | /// The database is opened in read-only mode. If the database does not already exist, an error is returned. 35 | extern const int OPEN_READONLY; // SQLITE_OPEN_READONLY 36 | /// The database is opened for reading and writing if possible, or reading only if the file is write protected 37 | /// by the operating system. In either case the database must already exist, otherwise an error is returned. 38 | extern const int OPEN_READWRITE; // SQLITE_OPEN_READWRITE 39 | /// With OPEN_READWRITE: The database is opened for reading and writing, and is created if it does not already exist. 40 | extern const int OPEN_CREATE; // SQLITE_OPEN_CREATE 41 | 42 | /// Enable URI filename interpretation, parsed according to RFC 3986 (ex. "file:data.db?mode=ro&cache=private") 43 | extern const int OPEN_URI; // SQLITE_OPEN_URI 44 | 45 | extern const int OK; ///< SQLITE_OK (used by check() bellow) 46 | 47 | extern const char* VERSION; ///< SQLITE_VERSION string from the sqlite3.h used at compile time 48 | extern const int VERSION_NUMBER; ///< SQLITE_VERSION_NUMBER from the sqlite3.h used at compile time 49 | 50 | /// Return SQLite version string using runtime call to the compiled library 51 | const char* getLibVersion() noexcept; 52 | /// Return SQLite version number using runtime call to the compiled library 53 | int getLibVersionNumber() noexcept; 54 | 55 | // Public structure for representing all fields contained within the SQLite header. 56 | // Official documentation for fields: https://www.sqlite.org/fileformat.html#the_database_header 57 | struct Header { 58 | unsigned char headerStr[16]; 59 | unsigned int pageSizeBytes; 60 | unsigned char fileFormatWriteVersion; 61 | unsigned char fileFormatReadVersion; 62 | unsigned char reservedSpaceBytes; 63 | unsigned char maxEmbeddedPayloadFrac; 64 | unsigned char minEmbeddedPayloadFrac; 65 | unsigned char leafPayloadFrac; 66 | unsigned long fileChangeCounter; 67 | unsigned long databaseSizePages; 68 | unsigned long firstFreelistTrunkPage; 69 | unsigned long totalFreelistPages; 70 | unsigned long schemaCookie; 71 | unsigned long schemaFormatNumber; 72 | unsigned long defaultPageCacheSizeBytes; 73 | unsigned long largestBTreePageNumber; 74 | unsigned long databaseTextEncoding; 75 | unsigned long userVersion; 76 | unsigned long incrementalVaccumMode; 77 | unsigned long applicationId; 78 | unsigned long versionValidFor; 79 | unsigned long sqliteVersion; 80 | }; 81 | 82 | /** 83 | * @brief RAII management of a SQLite Database Connection. 84 | * 85 | * A Database object manage a list of all SQLite Statements associated with the 86 | * underlying SQLite 3 database connection. 87 | * 88 | * Resource Acquisition Is Initialization (RAII) means that the Database Connection 89 | * is opened in the constructor and closed in the destructor, so that there is 90 | * no need to worry about memory management or the validity of the underlying SQLite Connection. 91 | * 92 | * Thread-safety: a Database object shall not be shared by multiple threads, because : 93 | * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 94 | * provided that no single database connection is used simultaneously in two or more threads." 95 | * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 96 | * because of the way it shares the underling SQLite precompiled statement 97 | * in a custom shared pointer (See the inner class "Statement::Ptr"). 98 | */ 99 | class Database 100 | { 101 | friend class Statement; // Give Statement constructor access to the mSQLitePtr Connection Handle 102 | 103 | public: 104 | /** 105 | * @brief Open the provided database UTF-8 filename. 106 | * 107 | * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior 108 | * of the old sqlite3_open() function (READWRITE+CREATE). 109 | * This makes sense if you want to use it on a readonly filesystem 110 | * or to prevent creation of a void file when a required file is missing. 111 | * 112 | * Exception is thrown in case of error, then the Database object is NOT constructed. 113 | * 114 | * @param[in] apFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) 115 | * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... 116 | * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) 117 | * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default 118 | * 119 | * @throw SQLite::Exception in case of error 120 | */ 121 | Database(const char* apFilename, 122 | const int aFlags = SQLite::OPEN_READONLY, 123 | const int aBusyTimeoutMs = 0, 124 | const char* apVfs = nullptr); 125 | 126 | /** 127 | * @brief Open the provided database UTF-8 filename. 128 | * 129 | * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior 130 | * of the old sqlite3_open() function (READWRITE+CREATE). 131 | * This makes sense if you want to use it on a readonly filesystem 132 | * or to prevent creation of a void file when a required file is missing. 133 | * 134 | * Exception is thrown in case of error, then the Database object is NOT constructed. 135 | * 136 | * @param[in] aFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) 137 | * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... 138 | * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) 139 | * @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default 140 | * 141 | * @throw SQLite::Exception in case of error 142 | */ 143 | Database(const std::string& aFilename, 144 | const int aFlags = SQLite::OPEN_READONLY, 145 | const int aBusyTimeoutMs = 0, 146 | const std::string& aVfs = "") : 147 | Database(aFilename.c_str(), aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) 148 | { 149 | } 150 | 151 | // Database is non-copyable 152 | Database(const Database&) = delete; 153 | Database& operator=(const Database&) = delete; 154 | 155 | // Database is movable 156 | Database(Database&& aDatabase) = default; 157 | Database& operator=(Database&& aDatabase) = default; 158 | 159 | /** 160 | * @brief Close the SQLite database connection. 161 | * 162 | * All SQLite statements must have been finalized before, 163 | * so all Statement objects must have been unregistered. 164 | * 165 | * @warning assert in case of error 166 | */ 167 | ~Database() = default; 168 | 169 | // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. 170 | struct Deleter 171 | { 172 | void operator()(sqlite3* apSQLite); 173 | }; 174 | 175 | /** 176 | * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked. 177 | * 178 | * This is useful in multithreaded program to handle case where a table is locked for writing by a thread. 179 | * Any other thread cannot access the table and will receive a SQLITE_BUSY error: 180 | * setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error. 181 | * Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;". 182 | * Default busy timeout is 0ms. 183 | * 184 | * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY 185 | * 186 | * @throw SQLite::Exception in case of error 187 | */ 188 | void setBusyTimeout(const int aBusyTimeoutMs); 189 | 190 | /** 191 | * @brief Shortcut to execute one or multiple statements without results. 192 | * 193 | * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : 194 | * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" 195 | * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" 196 | * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" 197 | * 198 | * @see Statement::exec() to handle precompiled statements (for better performances) without results 199 | * @see Statement::executeStep() to handle "SELECT" queries with results 200 | * 201 | * @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements 202 | * 203 | * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) 204 | * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. 205 | * 206 | * @throw SQLite::Exception in case of error 207 | */ 208 | int exec(const char* apQueries); 209 | 210 | /** 211 | * @brief Shortcut to execute one or multiple statements without results. 212 | * 213 | * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : 214 | * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" 215 | * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" 216 | * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" 217 | * 218 | * @see Statement::exec() to handle precompiled statements (for better performances) without results 219 | * @see Statement::executeStep() to handle "SELECT" queries with results 220 | * 221 | * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements 222 | * 223 | * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) 224 | * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. 225 | * 226 | * @throw SQLite::Exception in case of error 227 | */ 228 | int exec(const std::string& aQueries) 229 | { 230 | return exec(aQueries.c_str()); 231 | } 232 | 233 | /** 234 | * @brief Shortcut to execute a one step query and fetch the first column of the result. 235 | * 236 | * This is a shortcut to execute a simple statement with a single result. 237 | * This should be used only for non reusable queries (else you should use a Statement with bind()). 238 | * This should be used only for queries with expected results (else an exception is fired). 239 | * 240 | * @warning WARNING: Be very careful with this dangerous method: you have to 241 | * make a COPY OF THE result, else it will be destroy before the next line 242 | * (when the underlying temporary Statement and Column objects are destroyed) 243 | * 244 | * @see also Statement class for handling queries with multiple results 245 | * 246 | * @param[in] apQuery an UTF-8 encoded SQL query 247 | * 248 | * @return a temporary Column object with the first column of result. 249 | * 250 | * @throw SQLite::Exception in case of error 251 | */ 252 | Column execAndGet(const char* apQuery); 253 | 254 | /** 255 | * @brief Shortcut to execute a one step query and fetch the first column of the result. 256 | * 257 | * This is a shortcut to execute a simple statement with a single result. 258 | * This should be used only for non reusable queries (else you should use a Statement with bind()). 259 | * This should be used only for queries with expected results (else an exception is fired). 260 | * 261 | * @warning WARNING: Be very careful with this dangerous method: you have to 262 | * make a COPY OF THE result, else it will be destroy before the next line 263 | * (when the underlying temporary Statement and Column objects are destroyed) 264 | * 265 | * @see also Statement class for handling queries with multiple results 266 | * 267 | * @param[in] aQuery an UTF-8 encoded SQL query 268 | * 269 | * @return a temporary Column object with the first column of result. 270 | * 271 | * @throw SQLite::Exception in case of error 272 | */ 273 | Column execAndGet(const std::string& aQuery) 274 | { 275 | return execAndGet(aQuery.c_str()); 276 | } 277 | 278 | /** 279 | * @brief Shortcut to test if a table exists. 280 | * 281 | * Table names are case sensitive. 282 | * 283 | * @param[in] apTableName an UTF-8 encoded case sensitive Table name 284 | * 285 | * @return true if the table exists. 286 | * 287 | * @throw SQLite::Exception in case of error 288 | */ 289 | bool tableExists(const char* apTableName); 290 | 291 | /** 292 | * @brief Shortcut to test if a table exists. 293 | * 294 | * Table names are case sensitive. 295 | * 296 | * @param[in] aTableName an UTF-8 encoded case sensitive Table name 297 | * 298 | * @return true if the table exists. 299 | * 300 | * @throw SQLite::Exception in case of error 301 | */ 302 | bool tableExists(const std::string& aTableName) 303 | { 304 | return tableExists(aTableName.c_str()); 305 | } 306 | 307 | /** 308 | * @brief Get the rowid of the most recent successful INSERT into the database from the current connection. 309 | * 310 | * Each entry in an SQLite table always has a unique 64-bit signed integer key called the rowid. 311 | * If the table has a column of type INTEGER PRIMARY KEY, then it is an alias for the rowid. 312 | * 313 | * @return Rowid of the most recent successful INSERT into the database, or 0 if there was none. 314 | */ 315 | long long getLastInsertRowid() const noexcept; 316 | 317 | /// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection (not DROP table). 318 | int getTotalChanges() const noexcept; 319 | 320 | /// Return the numeric result code for the most recent failed API call (if any). 321 | int getErrorCode() const noexcept; 322 | /// Return the extended numeric result code for the most recent failed API call (if any). 323 | int getExtendedErrorCode() const noexcept; 324 | /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). 325 | const char* getErrorMsg() const noexcept; 326 | 327 | /// Return the filename used to open the database. 328 | const std::string& getFilename() const noexcept 329 | { 330 | return mFilename; 331 | } 332 | 333 | /** 334 | * @brief Return raw pointer to SQLite Database Connection Handle. 335 | * 336 | * This is often needed to mix this wrapper with other libraries or for advance usage not supported by SQLiteCpp. 337 | */ 338 | sqlite3* getHandle() const noexcept 339 | { 340 | return mSQLitePtr.get(); 341 | } 342 | 343 | /** 344 | * @brief Create or redefine a SQL function or aggregate in the sqlite database. 345 | * 346 | * This is the equivalent of the sqlite3_create_function_v2 command. 347 | * @see http://www.sqlite.org/c3ref/create_function.html 348 | * 349 | * @note UTF-8 text encoding assumed. 350 | * 351 | * @param[in] apFuncName Name of the SQL function to be created or redefined 352 | * @param[in] aNbArg Number of arguments in the function 353 | * @param[in] abDeterministic Optimize for deterministic functions (most are). A random number generator is not. 354 | * @param[in] apApp Arbitrary pointer of user data, accessible with sqlite3_user_data(). 355 | * @param[in] apFunc Pointer to a C-function to implement a scalar SQL function (apStep & apFinal nullptr) 356 | * @param[in] apStep Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) 357 | * @param[in] apFinal Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) 358 | * @param[in] apDestroy If not nullptr, then it is the destructor for the application data pointer. 359 | * 360 | * @throw SQLite::Exception in case of error 361 | */ 362 | void createFunction(const char* apFuncName, 363 | int aNbArg, 364 | bool abDeterministic, 365 | void* apApp, 366 | void (*apFunc)(sqlite3_context *, int, sqlite3_value **), 367 | void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr, 368 | void (*apFinal)(sqlite3_context *) = nullptr, // NOLINT(readability/casting) 369 | void (*apDestroy)(void *) = nullptr); 370 | 371 | /** 372 | * @brief Load a module into the current sqlite database instance. 373 | * 374 | * This is the equivalent of the sqlite3_load_extension call, but additionally enables 375 | * module loading support prior to loading the requested module. 376 | * 377 | * @see http://www.sqlite.org/c3ref/load_extension.html 378 | * 379 | * @note UTF-8 text encoding assumed. 380 | * 381 | * @param[in] apExtensionName Name of the shared library containing extension 382 | * @param[in] apEntryPointName Name of the entry point (nullptr to let sqlite work it out) 383 | * 384 | * @throw SQLite::Exception in case of error 385 | */ 386 | void loadExtension(const char* apExtensionName, const char* apEntryPointName); 387 | 388 | /** 389 | * @brief Set the key for the current sqlite database instance. 390 | * 391 | * This is the equivalent of the sqlite3_key call and should thus be called 392 | * directly after opening the database. 393 | * Open encrypted database -> call db.key("secret") -> database ready 394 | * 395 | * @param[in] aKey Key to decode/encode the database 396 | * 397 | * @throw SQLite::Exception in case of error 398 | */ 399 | void key(const std::string& aKey) const; 400 | 401 | /** 402 | * @brief Reset the key for the current sqlite database instance. 403 | * 404 | * This is the equivalent of the sqlite3_rekey call and should thus be called 405 | * after the database has been opened with a valid key. To decrypt a 406 | * database, call this method with an empty string. 407 | * Open normal database -> call db.rekey("secret") -> encrypted database, database ready 408 | * Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready 409 | * Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready 410 | * 411 | * @param[in] aNewKey New key to encode the database 412 | * 413 | * @throw SQLite::Exception in case of error 414 | */ 415 | void rekey(const std::string& aNewKey) const; 416 | 417 | /** 418 | * @brief Test if a file contains an unencrypted database. 419 | * 420 | * This is a simple test that reads the first bytes of a database file and 421 | * compares them to the standard header for unencrypted databases. If the 422 | * header does not match the standard string, we assume that we have an 423 | * encrypted file. 424 | * 425 | * @param[in] aFilename path/uri to a file 426 | * 427 | * @return true if the database has the standard header. 428 | * 429 | * @throw SQLite::Exception in case of error 430 | */ 431 | static bool isUnencrypted(const std::string& aFilename); 432 | 433 | /** 434 | * @brief Parse SQLite header data from a database file. 435 | * 436 | * This function reads the first 100 bytes of a SQLite database file 437 | * and reconstructs groups of individual bytes into the associated fields 438 | * in a Header object. 439 | * 440 | * @param[in] aFilename path/uri to a file 441 | * 442 | * @return Header object containing file data 443 | * 444 | * @throw SQLite::Exception in case of error 445 | */ 446 | static Header getHeaderInfo(const std::string& aFilename); 447 | 448 | // Parse SQLite header data from a database file. 449 | Header getHeaderInfo() 450 | { 451 | return getHeaderInfo(mFilename); 452 | } 453 | 454 | /** 455 | * @brief BackupType for the backup() method 456 | */ 457 | enum BackupType { Save, Load }; 458 | 459 | /** 460 | * @brief Load or save the database content. 461 | * 462 | * This function is used to load the contents of a database file on disk 463 | * into the "main" database of open database connection, or to save the current 464 | * contents of the database into a database file on disk. 465 | * 466 | * @throw SQLite::Exception in case of error 467 | */ 468 | void backup(const char* apFilename, BackupType aType); 469 | 470 | /** 471 | * @brief Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message 472 | */ 473 | void check(const int aRet) const 474 | { 475 | if (SQLite::OK != aRet) 476 | { 477 | throw SQLite::Exception(getHandle(), aRet); 478 | } 479 | } 480 | 481 | private: 482 | // TODO: perhaps switch to having Statement sharing a pointer to the Connexion 483 | std::unique_ptr mSQLitePtr; ///< Pointer to SQLite Database Connection Handle 484 | std::string mFilename; ///< UTF-8 filename used to open the database 485 | }; 486 | 487 | 488 | } // namespace SQLite 489 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Exception.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Exception.h 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | // Forward declaration to avoid inclusion of in a header 17 | struct sqlite3; 18 | 19 | namespace SQLite 20 | { 21 | 22 | 23 | /** 24 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 25 | */ 26 | class Exception : public std::runtime_error 27 | { 28 | public: 29 | /** 30 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 31 | * 32 | * @param[in] aErrorMessage The string message describing the SQLite error 33 | * @param[in] ret Return value from function call that failed. 34 | */ 35 | Exception(const char* aErrorMessage, int ret); 36 | 37 | Exception(const std::string& aErrorMessage, int ret) : 38 | Exception(aErrorMessage.c_str(), ret) 39 | { 40 | } 41 | 42 | /** 43 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 44 | * 45 | * @param[in] aErrorMessage The string message describing the SQLite error 46 | */ 47 | explicit Exception(const char* aErrorMessage) : 48 | Exception(aErrorMessage, -1) // 0 would be SQLITE_OK, which doesn't make sense 49 | { 50 | } 51 | explicit Exception(const std::string& aErrorMessage) : 52 | Exception(aErrorMessage.c_str(), -1) // 0 would be SQLITE_OK, which doesn't make sense 53 | { 54 | } 55 | 56 | /** 57 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 58 | * 59 | * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. 60 | */ 61 | explicit Exception(sqlite3* apSQLite); 62 | 63 | /** 64 | * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. 65 | * 66 | * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. 67 | * @param[in] ret Return value from function call that failed. 68 | */ 69 | Exception(sqlite3* apSQLite, int ret); 70 | 71 | /// Return the result code (if any, otherwise -1). 72 | int getErrorCode() const noexcept 73 | { 74 | return mErrcode; 75 | } 76 | 77 | /// Return the extended numeric result code (if any, otherwise -1). 78 | int getExtendedErrorCode() const noexcept 79 | { 80 | return mExtendedErrcode; 81 | } 82 | 83 | /// Return a string, solely based on the error code 84 | const char* getErrorStr() const noexcept; 85 | 86 | private: 87 | int mErrcode; ///< Error code value 88 | int mExtendedErrcode; ///< Detailed error code if any 89 | }; 90 | 91 | 92 | } // namespace SQLite 93 | -------------------------------------------------------------------------------- /include/SQLiteCpp/ExecuteMany.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ExecuteMany.h 3 | * @ingroup SQLiteCpp 4 | * @brief Convenience function to execute a Statement with multiple Parameter sets 5 | * 6 | * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) 7 | * Copyright (c) 2019-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | #pragma once 13 | 14 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 15 | 16 | #include 17 | #include 18 | 19 | /// @cond 20 | #include 21 | #include 22 | #include 23 | 24 | namespace SQLite 25 | { 26 | /// @endcond 27 | 28 | /** 29 | * \brief Convenience function to execute a Statement with multiple Parameter sets once for each parameter set given. 30 | * 31 | * 32 | * This feature requires a c++14 capable compiler. 33 | * 34 | * \code{.cpp} 35 | * execute_many(db, "INSERT INTO test VALUES (?, ?)", 36 | * 1, 37 | * std::make_tuple(2), 38 | * std::make_tuple(3, "three") 39 | * ); 40 | * \endcode 41 | * @param aDatabase Database to use 42 | * @param apQuery Query to use with all parameter sets 43 | * @param aArg first tuple with parameters 44 | * @param aParams the following tuples with parameters 45 | */ 46 | template 47 | void execute_many(Database& aDatabase, const char* apQuery, Arg&& aArg, Types&&... aParams) 48 | { 49 | SQLite::Statement query(aDatabase, apQuery); 50 | bind_exec(query, std::forward(aArg)); 51 | (void)std::initializer_list 52 | { 53 | ((void)reset_bind_exec(query, std::forward(aParams)), 0)... 54 | }; 55 | } 56 | 57 | /** 58 | * \brief Convenience function to reset a statement and call bind_exec to 59 | * bind new values to the statement and execute it 60 | * 61 | * This feature requires a c++14 capable compiler. 62 | * 63 | * @param apQuery Query to use 64 | * @param aTuple Tuple to bind 65 | */ 66 | template 67 | void reset_bind_exec(Statement& apQuery, TupleT&& aTuple) 68 | { 69 | apQuery.reset(); 70 | bind_exec(apQuery, std::forward(aTuple)); 71 | } 72 | 73 | /** 74 | * \brief Convenience function to bind values a the statement and execute it 75 | * 76 | * This feature requires a c++14 capable compiler. 77 | * 78 | * @param apQuery Query to use 79 | * @param aTuple Tuple to bind 80 | */ 81 | template 82 | void bind_exec(Statement& apQuery, TupleT&& aTuple) 83 | { 84 | SQLite::bind(apQuery, std::forward(aTuple)); 85 | while (apQuery.executeStep()) {} 86 | } 87 | 88 | } // namespace SQLite 89 | 90 | #endif // c++14 91 | -------------------------------------------------------------------------------- /include/SQLiteCpp/SQLiteCpp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SQLiteCpp.h 3 | * @ingroup SQLiteCpp 4 | * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. 5 | * 6 | * Include this main header file in your project to gain access to all functionality provided by the wrapper. 7 | * 8 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 11 | * or copy at http://opensource.org/licenses/MIT) 12 | */ 13 | /** 14 | * @defgroup SQLiteCpp SQLiteC++ 15 | * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. 16 | */ 17 | #pragma once 18 | 19 | 20 | // Include useful headers of SQLiteC++ 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | /** 30 | * @brief Version numbers for SQLiteC++ are provided in the same way as sqlite3.h 31 | * 32 | * The [SQLITECPP_VERSION] C preprocessor macro in the SQLiteC++.h header 33 | * evaluates to a string literal that is the SQLite version in the 34 | * format "X.Y.Z" where X is the major version number 35 | * and Y is the minor version number and Z is the release number. 36 | * 37 | * The [SQLITECPP_VERSION_NUMBER] C preprocessor macro resolves to an integer 38 | * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same 39 | * numbers used in [SQLITECPP_VERSION]. 40 | * 41 | * WARNING: shall always be updated in sync with PROJECT_VERSION in CMakeLists.txt 42 | */ 43 | #define SQLITECPP_VERSION "3.00.00" // 3.0.0 44 | #define SQLITECPP_VERSION_NUMBER 3000000 // 3.0.0 45 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Transaction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction.h 3 | * @ingroup SQLiteCpp 4 | * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | #include 14 | 15 | 16 | namespace SQLite 17 | { 18 | 19 | 20 | // Forward declaration 21 | class Database; 22 | 23 | /** 24 | * @brief RAII encapsulation of a SQLite Transaction. 25 | * 26 | * A Transaction is a way to group multiple SQL statements into an atomic secured operation; 27 | * either it succeeds, with all the changes committed to the database file, 28 | * or if it fails, all the changes are rolled back to the initial state. 29 | * 30 | * Resource Acquisition Is Initialization (RAII) means that the Transaction 31 | * begins in the constructor and is rollbacked in the destructor, so that there is 32 | * no need to worry about memory management or the validity of the underlying SQLite Connection. 33 | * 34 | * This method also offers big performances improvements compared to individually executed statements. 35 | * 36 | * Thread-safety: a Transaction object shall not be shared by multiple threads, because : 37 | * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 38 | * provided that no single database connection is used simultaneously in two or more threads." 39 | * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 40 | * because of the way it shares the underling SQLite precompiled statement 41 | * in a custom shared pointer (See the inner class "Statement::Ptr"). 42 | */ 43 | class Transaction 44 | { 45 | public: 46 | /** 47 | * @brief Begins the SQLite transaction 48 | * 49 | * @param[in] aDatabase the SQLite Database Connection 50 | * 51 | * Exception is thrown in case of error, then the Transaction is NOT initiated. 52 | */ 53 | explicit Transaction(Database& aDatabase); 54 | 55 | // Transaction is non-copyable 56 | Transaction(const Transaction&) = delete; 57 | Transaction& operator=(const Transaction&) = delete; 58 | 59 | /** 60 | * @brief Safely rollback the transaction if it has not been committed. 61 | */ 62 | ~Transaction(); 63 | 64 | /** 65 | * @brief Commit the transaction. 66 | */ 67 | void commit(); 68 | 69 | private: 70 | Database& mDatabase; ///< Reference to the SQLite Database Connection 71 | bool mbCommited; ///< True when commit has been called 72 | }; 73 | 74 | 75 | } // namespace SQLite 76 | -------------------------------------------------------------------------------- /include/SQLiteCpp/Utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Utils.h 3 | * @ingroup SQLiteCpp 4 | * @brief Definition of the SQLITECPP_PURE_FUNC macro. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #pragma once 12 | 13 | // macro taken from https://github.com/nemequ/hedley/blob/master/hedley.h that was in public domain at this time 14 | #if defined(__GNUC__) || defined(__GNUG__) || defined(__clang__) ||\ 15 | (defined(__INTEL_COMPILER) && __INTEL_COMPILER > 1600) ||\ 16 | (defined(__ARMCC_VERSION) && __ARMCC_VERSION > 4010000) ||\ 17 | (\ 18 | defined(__TI_COMPILER_VERSION__) && (\ 19 | __TI_COMPILER_VERSION__ > 8003000 ||\ 20 | (__TI_COMPILER_VERSION__ > 7003000 && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\ 21 | )\ 22 | ) 23 | #if defined(__has_attribute) 24 | #if !defined(SQLITECPP_PURE_FUNC) && __has_attribute(pure) 25 | #define SQLITECPP_PURE_FUNC __attribute__((pure)) 26 | #endif 27 | #endif 28 | #endif 29 | #if !defined(SQLITECPP_PURE_FUNC) 30 | #define SQLITECPP_PURE_FUNC 31 | #endif 32 | -------------------------------------------------------------------------------- /include/SQLiteCpp/VariadicBind.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file VariadicBind.h 3 | * @ingroup SQLiteCpp 4 | * @brief Convenience function for Statement::bind(...) 5 | * 6 | * Copyright (c) 2016 Paul Dreik (github@pauldreik.se) 7 | * Copyright (c) 2016-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * Copyright (c) 2019 Maximilian Bachmann (contact@maxbachmann.de) 9 | * 10 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 11 | * or copy at http://opensource.org/licenses/MIT) 12 | */ 13 | #pragma once 14 | 15 | #include 16 | 17 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 18 | #include 19 | #endif // c++14 20 | 21 | /// @cond 22 | #include 23 | #include 24 | 25 | namespace SQLite 26 | { 27 | /// @endcond 28 | 29 | /** 30 | * \brief Convenience function for calling Statement::bind(...) once for each argument given. 31 | * 32 | * This takes care of incrementing the index between each calls to bind. 33 | * 34 | * This feature requires a c++11 capable compiler. 35 | * 36 | * \code{.cpp} 37 | * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC 48 | void bind(SQLite::Statement& query, const Args& ... args) 49 | { 50 | int pos = 0; 51 | (void)std::initializer_list{ 52 | ((void)query.bind(++pos, std::forward(args)), 0)... 53 | }; 54 | } 55 | 56 | #if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 57 | 58 | /** 59 | * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, 60 | * by forwarding them to the variadic template 61 | * 62 | * This feature requires a c++14 capable compiler. 63 | * 64 | * \code{.cpp} 65 | * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC 76 | void bind(SQLite::Statement& query, const std::tuple &tuple) 77 | { 78 | bind(query, tuple, std::index_sequence_for()); 79 | } 80 | 81 | /** 82 | * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, 83 | * by forwarding them to the variadic template. This function is just needed to convert the tuples 84 | * to parameter packs 85 | * 86 | * This feature requires a c++14 capable compiler. 87 | * 88 | * @param query statement 89 | * @param tuple tuple with values to bind 90 | */ 91 | template 92 | void bind(SQLite::Statement& query, const std::tuple &tuple, std::index_sequence) 93 | { 94 | bind(query, std::get(tuple)...); 95 | } 96 | #endif // c++14 97 | 98 | } // namespace SQLite 99 | -------------------------------------------------------------------------------- /src/CQSDK/CQAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "CQAPI.h" 2 | #include "CQEVE.h" 3 | 4 | int AuthCode; 5 | CQEVENT(int, Initialize, 4)(const int AuthCode) 6 | { 7 | ::AuthCode = AuthCode; 8 | return 0; 9 | } 10 | 11 | int getAuthCode() 12 | { 13 | return AuthCode; 14 | } 15 | 16 | int CQ::getAuthCode() 17 | { 18 | return AuthCode; 19 | } 20 | -------------------------------------------------------------------------------- /src/CQSDK/CQAPI_EX.cpp: -------------------------------------------------------------------------------- 1 | #include "CQAPI_EX.h" 2 | 3 | #include "CQAPI.h" 4 | #include "Unpack.h" 5 | #include "CQEVE_GroupMsg.h" 6 | #include "CQTools.h" 7 | 8 | using namespace CQ; 9 | using namespace std; 10 | int lasterr; 11 | 12 | StrangerInfo::StrangerInfo(const char* msg) 13 | { 14 | if (msg != nullptr && msg[0] != '\0') 15 | { 16 | Unpack p(base64_decode(msg)); 17 | QQID = p.getLong(); 18 | nick = p.getstring(); 19 | sex = p.getInt(); 20 | age = p.getInt(); 21 | } 22 | } 23 | 24 | string StrangerInfo::tostring() const 25 | { 26 | return string("{") 27 | + "QQ:" + to_string(QQID) 28 | + " ,昵称:" + nick 29 | + " ,性别:" + (sex == 255 ? "未知" : sex == 1 ? "男" : "女") 30 | + " ,年龄:" + to_string(age) 31 | + "}"; 32 | } 33 | 34 | void GroupMemberInfo::setdata(Unpack& u) 35 | { 36 | Group = u.getLong(); 37 | QQID = u.getLong(); 38 | Nick = u.getstring(); 39 | GroupNick = u.getstring(); 40 | Gender = u.getInt(); 41 | Age = u.getInt(); 42 | Region = u.getstring(); 43 | AddGroupTime = u.getInt(); 44 | LastMsgTime = u.getInt(); 45 | LevelName = u.getstring(); 46 | permissions = u.getInt(); 47 | NaughtyRecord = u.getInt() == 1; 48 | Title = u.getstring(); 49 | ExpireTime = u.getInt(); 50 | canEditGroupNick = u.getInt() == 1; 51 | } 52 | 53 | GroupMemberInfo::GroupMemberInfo(Unpack& msg) { setdata(msg); } 54 | 55 | GroupMemberInfo::GroupMemberInfo(const char* msg) 56 | { 57 | if (msg != nullptr && msg[0] != '\0') 58 | { 59 | Unpack u(base64_decode(msg)); 60 | setdata(u); 61 | } 62 | } 63 | 64 | GroupMemberInfo::GroupMemberInfo(vector data) 65 | { 66 | if (data.size() != 0) 67 | { 68 | Unpack u(data); 69 | setdata(u); 70 | } 71 | } 72 | 73 | string GroupMemberInfo::tostring() const 74 | { 75 | string s = "{"; 76 | s += "群号:"; 77 | s += to_string(Group); 78 | s += " ,QQ号:"; 79 | s += to_string(QQID); 80 | s += " ,昵称:"; 81 | s += Nick; 82 | s += " ,名片:"; 83 | s += GroupNick; 84 | s += " ,性别:"; 85 | s += (Gender == 255 ? "未知" : Gender == 1 ? "男" : "女"); 86 | s += " ,年龄:"; 87 | s += to_string(Age); 88 | s += " ,地区:"; 89 | s += Region; 90 | s += " ,加群时间:"; 91 | s += to_string(AddGroupTime); 92 | s += " ,最后发言:"; 93 | s += to_string(LastMsgTime); 94 | s += " ,等级_名称:"; 95 | s += LevelName; 96 | s += " ,管理权限:"; 97 | s += (permissions == 3 ? "群主" : permissions == 2 ? "管理员" : "群员"); 98 | s += "("; 99 | s += to_string(permissions); 100 | s += ")"; 101 | s += " ,不良记录成员:"; 102 | s += to_string(NaughtyRecord); 103 | s += " ,专属头衔:"; 104 | s += Title; 105 | s += " ,专属头衔过期时间:"; 106 | s += to_string(ExpireTime); 107 | s += " ,允许修改名片:"; 108 | s += to_string(canEditGroupNick); 109 | s += "}"; 110 | return s; 111 | } 112 | 113 | //增加运行日志 114 | int CQ::addLog(const int Priorty, const char* Type, const char* Content) 115 | { 116 | return lasterr = CQ_addLog(getAuthCode(), Priorty, Type, Content); 117 | } 118 | 119 | //发送好友消息 120 | int CQ::sendPrivateMsg(const long long QQ, const char* msg) { return CQ_sendPrivateMsg(getAuthCode(), QQ, msg); } 121 | 122 | //发送好友消息 123 | int CQ::sendPrivateMsg(const long long QQ, const std::string& msg) { return sendPrivateMsg(QQ, msg.c_str()); } 124 | 125 | //发送群消息 126 | int CQ::sendGroupMsg(const long long GroupID, const char* msg) { return CQ_sendGroupMsg(getAuthCode(), GroupID, msg); } 127 | 128 | //发送群消息 129 | int CQ::sendGroupMsg(const long long GroupID, const std::string& msg) { return sendGroupMsg(GroupID, msg.c_str()); } 130 | 131 | int CQ::sendDiscussMsg(const long long DiscussID, const char* msg) 132 | { 133 | return CQ_sendDiscussMsg(getAuthCode(), DiscussID, msg); 134 | } 135 | 136 | //发送讨论组消息 137 | int CQ::sendDiscussMsg(const long long DiscussID, const std::string& msg) { return sendDiscussMsg(DiscussID, msg.c_str()); } 138 | 139 | //发送赞 140 | int CQ::sendLike(const long long QQID, const int times) { return lasterr = CQ_sendLikeV2(getAuthCode(), QQID, times); } 141 | 142 | //接收语音 143 | const char* CQ::getRecord(const char* file, const char* outformat) 144 | { 145 | return CQ_getRecord(getAuthCode(), file, outformat); 146 | } 147 | 148 | //接收语音 149 | std::string CQ::getRecord(const std::string& file, const std::string& outformat) 150 | { 151 | return getRecord(file.c_str(), outformat.c_str()); 152 | } 153 | 154 | //取CsrfToken (慎用,此接口需要严格授权) 155 | int CQ::getCsrfToken() { return CQ_getCsrfToken(getAuthCode()); } 156 | 157 | //取应用目录 158 | const char* CQ::getAppDirectory() { return CQ_getAppDirectory(getAuthCode()); } 159 | 160 | //取登录QQ 161 | long long CQ::getLoginQQ() { return CQ_getLoginQQ(getAuthCode()); } 162 | 163 | //取登录昵称 164 | const char* CQ::getLoginNick() { return CQ_getLoginNick(getAuthCode()); } 165 | 166 | //置群员移除 167 | int CQ::setGroupKick(const long long GroupID, const long long QQID, const CQBOOL refuseForever) 168 | { 169 | return lasterr = CQ_setGroupKick(getAuthCode(), GroupID, QQID, refuseForever); 170 | } 171 | 172 | //置群员禁言 173 | int CQ::setGroupBan(const long long GroupID, const long long QQID, const long long banTime) 174 | { 175 | return lasterr = CQ_setGroupBan(getAuthCode(), GroupID, QQID, banTime); 176 | } 177 | 178 | //置群管理员 179 | int CQ::setGroupAdmin(const long long GroupID, const long long QQID, const CQBOOL isAdmin) 180 | { 181 | return lasterr = CQ_setGroupAdmin(getAuthCode(), GroupID, QQID, isAdmin); 182 | } 183 | 184 | //置群成员专属头衔 185 | int CQ::setGroupSpecialTitle(const long long GroupID, const long long QQID, const char* Title, const long long ExpireTime) 186 | { 187 | return lasterr = CQ_setGroupSpecialTitle(getAuthCode(), GroupID, QQID, Title, ExpireTime); 188 | } 189 | 190 | //置群成员专属头衔 191 | int CQ::setGroupSpecialTitle(const long long GroupID, const long long QQID, const std::string& Title, const long long ExpireTime) 192 | { 193 | return setGroupSpecialTitle(GroupID, QQID, Title.c_str(), ExpireTime); 194 | } 195 | 196 | //置全群禁言 197 | int CQ::setGroupWholeBan(const long long GroupID, const CQBOOL isBan) 198 | { 199 | return lasterr = CQ_setGroupWholeBan(getAuthCode(), GroupID, isBan); 200 | } 201 | 202 | //置Anonymous群员禁言 203 | int CQ::setGroupAnonymousBan(const long long GroupID, const char* AnonymousToken, const long long banTime) 204 | { 205 | return lasterr = CQ_setGroupAnonymousBan(getAuthCode(), GroupID, AnonymousToken, banTime); 206 | } 207 | 208 | //置群Anonymous设置 209 | int CQ::setGroupAnonymous(const long long GroupID, const CQBOOL enableAnonymous) 210 | { 211 | return lasterr = CQ_setGroupAnonymous(getAuthCode(), GroupID, enableAnonymous); 212 | } 213 | 214 | //置群成员名片 215 | int CQ::setGroupCard(const long long GroupID, const long long QQID, const char* newGroupNick) 216 | { 217 | return lasterr = CQ_setGroupCard(getAuthCode(), GroupID, QQID, newGroupNick); 218 | } 219 | 220 | //置群成员名片 221 | int CQ::setGroupCard(const long long GroupID, const long long QQID, const std::string& newGroupNick) 222 | { 223 | return setGroupCard(GroupID, QQID, newGroupNick.c_str()); 224 | } 225 | 226 | //置群退出 227 | int CQ::setGroupLeave(const long long GroupID, const CQBOOL isDismiss) 228 | { 229 | return lasterr = CQ_setGroupLeave(getAuthCode(), GroupID, isDismiss); 230 | } 231 | 232 | //置讨论组退出 233 | int CQ::setDiscussLeave(const long long DiscussID) { return lasterr = CQ_setDiscussLeave(getAuthCode(), DiscussID); } 234 | 235 | //置好友添加请求 236 | int CQ::setFriendAddRequest(const char* RequestToken, const int ReturnType, const char* Remarks) 237 | { 238 | return lasterr = CQ_setFriendAddRequest(getAuthCode(), RequestToken, ReturnType, Remarks); 239 | } 240 | 241 | //置群添加请求 242 | int CQ::setGroupAddRequest(const char* RequestToken, const int RequestType, const int ReturnType, const char* Reason) 243 | { 244 | return lasterr = CQ_setGroupAddRequestV2(getAuthCode(), RequestToken, RequestType, ReturnType, Reason); 245 | } 246 | 247 | //置致命错误提示 248 | int CQ::setFatal(const char* ErrorMsg) { return lasterr = CQ_setFatal(getAuthCode(), ErrorMsg); } 249 | 250 | //取群成员信息 (支持缓存) 251 | GroupMemberInfo CQ::getGroupMemberInfo(const long long GroupID, const long long QQID, const CQBOOL disableCache) 252 | { 253 | return GroupMemberInfo(CQ_getGroupMemberInfoV2(getAuthCode(), GroupID, QQID, disableCache)); 254 | } 255 | 256 | //取陌生人信息 (支持缓存) 257 | StrangerInfo CQ::getStrangerInfo(const long long QQID, const CQBOOL DisableCache) 258 | { 259 | return StrangerInfo(CQ_getStrangerInfo(getAuthCode(), QQID, DisableCache)); 260 | } 261 | 262 | //取群成员列表 263 | std::vector CQ::getGroupMemberList(const long long GroupID) 264 | { 265 | const char* data = CQ_getGroupMemberList(getAuthCode(), GroupID); 266 | vector infovector; 267 | if (data[0] == '\0')return infovector; 268 | 269 | Unpack u(base64_decode(data)); 270 | auto i = u.getInt(); 271 | while (--i && u.len() > 0) 272 | { 273 | auto tmp = u.getUnpack(); 274 | infovector.emplace_back(tmp); 275 | } 276 | 277 | return infovector; 278 | } 279 | 280 | //取群列表 281 | std::map CQ::getGroupList() 282 | { 283 | const string source(CQ_getGroupList(getAuthCode())); // 获取原始数据 284 | const auto data(base64_decode(source)); // 解码 285 | Unpack pack(data); // 转换为Unpack 286 | 287 | pack.getInt(); //获取总群数, 返回值在此并没有用 288 | std::map ret; 289 | while (pack.len() > 0) 290 | { 291 | //如果还有剩余数据,就继续读取 292 | auto tep = pack.getUnpack(); //读取下一个群 293 | auto ID = tep.getLong(); //读取GroupID 294 | const auto name = tep.getstring(); //读取群名称 295 | ret[ID] = name; //写入map 296 | } 297 | return ret; 298 | } 299 | 300 | int CQ::deleteMsg(const long long MsgId) 301 | { 302 | return lasterr = CQ_deleteMsg(getAuthCode(), MsgId); 303 | } 304 | 305 | const char* CQ::getlasterrmsg() 306 | { 307 | switch (lasterr) 308 | { 309 | case 0: return "操作成功"; 310 | case -1: return "请求发送失败"; 311 | case -2: return "未收到服务器回复,可能未发送成功"; 312 | case -3: return "消息过长或为空"; 313 | case -4: return "消息解析过程异常"; 314 | case -5: return "日志功能未启用"; 315 | case -6: return "日志优先级错误"; 316 | case -7: return "数据入库失败"; 317 | case -8: return "不支持对系统帐号操作"; 318 | case -9: return "帐号不在该群内,消息无法发送"; 319 | case -10: return "该用户不存在/不在群内"; 320 | case -11: return "数据错误,无法请求发送"; 321 | case -12: return "不支持对Anonymous成员解除禁言"; 322 | case -13: return "无法解析要禁言的Anonymous成员数据"; 323 | case -14: return "由于未知原因,操作失败"; 324 | case -15: return "群未开启Anonymous发言功能,或Anonymous帐号被禁言"; 325 | case -16: return "帐号不在群内或网络错误,无法退出/解散该群"; 326 | case -17: return "帐号为群主,无法退出该群"; 327 | case -18: return "帐号非群主,无法解散该群"; 328 | case -19: return "临时消息已失效或未建立"; 329 | case -20: return "参数错误"; 330 | case -21: return "临时消息已失效或未建立"; 331 | case -22: return "获取QQ信息失败"; 332 | case -23: return "找不到与目标QQ的关系,消息无法发送"; 333 | case -99: return "您调用的功能无法在此版本上实现"; 334 | case -101: return "应用过大"; 335 | case -102: return "不是合法的应用"; 336 | case -103: return "不是合法的应用"; 337 | case -104: return "应用不存在公开的Information函数"; 338 | case -105: return "无法载入应用信息"; 339 | case -106: return "文件名与应用ID不同"; 340 | case -107: return "返回信息解析错误"; 341 | case -108: return "AppInfo返回的Api版本不支持直接加载,仅支持Api版本为9(及以上)的应用直接加载"; 342 | case -109: return "AppInfo返回的AppID错误"; 343 | case -110: return "缺失AppInfo返回的AppID对应的[Appid].json文件"; 344 | case -111: return "[Appid].json文件内的AppID与其文件名不同"; 345 | case -120: return "无Api授权接收函数(Initialize)"; 346 | case -121: return "Api授权接收函数(Initialize)返回值非0"; 347 | case -122: return "尝试恶意修改酷Q配置文件,将取消加载并关闭酷Q"; 348 | case -150: return "无法载入应用信息"; 349 | case -151: return "应用信息Json串解析失败,请检查Json串是否正确"; 350 | case -152: return "Api版本过旧或过新"; 351 | case -153: return "应用信息错误或存在缺失"; 352 | case -154: return "Appid不合法"; 353 | case -160: return "事件类型(Type)错误或缺失"; 354 | case -161: return "事件函数(Function)错误或缺失"; 355 | case -162: return "应用优先级不为10000、20000、30000、40000中的一个"; 356 | case -163: return "事件类型(Api)不支持应用Api版本"; 357 | case -164: return "应用Api版本大于8,但使用了新版本已停用的事件类型(Type):1(好友消息)、3(临时消息)"; 358 | case -165: return "事件类型为2(群消息)、4(讨论组消息)、21(私聊消息),但缺少正则表达式(regex)的表达式部分(expression)"; 359 | case -166: return "存在为空的正则表达式(regex)的key"; 360 | case -167: return "存在为空的正则表达式(regex)的表达式部分(expression)"; 361 | case -168: return "应用事件(event)id参数不存在或为0"; 362 | case -169: return "应用事件(event)id参数有重复"; 363 | case -180: return "应用状态(status)id参数不存在或为0"; 364 | case -181: return "应用状态(status)period参数不存在或设置错误"; 365 | case -182: return "应用状态(status)id参数有重复"; 366 | case -201: return "无法载入应用,可能是应用文件已损坏"; 367 | case -202: return "Api版本过旧或过新"; 368 | case -997: return "应用未启用"; 369 | case -998: return "应用调用在Auth声明之外的 酷Q Api。"; 370 | default: return "未知错误"; 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /src/CQSDK/CQEVE.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 此文件是所有EX事件的实现 3 | */ 4 | #include "CQEVE_ALL.h" 5 | 6 | #include "CQAPI_EX.h" 7 | #include "CQTools.h" 8 | #include "Unpack.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN 11 | #include 12 | 13 | using namespace CQ; 14 | 15 | void EVE::message_ignore() { _EVEret = Msg_Ignored; } 16 | void EVE::message_block() { _EVEret = Msg_Blocked; } 17 | 18 | bool EVEMsg::isSystem() const { return fromQQ == 1000000; } 19 | 20 | Font::Font(const int Font) 21 | { 22 | RtlMoveMemory(static_cast(this), reinterpret_cast(Font), 20); 23 | } 24 | 25 | EVEMsg::EVEMsg(const int subType, const int msgId, const long long fromQQ, std::string message, const int font) 26 | : subType(subType), msgId(msgId), fromQQ(fromQQ), message(move(message)), font(font) 27 | { 28 | } 29 | 30 | //真实用户 31 | bool EVEMsg::isUser() const 32 | { 33 | switch (fromQQ) 34 | { 35 | case 1000000: // 系统提示 36 | case 80000000: // 匿名 37 | return false; 38 | default: 39 | return true; 40 | } 41 | } 42 | 43 | EVEGroupMsg::EVEGroupMsg(const int subType, const int msgId, const long long fromGroup, const long long fromQQ, const char* fromAnonymous, 44 | const char* msg, const int font) 45 | : EVEMsg(subType, msgId, fromQQ, msg, font), fromAnonymousInfo(), fromGroup(fromGroup), 46 | fromAnonymousToken(fromAnonymous) 47 | { 48 | } 49 | 50 | EVEGroupMsg::~EVEGroupMsg() { delete fromAnonymousInfo; } 51 | 52 | 53 | bool EVEGroupMsg::isAnonymous() const { return fromQQ == 80000000; } 54 | 55 | 56 | AnonymousInfo& EVEGroupMsg::getFromAnonymousInfo() //throw(std::exception_ptr) 57 | { 58 | if (isAnonymous()) 59 | return 60 | fromAnonymousInfo != nullptr 61 | ? *fromAnonymousInfo 62 | : *(fromAnonymousInfo = new AnonymousInfo(fromAnonymousToken)); 63 | throw std::exception_ptr(); 64 | } 65 | 66 | bool EVEGroupMsg::setGroupKick(const bool refusedAddAgain) const 67 | { 68 | return !CQ::setGroupKick(fromGroup, fromQQ, refusedAddAgain); 69 | } 70 | 71 | bool EVEGroupMsg::setGroupBan(const long long banTime) const 72 | { 73 | if (isAnonymous()) 74 | { 75 | return !setGroupAnonymousBan(fromGroup, fromAnonymousToken, banTime); 76 | } 77 | return !CQ::setGroupBan(fromGroup, fromQQ, banTime); 78 | } 79 | 80 | bool EVEGroupMsg::setGroupAdmin(const bool isAdmin) const 81 | { 82 | return !CQ::setGroupAdmin(fromGroup, fromQQ, isAdmin); 83 | } 84 | 85 | bool EVEGroupMsg::setGroupSpecialTitle(const std::string& Title, const long long ExpireTime) const 86 | { 87 | return !CQ::setGroupSpecialTitle(fromGroup, fromQQ, Title, ExpireTime); 88 | } 89 | 90 | bool EVEGroupMsg::setGroupWholeBan(const bool isBan) const 91 | { 92 | return CQ::setGroupWholeBan(fromGroup, isBan) != 0; 93 | } 94 | 95 | bool EVEGroupMsg::setGroupAnonymous(const bool enableAnonymous) const 96 | { 97 | return CQ::setGroupAnonymous(fromGroup, enableAnonymous) != 0; 98 | } 99 | 100 | bool EVEGroupMsg::setGroupCard(const std::string& newGroupNick) const 101 | { 102 | return CQ::setGroupCard(fromGroup, fromQQ, newGroupNick) != 0; 103 | } 104 | 105 | bool EVEGroupMsg::setGroupLeave(const bool isDismiss) const 106 | { 107 | return CQ::setGroupLeave(fromGroup, isDismiss) != 0; 108 | } 109 | 110 | GroupMemberInfo EVEGroupMsg::getGroupMemberInfo(const bool disableCache) const 111 | { 112 | return CQ::getGroupMemberInfo(fromGroup, fromQQ, disableCache); 113 | } 114 | 115 | std::vector EVEGroupMsg::getGroupMemberList() const 116 | { 117 | return CQ::getGroupMemberList(fromGroup); 118 | } 119 | 120 | EVEPrivateMsg::EVEPrivateMsg(const int subType, const int msgId, const long long fromQQ, const char* msg, const int font) 121 | : EVEMsg(subType, msgId, fromQQ, msg, font) 122 | { 123 | } 124 | 125 | //来自好友 126 | bool EVEPrivateMsg::fromPrivate() const { return subType == 11; } 127 | 128 | //来自在线状态 129 | bool EVEPrivateMsg::fromOnlineStatus() const { return subType == 1; } 130 | 131 | //来自群临时 132 | bool EVEPrivateMsg::fromGroup() const { return subType == 2; } 133 | 134 | //来自讨论组临时 135 | bool EVEPrivateMsg::fromDiscuss() const { return subType == 3; } 136 | 137 | msg EVEPrivateMsg::sendMsg() const { return msg(fromQQ, Friend); } 138 | msg EVEGroupMsg::sendMsg() const { return msg(fromGroup, Group); } 139 | msg EVEDiscussMsg::sendMsg() const { return msg(fromQQ, Discuss); } 140 | 141 | int EVEPrivateMsg::sendMsg(const char* msg) const { return sendPrivateMsg(fromQQ, msg); } 142 | int EVEPrivateMsg::sendMsg(const std::string& msg) const { return sendPrivateMsg(fromQQ, msg); } 143 | int EVEGroupMsg::sendMsg(const char* msg) const { return sendGroupMsg(fromGroup, msg); } 144 | int EVEGroupMsg::sendMsg(const std::string& msg) const { return sendGroupMsg(fromGroup, msg); } 145 | int EVEDiscussMsg::sendMsg(const char* msg) const { return sendDiscussMsg(fromDiscuss, msg); } 146 | int EVEDiscussMsg::sendMsg(const std::string& msg) const { return sendDiscussMsg(fromDiscuss, msg); } 147 | 148 | EVEDiscussMsg::EVEDiscussMsg(const int subType, const int msgId, const long long fromDiscuss, const long long fromQQ, const char* msg, const int font) 149 | : EVEMsg(subType, msgId, fromQQ, msg, font), fromDiscuss(fromDiscuss) 150 | { 151 | } 152 | 153 | bool EVEDiscussMsg::leave() const { return !setDiscussLeave(fromDiscuss); } 154 | 155 | void EVEStatus::color_green() { color = 1; } 156 | void EVEStatus::color_orange() { color = 2; } 157 | void EVEStatus::color_red() { color = 3; } 158 | void EVEStatus::color_crimson() { color = 4; } 159 | void EVEStatus::color_black() { color = 5; } 160 | void EVEStatus::color_gray() { color = 6; } 161 | 162 | std::string CQ::statusEVEreturn(EVEStatus& eve) 163 | { 164 | Unpack pack; 165 | std::string _ret = pack.add(eve.data).add(eve.dataf).add(eve.color).getAll(); 166 | _ret = base64_encode(_ret); 167 | return _ret; 168 | } 169 | 170 | EVERequest::EVERequest(const int sendTime, const long long fromQQ, const char* msg, const char* responseFlag) 171 | : sendTime(sendTime), fromQQ(fromQQ), msg(msg), responseFlag(responseFlag) 172 | { 173 | } 174 | 175 | EVERequestAddFriend::EVERequestAddFriend(const int subType, const int sendTime, const long long fromQQ, const char* msg, 176 | const char* responseFlag) 177 | : EVERequest(sendTime, fromQQ, msg, responseFlag), subType(subType), fromGroup(0) 178 | { 179 | } 180 | 181 | void EVERequestAddFriend::pass(const std::string& msg) const 182 | { 183 | setFriendAddRequest(responseFlag, RequestAccepted, msg.c_str()); 184 | } 185 | 186 | void EVERequestAddFriend::fail(const std::string& msg) const 187 | { 188 | setFriendAddRequest(responseFlag, RequestRefused, msg.c_str()); 189 | } 190 | 191 | EVERequestAddGroup::EVERequestAddGroup(const int subType, const int sendTime, const long long fromGroup, const long long fromQQ, 192 | const char* const msg, const char* const responseFlag) 193 | : EVERequest(sendTime, fromQQ, msg, responseFlag), subType(subType), fromGroup(fromGroup) 194 | { 195 | } 196 | 197 | void EVERequestAddGroup::pass(const std::string& msg) const 198 | { 199 | setGroupAddRequest(responseFlag, subType, RequestAccepted, msg.c_str()); 200 | } 201 | 202 | void EVERequestAddGroup::fail(const std::string& msg) const 203 | { 204 | setGroupAddRequest(responseFlag, subType, RequestRefused, msg.c_str()); 205 | } 206 | 207 | AnonymousInfo::AnonymousInfo(const char* msg) 208 | { 209 | if (msg != nullptr && msg[0] != '\0') 210 | { 211 | Unpack p(base64_decode(msg)); 212 | AID = p.getLong(); 213 | AnonymousNick = p.getstring(); 214 | } 215 | } 216 | 217 | regexMsg::regexMsg(const std::string& msg) 218 | { 219 | Unpack msgs(base64_decode(msg)); 220 | auto len = msgs.getInt(); //获取参数数量 221 | while (len-- > 0) 222 | { 223 | auto tep = msgs.getUnpack(); 224 | const auto key = tep.getstring(); 225 | const auto value = tep.getstring(); 226 | if (key == "") 227 | { 228 | return; 229 | } 230 | regexMap[key] = value; 231 | } 232 | } 233 | 234 | std::string regexMsg::get(const std::string& key) 235 | { 236 | return regexMap[key]; 237 | } 238 | 239 | std::string regexMsg::operator[](const std::string& key) 240 | { 241 | return regexMap[key]; 242 | } 243 | -------------------------------------------------------------------------------- /src/CQSDK/CQTools.cpp: -------------------------------------------------------------------------------- 1 | #include "CQTools.h" 2 | 3 | #include 4 | 5 | 6 | using namespace std; 7 | 8 | //代码来源于网络 9 | static const string base64_chars = 10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 11 | "abcdefghijklmnopqrstuvwxyz" 12 | "0123456789+/"; 13 | 14 | static bool is_base64(const unsigned char c) 15 | { 16 | return isalnum(c) || c == '+' || c == '/'; 17 | } 18 | 19 | string base64_encode(const string& decode_string) 20 | { 21 | auto in_len = decode_string.size(); 22 | auto bytes_to_encode = decode_string.data(); 23 | string ret; 24 | auto i = 0; 25 | int j; 26 | unsigned char char_array_3[3]; 27 | unsigned char char_array_4[4]; 28 | 29 | while (in_len--) 30 | { 31 | char_array_3[i++] = *(bytes_to_encode++); 32 | if (i == 3) 33 | { 34 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 35 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 36 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 37 | char_array_4[3] = char_array_3[2] & 0x3f; 38 | 39 | for (i = 0; (i < 4); i++) 40 | ret += base64_chars[char_array_4[i]]; 41 | i = 0; 42 | } 43 | } 44 | 45 | if (i) 46 | { 47 | for (j = i; j < 3; j++) 48 | char_array_3[j] = '\0'; 49 | 50 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 51 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 52 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 53 | char_array_4[3] = char_array_3[2] & 0x3f; 54 | 55 | for (j = 0; (j < i + 1); j++) 56 | ret += base64_chars[char_array_4[j]]; 57 | 58 | while (i++ < 3) 59 | ret += '='; 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | string base64_decode(const string& encoded_string) 66 | { 67 | int in_len = encoded_string.size(); 68 | auto i = 0; 69 | int j; 70 | auto in_ = 0; 71 | unsigned char char_array_4[4], char_array_3[3]; 72 | string ret; 73 | 74 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) 75 | { 76 | char_array_4[i++] = encoded_string[in_]; 77 | in_++; 78 | if (i == 4) 79 | { 80 | for (i = 0; i < 4; i++) 81 | char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); 82 | 83 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 84 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 85 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 86 | 87 | for (i = 0; (i < 3); i++) 88 | ret += char_array_3[i]; 89 | i = 0; 90 | } 91 | } 92 | 93 | if (i) 94 | { 95 | for (j = i; j < 4; j++) 96 | char_array_4[j] = 0; 97 | 98 | for (j = 0; j < 4; j++) 99 | char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); 100 | 101 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 102 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 103 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 104 | 105 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 106 | } 107 | 108 | return ret; 109 | } 110 | 111 | std::string& msg_replace(std::string& s, const std::string& old, const std::string& n) 112 | { 113 | size_t st = 0; 114 | while ((st = s.find(old, st)) < s.size()) 115 | { 116 | s.replace(st, old.size(), n); 117 | st += n.size(); 118 | } 119 | return s; 120 | } 121 | 122 | std::string& msg_encode(std::string& s, const bool isCQ) 123 | { 124 | msg_replace(s, "&", "&"); 125 | msg_replace(s, "[", "["); 126 | msg_replace(s, "]", "]"); 127 | msg_replace(s, "\t", ","); 128 | if (isCQ) 129 | msg_replace(s, ",", ","); 130 | return s; 131 | } 132 | 133 | std::string& msg_decode(std::string& s, const bool isCQ) 134 | { 135 | if (isCQ) 136 | msg_replace(s, ",", ","); 137 | msg_replace(s, "[", "["); 138 | msg_replace(s, "]", "]"); 139 | msg_replace(s, ",", "\t"); 140 | msg_replace(s, "&", "&"); 141 | return s; 142 | } 143 | -------------------------------------------------------------------------------- /src/CQSDK/CQstream.cpp: -------------------------------------------------------------------------------- 1 | /*此文件是下面三个头文件的实现*/ 2 | #include 3 | #include "CQAPI_EX.h" 4 | #include "bufstream.h" 5 | #include "CQLogger.h" 6 | #include "CQMsgSend.h" 7 | #include "CQconstant.h" 8 | 9 | using namespace CQ; 10 | using namespace std; 11 | 12 | logger::logger(std::string title) : title(std::move(title)) 13 | { 14 | } 15 | 16 | void logger::setTitle(std::string title) { this->title = std::move(title); } 17 | 18 | void logger::Debug(const std::string& msg) const { Debug(msg.c_str()); } 19 | 20 | void logger::Info(const std::string& msg) const { Info(msg.c_str()); } 21 | 22 | void logger::InfoSuccess(const std::string& msg) const { InfoSuccess(msg.c_str()); } 23 | 24 | void logger::InfoRecv(const std::string& msg) const { InfoRecv(msg.c_str()); } 25 | 26 | void logger::InfoSend(const std::string& msg) const { InfoSend(msg.c_str()); } 27 | 28 | void logger::Warning(const std::string& msg) const { Warning(msg.c_str()); } 29 | 30 | void logger::Error(const std::string& msg) const { Error(msg.c_str()); } 31 | 32 | void logger::Fatal(const std::string& msg) const { Fatal(msg.c_str()); } 33 | 34 | void logger::Debug(const char* const msg) const { addLog(Log_Debug, title.c_str(), msg); } 35 | 36 | void logger::Info(const char* const msg) const { addLog(Log_Info, title.c_str(), msg); } 37 | 38 | void logger::InfoSuccess(const char* const msg) const { addLog(Log_InfoSuccess, title.c_str(), msg); } 39 | 40 | void logger::InfoRecv(const char* const msg) const { addLog(Log_InfoRecv, title.c_str(), msg); } 41 | 42 | void logger::InfoSend(const char* const msg) const { addLog(Log_InfoSend, title.c_str(), msg); } 43 | 44 | void logger::Warning(const char* const msg) const { addLog(Log_Warning, title.c_str(), msg); } 45 | 46 | void logger::Error(const char* const msg) const { addLog(Log_Error, title.c_str(), msg); } 47 | 48 | void logger::Fatal(const char* const msg) const { addLog(Log_Fatal, title.c_str(), msg); } 49 | 50 | logstream logger::Debug() const { return logstream(title, Log_Debug); } 51 | 52 | logstream logger::Info() const { return logstream(title, Log_Info); } 53 | 54 | logstream logger::InfoSuccess() const { return logstream(title, Log_InfoSuccess); } 55 | 56 | logstream logger::InfoRecv() const { return logstream(title, Log_InfoRecv); } 57 | 58 | logstream logger::InfoSend() const { return logstream(title, Log_InfoSend); } 59 | 60 | logstream logger::Warning() const { return logstream(title, Log_Warning); } 61 | 62 | logstream logger::Error() const { return logstream(title, Log_Error); } 63 | 64 | logstream logger::Fatal() const { return logstream(title, Log_Fatal); } 65 | 66 | void CQ::send(CQstream& log) 67 | { 68 | log.send(); 69 | log.clear(); 70 | } 71 | 72 | void CQ::flush(CQstream& log) { log.flush(); } 73 | void CQ::endl(CQstream& log) { log << "\r\n"; } 74 | 75 | void CQstream::clear() { buf.clear(); } 76 | 77 | CQstream& CQstream::append(const string& s) 78 | { 79 | buf += s; 80 | return *this; 81 | } 82 | 83 | CQstream& CQstream::operator<<(const string& s) { return append(s); } 84 | 85 | CQstream& CQstream::append(const int& i) 86 | { 87 | buf += to_string(i); 88 | return *this; 89 | } 90 | 91 | CQstream& CQstream::operator<<(const int& i) { return append(i); } 92 | 93 | CQstream& CQstream::append(const size_t& i) 94 | { 95 | buf += to_string(i); 96 | return *this; 97 | } 98 | 99 | CQstream& CQstream::operator<<(const size_t& i) { return append(i); } 100 | 101 | CQstream& CQstream::append(const long long& l) 102 | { 103 | buf += to_string(l); 104 | return *this; 105 | } 106 | 107 | CQstream& CQstream::operator<<(const long long& l) { return append(l); } 108 | 109 | CQstream& CQstream::append(const char* const c) 110 | { 111 | buf += c; 112 | return *this; 113 | } 114 | 115 | CQstream& CQstream::operator<<(const char* const c) { return append(c); } 116 | 117 | CQstream& CQstream::operator<<(void (*control)(CQstream&)) 118 | { 119 | control(*this); 120 | return *this; 121 | } 122 | 123 | void CQstream::flush() { send(); } 124 | 125 | inline CQstream::~CQstream() = default; 126 | 127 | inline logstream::logstream(std::string title, const int Log_flag) : flag(Log_flag), title(std::move(title)) 128 | { 129 | } 130 | 131 | void logstream::send() 132 | { 133 | if (buf.empty())return; 134 | addLog(flag, title.c_str(), buf.c_str()); 135 | } 136 | 137 | msg::msg(const long long GroupID_Or_QQID, const msgtype Type) : ID(GroupID_Or_QQID), subType(Type) 138 | { 139 | } 140 | 141 | msg::msg(const long long GroupID_Or_QQID, const int Type) : ID(GroupID_Or_QQID), subType(Type) 142 | { 143 | } 144 | 145 | void msg::send() 146 | { 147 | if (buf.empty())return; 148 | switch (subType) 149 | { 150 | case Friend: //好友 151 | sendPrivateMsg(ID, buf); 152 | break; 153 | case Group: //群 154 | sendGroupMsg(ID, buf); 155 | break; 156 | case Discuss: //讨论组 157 | sendDiscussMsg(ID, buf); 158 | break; 159 | default: 160 | static logger log("异常报告"); 161 | log.Warning() 162 | << "消息发送异常" 163 | << ",类别:" << ID 164 | << ",原文: " << buf 165 | << CQ::send; 166 | break; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/CQSDK/Unpack.cpp: -------------------------------------------------------------------------------- 1 | #include "Unpack.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | //打印内存数据 10 | void show(void* t, const int len) 11 | { 12 | const auto p = static_cast(t); 13 | cout << "{"; 14 | for (auto i = 0; i < len; ++i) 15 | { 16 | cout << static_cast(p[i]); 17 | if (i != len - 1)cout << ", "; 18 | } 19 | cout << "}" << endl; 20 | } 21 | 22 | //内存翻转 23 | unsigned char* Flip(unsigned char* const str, int len) 24 | { 25 | auto f = 0; 26 | --len; 27 | while (f < len) 28 | { 29 | const auto p = str[len]; 30 | str[len] = str[f]; 31 | str[f] = p; 32 | ++f; 33 | --len; 34 | } 35 | return str; 36 | } 37 | 38 | //到字节集... 39 | //在原有的数据基础上操作 40 | template 41 | unsigned char* toBin(ClassType& i) 42 | { 43 | return Flip(reinterpret_cast(&i), sizeof(ClassType)); 44 | } 45 | 46 | Unpack& Unpack::setData(const char* i, const int len) 47 | { 48 | buff.assign(i, i + len); 49 | return *this; 50 | } 51 | 52 | Unpack::Unpack() = default; 53 | 54 | Unpack::Unpack(const char* data) 55 | { 56 | setData(data, strlen(data)); 57 | } 58 | 59 | Unpack::Unpack(std::vector data) 60 | { 61 | buff = std::move(data); 62 | } 63 | 64 | Unpack::Unpack(const std::string& data) 65 | { 66 | setData(data.data(), data.size()); 67 | } 68 | 69 | Unpack& Unpack::clear() 70 | { 71 | buff.clear(); 72 | return *this; 73 | } 74 | 75 | int Unpack::len() const 76 | { 77 | return buff.size(); 78 | } 79 | 80 | Unpack& Unpack::add(int i) 81 | { 82 | const auto t = toBin(i); 83 | buff.insert(buff.end(), t, t + sizeof(int)); 84 | return *this; 85 | } 86 | 87 | int Unpack::getInt() 88 | { 89 | const auto len = sizeof(int); 90 | if (buff.size() < len)return 0; 91 | 92 | const auto ret = *reinterpret_cast(Flip(&(buff[0]), len)); 93 | buff.erase(buff.begin(), buff.begin() + len); 94 | return ret; 95 | } 96 | 97 | Unpack& Unpack::add(long long i) 98 | { 99 | const auto t = toBin(i); 100 | buff.insert(buff.end(), t, t + sizeof(long long)); 101 | return *this; 102 | } 103 | 104 | long long Unpack::getLong() 105 | { 106 | const auto len = sizeof(long long); 107 | if (buff.size() < len)return 0; 108 | 109 | const auto ret = *reinterpret_cast(Flip(&(buff[0]), len)); 110 | buff.erase(buff.begin(), buff.begin() + len); 111 | return ret; 112 | } 113 | 114 | Unpack& Unpack::add(short i) 115 | { 116 | const auto t = toBin(i); 117 | buff.insert(buff.end(), t, t + sizeof(short)); 118 | return *this; 119 | } 120 | 121 | short Unpack::getshort() 122 | { 123 | const auto len = sizeof(short); 124 | if (buff.size() < len)return 0; 125 | 126 | const auto ret = *reinterpret_cast(Flip(&(buff[0]), len)); 127 | buff.erase(buff.begin(), buff.begin() + len); 128 | return ret; 129 | } 130 | 131 | Unpack& Unpack::add(const unsigned char* i, const short len) 132 | { 133 | if (len < 0) 134 | return *this; 135 | add(len); 136 | if (len) 137 | buff.insert(buff.end(), i, i + len); 138 | return *this; 139 | } 140 | 141 | std::vector Unpack::getchars() 142 | { 143 | const auto len = getshort(); 144 | if (buff.size() < static_cast(len))return vector(); 145 | 146 | auto tep = vector(buff.begin(), buff.begin() + len); 147 | buff.erase(buff.begin(), buff.begin() + len); 148 | return tep; 149 | } 150 | 151 | Unpack& Unpack::add(string i) 152 | { 153 | if (i.empty()) //字符串长度为0,直接放入长度0 154 | { 155 | add(static_cast(0)); 156 | return *this; 157 | } 158 | if (i.size() > 32767) //字符串长度超出限制, 159 | { 160 | i = i.substr(0, 32767); 161 | } 162 | 163 | add(reinterpret_cast(i.data()), static_cast(i.size())); 164 | 165 | return *this; 166 | } 167 | 168 | string Unpack::getstring() 169 | { 170 | auto tep = getchars(); 171 | if (tep.empty())return ""; 172 | 173 | tep.push_back(static_cast(0)); 174 | return string(reinterpret_cast(&tep[0])); 175 | } 176 | 177 | Unpack& Unpack::add(Unpack& i) 178 | { 179 | add(i.getAll()); 180 | return *this; 181 | } 182 | 183 | Unpack Unpack::getUnpack() { return Unpack(getchars()); } 184 | 185 | std::string Unpack::getAll() 186 | { 187 | string ret; 188 | for (auto b : buff) 189 | ret += b; 190 | return ret; 191 | } 192 | 193 | void Unpack::show() 194 | { 195 | string out; 196 | auto len = 0; 197 | for (auto c : buff) 198 | { 199 | out.append(to_string(static_cast(c))).append(", "); 200 | ++len; 201 | } 202 | out = to_string(len).append("{").append(out.substr(0, out.size() - 2)).append("}"); 203 | cout << out.data() << endl; 204 | } 205 | -------------------------------------------------------------------------------- /src/SQLiteCpp/Backup.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Backup.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Backup is used to backup a database file in a safe and online way. 5 | * 6 | * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) 7 | * Copyright (c) 2015-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 8 | * 9 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 10 | * or copy at http://opensource.org/licenses/MIT) 11 | */ 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | namespace SQLite 19 | { 20 | 21 | // Initialize resource for SQLite database backup 22 | Backup::Backup(Database& aDestDatabase, 23 | const char* apDestDatabaseName, 24 | Database& aSrcDatabase, 25 | const char* apSrcDatabaseName) 26 | { 27 | mpSQLiteBackup = sqlite3_backup_init(aDestDatabase.getHandle(), 28 | apDestDatabaseName, 29 | aSrcDatabase.getHandle(), 30 | apSrcDatabaseName); 31 | if (nullptr == mpSQLiteBackup) 32 | { 33 | // If an error occurs, the error code and message are attached to the destination database connection. 34 | throw SQLite::Exception(aDestDatabase.getHandle()); 35 | } 36 | } 37 | 38 | Backup::Backup(Database& aDestDatabase, 39 | const std::string& aDestDatabaseName, 40 | Database& aSrcDatabase, 41 | const std::string& aSrcDatabaseName) : 42 | Backup(aDestDatabase, aDestDatabaseName.c_str(), aSrcDatabase, aSrcDatabaseName.c_str()) 43 | { 44 | } 45 | 46 | Backup::Backup(Database &aDestDatabase, Database &aSrcDatabase) : 47 | Backup(aDestDatabase, "main", aSrcDatabase, "main") 48 | { 49 | } 50 | 51 | // Release resource for SQLite database backup 52 | Backup::~Backup() 53 | { 54 | if (mpSQLiteBackup) 55 | { 56 | sqlite3_backup_finish(mpSQLiteBackup); 57 | } 58 | } 59 | 60 | // Execute backup step with a given number of source pages to be copied 61 | int Backup::executeStep(const int aNumPage /* = -1 */) 62 | { 63 | const int res = sqlite3_backup_step(mpSQLiteBackup, aNumPage); 64 | if (SQLITE_OK != res && SQLITE_DONE != res && SQLITE_BUSY != res && SQLITE_LOCKED != res) 65 | { 66 | throw SQLite::Exception(sqlite3_errstr(res), res); 67 | } 68 | return res; 69 | } 70 | 71 | // Get the number of remaining source pages to be copied in this backup process 72 | int Backup::getRemainingPageCount() 73 | { 74 | return sqlite3_backup_remaining(mpSQLiteBackup); 75 | } 76 | 77 | // Get the number of total source pages to be copied in this backup process 78 | int Backup::getTotalPageCount() 79 | { 80 | return sqlite3_backup_pagecount(mpSQLiteBackup); 81 | } 82 | 83 | } // namespace SQLite 84 | -------------------------------------------------------------------------------- /src/SQLiteCpp/Column.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Column.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | 17 | 18 | namespace SQLite 19 | { 20 | 21 | const int INTEGER = SQLITE_INTEGER; 22 | const int FLOAT = SQLITE_FLOAT; 23 | const int TEXT = SQLITE_TEXT; 24 | const int BLOB = SQLITE_BLOB; 25 | const int Null = SQLITE_NULL; 26 | 27 | 28 | // Encapsulation of a Column in a row of the result pointed by the prepared Statement. 29 | Column::Column(Statement::Ptr& aStmtPtr, int aIndex) noexcept : 30 | mStmtPtr(aStmtPtr), 31 | mIndex(aIndex) 32 | { 33 | } 34 | 35 | // Return the named assigned to this result column (potentially aliased) 36 | const char* Column::getName() const noexcept 37 | { 38 | return sqlite3_column_name(mStmtPtr, mIndex); 39 | } 40 | 41 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 42 | // Return the name of the table column that is the origin of this result column 43 | const char* Column::getOriginName() const noexcept 44 | { 45 | return sqlite3_column_origin_name(mStmtPtr, mIndex); 46 | } 47 | #endif 48 | 49 | // Return the integer value of the column specified by its index starting at 0 50 | int Column::getInt() const noexcept 51 | { 52 | return sqlite3_column_int(mStmtPtr, mIndex); 53 | } 54 | 55 | // Return the unsigned integer value of the column specified by its index starting at 0 56 | unsigned Column::getUInt() const noexcept 57 | { 58 | return static_cast(getInt64()); 59 | } 60 | 61 | // Return the 64bits integer value of the column specified by its index starting at 0 62 | long long Column::getInt64() const noexcept 63 | { 64 | return sqlite3_column_int64(mStmtPtr, mIndex); 65 | } 66 | 67 | // Return the double value of the column specified by its index starting at 0 68 | double Column::getDouble() const noexcept 69 | { 70 | return sqlite3_column_double(mStmtPtr, mIndex); 71 | } 72 | 73 | // Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0 74 | const char* Column::getText(const char* apDefaultValue /* = "" */) const noexcept 75 | { 76 | const char* pText = reinterpret_cast(sqlite3_column_text(mStmtPtr, mIndex)); 77 | return (pText?pText:apDefaultValue); 78 | } 79 | 80 | // Return a pointer to the blob value (*not* NULL terminated) of the column specified by its index starting at 0 81 | const void* Column::getBlob() const noexcept 82 | { 83 | return sqlite3_column_blob(mStmtPtr, mIndex); 84 | } 85 | 86 | // Return a std::string to a TEXT or BLOB column 87 | std::string Column::getString() const 88 | { 89 | // Note: using sqlite3_column_blob and not sqlite3_column_text 90 | // - no need for sqlite3_column_text to add a \0 on the end, as we're getting the bytes length directly 91 | const char *data = static_cast(sqlite3_column_blob(mStmtPtr, mIndex)); 92 | 93 | // SQLite docs: "The safest policy is to invoke… sqlite3_column_blob() followed by sqlite3_column_bytes()" 94 | // Note: std::string is ok to pass nullptr as first arg, if length is 0 95 | return std::string(data, sqlite3_column_bytes(mStmtPtr, mIndex)); 96 | } 97 | 98 | // Return the type of the value of the column 99 | int Column::getType() const noexcept 100 | { 101 | return sqlite3_column_type(mStmtPtr, mIndex); 102 | } 103 | 104 | // Return the number of bytes used by the text value of the column 105 | int Column::getBytes() const noexcept 106 | { 107 | return sqlite3_column_bytes(mStmtPtr, mIndex); 108 | } 109 | 110 | // Standard std::ostream inserter 111 | std::ostream& operator<<(std::ostream& aStream, const Column& aColumn) 112 | { 113 | aStream.write(aColumn.getText(), aColumn.getBytes()); 114 | return aStream; 115 | } 116 | 117 | 118 | } // namespace SQLite 119 | -------------------------------------------------------------------------------- /src/SQLiteCpp/Database.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Database.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Management of a SQLite Database Connection. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | 12 | #define _CRT_SECURE_NO_WARNINGS 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifndef SQLITE_DETERMINISTIC 26 | #define SQLITE_DETERMINISTIC 0x800 27 | #endif // SQLITE_DETERMINISTIC 28 | 29 | 30 | namespace SQLite 31 | { 32 | 33 | const int OPEN_READONLY = SQLITE_OPEN_READONLY; 34 | const int OPEN_READWRITE = SQLITE_OPEN_READWRITE; 35 | const int OPEN_CREATE = SQLITE_OPEN_CREATE; 36 | const int OPEN_URI = SQLITE_OPEN_URI; 37 | 38 | const int OK = SQLITE_OK; 39 | 40 | const char* VERSION = SQLITE_VERSION; 41 | const int VERSION_NUMBER = SQLITE_VERSION_NUMBER; 42 | 43 | // Return SQLite version string using runtime call to the compiled library 44 | const char* getLibVersion() noexcept 45 | { 46 | return sqlite3_libversion(); 47 | } 48 | 49 | // Return SQLite version number using runtime call to the compiled library 50 | int getLibVersionNumber() noexcept 51 | { 52 | return sqlite3_libversion_number(); 53 | } 54 | 55 | 56 | // Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags. 57 | Database::Database(const char* apFilename, 58 | const int aFlags /* = SQLite::OPEN_READONLY*/, 59 | const int aBusyTimeoutMs /* = 0 */, 60 | const char* apVfs /* = nullptr*/) : 61 | mFilename(apFilename) 62 | { 63 | sqlite3* handle; 64 | const int ret = sqlite3_open_v2(apFilename, &handle, aFlags, apVfs); 65 | mSQLitePtr.reset(handle); 66 | if (SQLITE_OK != ret) 67 | { 68 | throw SQLite::Exception(handle, ret); 69 | } 70 | if (aBusyTimeoutMs > 0) 71 | { 72 | setBusyTimeout(aBusyTimeoutMs); 73 | } 74 | } 75 | 76 | // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. 77 | void Database::Deleter::operator()(sqlite3* apSQLite) 78 | { 79 | const int ret = sqlite3_close(apSQLite); // Calling sqlite3_close() with a nullptr argument is a harmless no-op. 80 | 81 | // Avoid unreferenced variable warning when build in release mode 82 | (void) ret; 83 | 84 | // Only case of error is SQLITE_BUSY: "database is locked" (some statements are not finalized) 85 | // Never throw an exception in a destructor : 86 | SQLITECPP_ASSERT(SQLITE_OK == ret, "database is locked"); // See SQLITECPP_ENABLE_ASSERT_HANDLER 87 | } 88 | 89 | /** 90 | * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked. 91 | * 92 | * This is useful in multithreaded program to handle case where a table is locked for writting by a thread. 93 | * Any other thread cannot access the table and will receive a SQLITE_BUSY error: 94 | * setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error. 95 | * Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;". 96 | * Default busy timeout is 0ms. 97 | * 98 | * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY 99 | * 100 | * @throw SQLite::Exception in case of error 101 | */ 102 | void Database::setBusyTimeout(const int aBusyTimeoutMs) 103 | { 104 | const int ret = sqlite3_busy_timeout(getHandle(), aBusyTimeoutMs); 105 | check(ret); 106 | } 107 | 108 | // Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...). 109 | int Database::exec(const char* apQueries) 110 | { 111 | const int ret = sqlite3_exec(getHandle(), apQueries, nullptr, nullptr, nullptr); 112 | check(ret); 113 | 114 | // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only) 115 | return sqlite3_changes(getHandle()); 116 | } 117 | 118 | // Shortcut to execute a one step query and fetch the first column of the result. 119 | // WARNING: Be very careful with this dangerous method: you have to 120 | // make a COPY OF THE result, else it will be destroy before the next line 121 | // (when the underlying temporary Statement and Column objects are destroyed) 122 | // this is an issue only for pointer type result (ie. char* and blob) 123 | // (use the Column copy-constructor) 124 | Column Database::execAndGet(const char* apQuery) 125 | { 126 | Statement query(*this, apQuery); 127 | (void)query.executeStep(); // Can return false if no result, which will throw next line in getColumn() 128 | return query.getColumn(0); 129 | } 130 | 131 | // Shortcut to test if a table exists. 132 | bool Database::tableExists(const char* apTableName) 133 | { 134 | Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?"); 135 | query.bind(1, apTableName); 136 | (void)query.executeStep(); // Cannot return false, as the above query always return a result 137 | return (1 == query.getColumn(0).getInt()); 138 | } 139 | 140 | // Get the rowid of the most recent successful INSERT into the database from the current connection. 141 | long long Database::getLastInsertRowid() const noexcept 142 | { 143 | return sqlite3_last_insert_rowid(getHandle()); 144 | } 145 | 146 | // Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection. 147 | int Database::getTotalChanges() const noexcept 148 | { 149 | return sqlite3_total_changes(getHandle()); 150 | } 151 | 152 | // Return the numeric result code for the most recent failed API call (if any). 153 | int Database::getErrorCode() const noexcept 154 | { 155 | return sqlite3_errcode(getHandle()); 156 | } 157 | 158 | // Return the extended numeric result code for the most recent failed API call (if any). 159 | int Database::getExtendedErrorCode() const noexcept 160 | { 161 | return sqlite3_extended_errcode(getHandle()); 162 | } 163 | 164 | // Return UTF-8 encoded English language explanation of the most recent failed API call (if any). 165 | const char* Database::getErrorMsg() const noexcept 166 | { 167 | return sqlite3_errmsg(getHandle()); 168 | } 169 | 170 | // Attach a custom function to your sqlite database. Assumes UTF8 text representation. 171 | // Parameter details can be found here: http://www.sqlite.org/c3ref/create_function.html 172 | void Database::createFunction(const char* apFuncName, 173 | int aNbArg, 174 | bool abDeterministic, 175 | void* apApp, 176 | void (*apFunc)(sqlite3_context *, int, sqlite3_value **), 177 | void (*apStep)(sqlite3_context *, int, sqlite3_value **) /* = nullptr */, 178 | void (*apFinal)(sqlite3_context *) /* = nullptr */, // NOLINT(readability/casting) 179 | void (*apDestroy)(void *) /* = nullptr */) 180 | { 181 | int textRep = SQLITE_UTF8; 182 | // optimization if deterministic function (e.g. of nondeterministic function random()) 183 | if (abDeterministic) 184 | { 185 | textRep = textRep | SQLITE_DETERMINISTIC; 186 | } 187 | const int ret = sqlite3_create_function_v2(getHandle(), apFuncName, aNbArg, textRep, 188 | apApp, apFunc, apStep, apFinal, apDestroy); 189 | check(ret); 190 | } 191 | 192 | // Load an extension into the sqlite database. Only affects the current connection. 193 | // Parameter details can be found here: http://www.sqlite.org/c3ref/load_extension.html 194 | void Database::loadExtension(const char* apExtensionName, const char *apEntryPointName) 195 | { 196 | #ifdef SQLITE_OMIT_LOAD_EXTENSION 197 | // Unused 198 | (void)apExtensionName; 199 | (void)apEntryPointName; 200 | 201 | throw SQLite::Exception("sqlite extensions are disabled"); 202 | #else 203 | #ifdef SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION // Since SQLite 3.13 (2016-05-18): 204 | // Security warning: 205 | // It is recommended that the SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION method be used to enable only this interface. 206 | // The use of the sqlite3_enable_load_extension() interface should be avoided to keep the SQL load_extension() 207 | // disabled and prevent SQL injections from giving attackers access to extension loading capabilities. 208 | // (NOTE: not using nullptr: cannot pass object of non-POD type 'std::__1::nullptr_t' through variadic function) 209 | int ret = sqlite3_db_config(getHandle(), SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); // NOTE: not using nullptr 210 | #else 211 | int ret = sqlite3_enable_load_extension(getHandle(), 1); 212 | #endif 213 | check(ret); 214 | 215 | ret = sqlite3_load_extension(getHandle(), apExtensionName, apEntryPointName, 0); 216 | check(ret); 217 | #endif 218 | } 219 | 220 | // Set the key for the current sqlite database instance. 221 | void Database::key(const std::string& aKey) const 222 | { 223 | int passLen = static_cast(aKey.length()); 224 | #ifdef SQLITE_HAS_CODEC 225 | if (passLen > 0) 226 | { 227 | const int ret = sqlite3_key(getHandle(), aKey.c_str(), passLen); 228 | check(ret); 229 | } 230 | #else // SQLITE_HAS_CODEC 231 | if (passLen > 0) 232 | { 233 | throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); 234 | } 235 | #endif // SQLITE_HAS_CODEC 236 | } 237 | 238 | // Reset the key for the current sqlite database instance. 239 | void Database::rekey(const std::string& aNewKey) const 240 | { 241 | #ifdef SQLITE_HAS_CODEC 242 | int passLen = aNewKey.length(); 243 | if (passLen > 0) 244 | { 245 | const int ret = sqlite3_rekey(getHandle(), aNewKey.c_str(), passLen); 246 | check(ret); 247 | } 248 | else 249 | { 250 | const int ret = sqlite3_rekey(getHandle(), nullptr, 0); 251 | check(ret); 252 | } 253 | #else // SQLITE_HAS_CODEC 254 | static_cast(aNewKey); // silence unused parameter warning 255 | throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); 256 | #endif // SQLITE_HAS_CODEC 257 | } 258 | 259 | // Test if a file contains an unencrypted database. 260 | bool Database::isUnencrypted(const std::string& aFilename) 261 | { 262 | if (aFilename.empty()) 263 | { 264 | throw SQLite::Exception("Could not open database, the aFilename parameter was empty."); 265 | } 266 | 267 | std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); 268 | char header[16]; 269 | if (fileBuffer.is_open()) 270 | { 271 | fileBuffer.seekg(0, std::ios::beg); 272 | fileBuffer.getline(header, 16); 273 | fileBuffer.close(); 274 | } 275 | else 276 | { 277 | throw SQLite::Exception("Error opening file: " + aFilename); 278 | } 279 | 280 | return strncmp(header, "SQLite format 3\000", 16) == 0; 281 | } 282 | 283 | // Parse header data from a database. 284 | Header Database::getHeaderInfo(const std::string& aFilename) 285 | { 286 | Header h; 287 | unsigned char buf[100]; 288 | char* pBuf = reinterpret_cast(&buf[0]); 289 | char* pHeaderStr = reinterpret_cast(&h.headerStr[0]); 290 | 291 | if (aFilename.empty()) 292 | { 293 | throw SQLite::Exception("Filename parameter is empty"); 294 | } 295 | 296 | { 297 | std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); 298 | if (fileBuffer.is_open()) 299 | { 300 | fileBuffer.seekg(0, std::ios::beg); 301 | fileBuffer.read(pBuf, 100); 302 | fileBuffer.close(); 303 | if (fileBuffer.gcount() < 100) 304 | { 305 | throw SQLite::Exception("File " + aFilename + " is too short"); 306 | } 307 | } 308 | else 309 | { 310 | throw SQLite::Exception("Error opening file " + aFilename); 311 | } 312 | } 313 | 314 | // If the "magic string" can't be found then header is invalid, corrupt or unreadable 315 | strncpy(pHeaderStr, pBuf, 16); 316 | if (strncmp(pHeaderStr, "SQLite format 3", 15) != 0) 317 | { 318 | throw SQLite::Exception("Invalid or encrypted SQLite header in file " + aFilename); 319 | } 320 | 321 | h.pageSizeBytes = (buf[16] << 8) | buf[17]; 322 | h.fileFormatWriteVersion = buf[18]; 323 | h.fileFormatReadVersion = buf[19]; 324 | h.reservedSpaceBytes = buf[20]; 325 | h.maxEmbeddedPayloadFrac = buf[21]; 326 | h.minEmbeddedPayloadFrac = buf[22]; 327 | h.leafPayloadFrac = buf[23]; 328 | 329 | h.fileChangeCounter = 330 | (buf[24] << 24) | 331 | (buf[25] << 16) | 332 | (buf[26] << 8) | 333 | (buf[27] << 0); 334 | 335 | h.databaseSizePages = 336 | (buf[28] << 24) | 337 | (buf[29] << 16) | 338 | (buf[30] << 8) | 339 | (buf[31] << 0); 340 | 341 | h.firstFreelistTrunkPage = 342 | (buf[32] << 24) | 343 | (buf[33] << 16) | 344 | (buf[34] << 8) | 345 | (buf[35] << 0); 346 | 347 | h.totalFreelistPages = 348 | (buf[36] << 24) | 349 | (buf[37] << 16) | 350 | (buf[38] << 8) | 351 | (buf[39] << 0); 352 | 353 | h.schemaCookie = 354 | (buf[40] << 24) | 355 | (buf[41] << 16) | 356 | (buf[42] << 8) | 357 | (buf[43] << 0); 358 | 359 | h.schemaFormatNumber = 360 | (buf[44] << 24) | 361 | (buf[45] << 16) | 362 | (buf[46] << 8) | 363 | (buf[47] << 0); 364 | 365 | h.defaultPageCacheSizeBytes = 366 | (buf[48] << 24) | 367 | (buf[49] << 16) | 368 | (buf[50] << 8) | 369 | (buf[51] << 0); 370 | 371 | h.largestBTreePageNumber = 372 | (buf[52] << 24) | 373 | (buf[53] << 16) | 374 | (buf[54] << 8) | 375 | (buf[55] << 0); 376 | 377 | h.databaseTextEncoding = 378 | (buf[56] << 24) | 379 | (buf[57] << 16) | 380 | (buf[58] << 8) | 381 | (buf[59] << 0); 382 | 383 | h.userVersion = 384 | (buf[60] << 24) | 385 | (buf[61] << 16) | 386 | (buf[62] << 8) | 387 | (buf[63] << 0); 388 | 389 | h.incrementalVaccumMode = 390 | (buf[64] << 24) | 391 | (buf[65] << 16) | 392 | (buf[66] << 8) | 393 | (buf[67] << 0); 394 | 395 | h.applicationId = 396 | (buf[68] << 24) | 397 | (buf[69] << 16) | 398 | (buf[70] << 8) | 399 | (buf[71] << 0); 400 | 401 | h.versionValidFor = 402 | (buf[92] << 24) | 403 | (buf[93] << 16) | 404 | (buf[94] << 8) | 405 | (buf[95] << 0); 406 | 407 | h.sqliteVersion = 408 | (buf[96] << 24) | 409 | (buf[97] << 16) | 410 | (buf[98] << 8) | 411 | (buf[99] << 0); 412 | 413 | return h; 414 | } 415 | 416 | void Database::backup(const char* apFilename, BackupType aType) 417 | { 418 | // Open the database file identified by apFilename 419 | Database otherDatabase(apFilename, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); 420 | 421 | // For a 'Save' operation, data is copied from the current Database to the other. A 'Load' is the reverse. 422 | Database& src = (aType == Save ? *this : otherDatabase); 423 | Database& dest = (aType == Save ? otherDatabase : *this); 424 | 425 | // Set up the backup procedure to copy between the "main" databases of each connection 426 | Backup bkp(dest, src); 427 | bkp.executeStep(); // Execute all steps at once 428 | 429 | // RAII Finish Backup an Close the other Database 430 | } 431 | 432 | } // namespace SQLite 433 | -------------------------------------------------------------------------------- /src/SQLiteCpp/Exception.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Exception.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | 15 | 16 | namespace SQLite 17 | { 18 | 19 | Exception::Exception(const char* aErrorMessage, int ret) : 20 | std::runtime_error(aErrorMessage), 21 | mErrcode(ret), 22 | mExtendedErrcode(-1) 23 | { 24 | } 25 | 26 | Exception::Exception(sqlite3* apSQLite) : 27 | std::runtime_error(sqlite3_errmsg(apSQLite)), 28 | mErrcode(sqlite3_errcode(apSQLite)), 29 | mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) 30 | { 31 | } 32 | 33 | Exception::Exception(sqlite3* apSQLite, int ret) : 34 | std::runtime_error(sqlite3_errmsg(apSQLite)), 35 | mErrcode(ret), 36 | mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) 37 | { 38 | } 39 | 40 | // Return a string, solely based on the error code 41 | const char* Exception::getErrorStr() const noexcept 42 | { 43 | return sqlite3_errstr(mErrcode); 44 | } 45 | 46 | 47 | } // namespace SQLite 48 | -------------------------------------------------------------------------------- /src/SQLiteCpp/Statement.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Statement.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. 5 | * 6 | * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace SQLite 21 | { 22 | 23 | Statement::Statement(Database &aDatabase, const char* apQuery) : 24 | mQuery(apQuery), 25 | mStmtPtr(aDatabase.getHandle(), mQuery), // prepare the SQL query, and ref count (needs Database friendship) 26 | mColumnCount(0), 27 | mbHasRow(false), 28 | mbDone(false) 29 | { 30 | mColumnCount = sqlite3_column_count(mStmtPtr); 31 | } 32 | 33 | Statement::Statement(Statement&& aStatement) noexcept : 34 | mQuery(std::move(aStatement.mQuery)), 35 | mStmtPtr(std::move(aStatement.mStmtPtr)), 36 | mColumnCount(aStatement.mColumnCount), 37 | mbHasRow(aStatement.mbHasRow), 38 | mbDone(aStatement.mbDone) 39 | { 40 | aStatement.mColumnCount = 0; 41 | aStatement.mbHasRow = false; 42 | aStatement.mbDone = false; 43 | } 44 | 45 | // Reset the statement to make it ready for a new execution (see also #clearBindings() bellow) 46 | void Statement::reset() 47 | { 48 | const int ret = tryReset(); 49 | check(ret); 50 | } 51 | 52 | int Statement::tryReset() noexcept 53 | { 54 | mbHasRow = false; 55 | mbDone = false; 56 | return sqlite3_reset(mStmtPtr); 57 | } 58 | 59 | // Clears away all the bindings of a prepared statement (can be associated with #reset() above). 60 | void Statement::clearBindings() 61 | { 62 | const int ret = sqlite3_clear_bindings(mStmtPtr); 63 | check(ret); 64 | } 65 | 66 | int Statement::getIndex(const char * const apName) 67 | { 68 | return sqlite3_bind_parameter_index(mStmtPtr, apName); 69 | } 70 | 71 | // Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 72 | void Statement::bind(const int aIndex, const int aValue) 73 | { 74 | const int ret = sqlite3_bind_int(mStmtPtr, aIndex, aValue); 75 | check(ret); 76 | } 77 | 78 | // Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 79 | void Statement::bind(const int aIndex, const unsigned aValue) 80 | { 81 | const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue); 82 | check(ret); 83 | } 84 | 85 | // Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 86 | void Statement::bind(const int aIndex, const long long aValue) 87 | { 88 | const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue); 89 | check(ret); 90 | } 91 | 92 | // Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 93 | void Statement::bind(const int aIndex, const double aValue) 94 | { 95 | const int ret = sqlite3_bind_double(mStmtPtr, aIndex, aValue); 96 | check(ret); 97 | } 98 | 99 | // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 100 | void Statement::bind(const int aIndex, const std::string& aValue) 101 | { 102 | const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(), 103 | static_cast(aValue.size()), SQLITE_TRANSIENT); 104 | check(ret); 105 | } 106 | 107 | // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 108 | void Statement::bind(const int aIndex, const char* apValue) 109 | { 110 | const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT); 111 | check(ret); 112 | } 113 | 114 | // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 115 | void Statement::bind(const int aIndex, const void* apValue, const int aSize) 116 | { 117 | const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT); 118 | check(ret); 119 | } 120 | 121 | // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 122 | void Statement::bindNoCopy(const int aIndex, const std::string& aValue) 123 | { 124 | const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(), 125 | static_cast(aValue.size()), SQLITE_STATIC); 126 | check(ret); 127 | } 128 | 129 | // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 130 | void Statement::bindNoCopy(const int aIndex, const char* apValue) 131 | { 132 | const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_STATIC); 133 | check(ret); 134 | } 135 | 136 | // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 137 | void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize) 138 | { 139 | const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_STATIC); 140 | check(ret); 141 | } 142 | 143 | // Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement 144 | void Statement::bind(const int aIndex) 145 | { 146 | const int ret = sqlite3_bind_null(mStmtPtr, aIndex); 147 | check(ret); 148 | } 149 | 150 | 151 | // Execute a step of the query to fetch one row of results 152 | bool Statement::executeStep() 153 | { 154 | const int ret = tryExecuteStep(); 155 | if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem 156 | { 157 | if (ret == sqlite3_errcode(mStmtPtr)) 158 | { 159 | throw SQLite::Exception(mStmtPtr, ret); 160 | } 161 | else 162 | { 163 | throw SQLite::Exception("Statement needs to be reseted", ret); 164 | } 165 | } 166 | 167 | return mbHasRow; // true only if one row is accessible by getColumn(N) 168 | } 169 | 170 | // Execute a one-step query with no expected result 171 | int Statement::exec() 172 | { 173 | const int ret = tryExecuteStep(); 174 | if (SQLITE_DONE != ret) // the statement has finished executing successfully 175 | { 176 | if (SQLITE_ROW == ret) 177 | { 178 | throw SQLite::Exception("exec() does not expect results. Use executeStep."); 179 | } 180 | else if (ret == sqlite3_errcode(mStmtPtr)) 181 | { 182 | throw SQLite::Exception(mStmtPtr, ret); 183 | } 184 | else 185 | { 186 | throw SQLite::Exception("Statement needs to be reseted", ret); 187 | } 188 | } 189 | 190 | // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE) 191 | return sqlite3_changes(mStmtPtr); 192 | } 193 | 194 | int Statement::tryExecuteStep() noexcept 195 | { 196 | if (false == mbDone) 197 | { 198 | const int ret = sqlite3_step(mStmtPtr); 199 | if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it 200 | { 201 | mbHasRow = true; 202 | } 203 | else if (SQLITE_DONE == ret) // no (more) row ready : the query has finished executing 204 | { 205 | mbHasRow = false; 206 | mbDone = true; 207 | } 208 | else 209 | { 210 | mbHasRow = false; 211 | mbDone = false; 212 | } 213 | 214 | return ret; 215 | } 216 | else 217 | { 218 | // Statement needs to be reseted ! 219 | return SQLITE_MISUSE; 220 | } 221 | } 222 | 223 | 224 | // Return a copy of the column data specified by its index starting at 0 225 | // (use the Column copy-constructor) 226 | Column Statement::getColumn(const int aIndex) 227 | { 228 | checkRow(); 229 | checkIndex(aIndex); 230 | 231 | // Share the Statement Object handle with the new Column created 232 | return Column(mStmtPtr, aIndex); 233 | } 234 | 235 | // Return a copy of the column data specified by its column name starting at 0 236 | // (use the Column copy-constructor) 237 | Column Statement::getColumn(const char* apName) 238 | { 239 | checkRow(); 240 | const int index = getColumnIndex(apName); 241 | 242 | // Share the Statement Object handle with the new Column created 243 | return Column(mStmtPtr, index); 244 | } 245 | 246 | // Test if the column is NULL 247 | bool Statement::isColumnNull(const int aIndex) const 248 | { 249 | checkRow(); 250 | checkIndex(aIndex); 251 | return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, aIndex)); 252 | } 253 | 254 | bool Statement::isColumnNull(const char* apName) const 255 | { 256 | checkRow(); 257 | const int index = getColumnIndex(apName); 258 | return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, index)); 259 | } 260 | 261 | // Return the named assigned to the specified result column (potentially aliased) 262 | const char* Statement::getColumnName(const int aIndex) const 263 | { 264 | checkIndex(aIndex); 265 | return sqlite3_column_name(mStmtPtr, aIndex); 266 | } 267 | 268 | #ifdef SQLITE_ENABLE_COLUMN_METADATA 269 | // Return the named assigned to the specified result column (potentially aliased) 270 | const char* Statement::getColumnOriginName(const int aIndex) const 271 | { 272 | checkIndex(aIndex); 273 | return sqlite3_column_origin_name(mStmtPtr, aIndex); 274 | } 275 | #endif 276 | 277 | // Return the index of the specified (potentially aliased) column name 278 | int Statement::getColumnIndex(const char* apName) const 279 | { 280 | // Build the map of column index by name on first call 281 | if (mColumnNames.empty()) 282 | { 283 | for (int i = 0; i < mColumnCount; ++i) 284 | { 285 | const char* pName = sqlite3_column_name(mStmtPtr, i); 286 | mColumnNames[pName] = i; 287 | } 288 | } 289 | 290 | const TColumnNames::const_iterator iIndex = mColumnNames.find(apName); 291 | if (iIndex == mColumnNames.end()) 292 | { 293 | throw SQLite::Exception("Unknown column name."); 294 | } 295 | 296 | return (*iIndex).second; 297 | } 298 | 299 | int Statement::getBindParameterCount() const noexcept 300 | { 301 | return sqlite3_bind_parameter_count(mStmtPtr); 302 | } 303 | 304 | // Return the numeric result code for the most recent failed API call (if any). 305 | int Statement::getErrorCode() const noexcept 306 | { 307 | return sqlite3_errcode(mStmtPtr); 308 | } 309 | 310 | // Return the extended numeric result code for the most recent failed API call (if any). 311 | int Statement::getExtendedErrorCode() const noexcept 312 | { 313 | return sqlite3_extended_errcode(mStmtPtr); 314 | } 315 | 316 | // Return UTF-8 encoded English language explanation of the most recent failed API call (if any). 317 | const char* Statement::getErrorMsg() const noexcept 318 | { 319 | return sqlite3_errmsg(mStmtPtr); 320 | } 321 | 322 | // Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded. 323 | std::string Statement::getExpandedSQL() { 324 | char* expanded = sqlite3_expanded_sql(mStmtPtr); 325 | std::string expandedString(expanded); 326 | sqlite3_free(expanded); 327 | return expandedString; 328 | } 329 | 330 | //////////////////////////////////////////////////////////////////////////////// 331 | // Internal class : shared pointer to the sqlite3_stmt SQLite Statement Object 332 | //////////////////////////////////////////////////////////////////////////////// 333 | 334 | /** 335 | * @brief Prepare the statement and initialize its reference counter 336 | * 337 | * @param[in] apSQLite The sqlite3 database connexion 338 | * @param[in] aQuery The SQL query string to prepare 339 | */ 340 | Statement::Ptr::Ptr(sqlite3* apSQLite, std::string& aQuery) : 341 | mpSQLite(apSQLite), 342 | mpStmt(NULL), 343 | mpRefCount(NULL) 344 | { 345 | const int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast(aQuery.size()), &mpStmt, NULL); 346 | if (SQLITE_OK != ret) 347 | { 348 | throw SQLite::Exception(apSQLite, ret); 349 | } 350 | // Initialize the reference counter of the sqlite3_stmt : 351 | // used to share the mStmtPtr between Statement and Column objects; 352 | // This is needed to enable Column objects to live longer than the Statement objet it refers to. 353 | mpRefCount = new unsigned int(1); // NOLINT(readability/casting) 354 | } 355 | 356 | /** 357 | * @brief Copy constructor increments the ref counter 358 | * 359 | * @param[in] aPtr Pointer to copy 360 | */ 361 | Statement::Ptr::Ptr(const Statement::Ptr& aPtr) : 362 | mpSQLite(aPtr.mpSQLite), 363 | mpStmt(aPtr.mpStmt), 364 | mpRefCount(aPtr.mpRefCount) 365 | { 366 | assert(mpRefCount); 367 | assert(0 != *mpRefCount); 368 | 369 | // Increment the reference counter of the sqlite3_stmt, 370 | // asking not to finalize the sqlite3_stmt during the lifetime of the new objet 371 | ++(*mpRefCount); 372 | } 373 | 374 | Statement::Ptr::Ptr(Ptr&& aPtr) : 375 | mpSQLite(aPtr.mpSQLite), 376 | mpStmt(aPtr.mpStmt), 377 | mpRefCount(aPtr.mpRefCount) 378 | { 379 | aPtr.mpSQLite = nullptr; 380 | aPtr.mpStmt = nullptr; 381 | aPtr.mpRefCount = nullptr; 382 | } 383 | 384 | /** 385 | * @brief Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0 386 | */ 387 | Statement::Ptr::~Ptr() 388 | { 389 | if (mpRefCount) 390 | { 391 | assert(0 != *mpRefCount); 392 | 393 | // Decrement and check the reference counter of the sqlite3_stmt 394 | --(*mpRefCount); 395 | if (0 == *mpRefCount) 396 | { 397 | // If count reaches zero, finalize the sqlite3_stmt, as no Statement nor Column objet use it anymore. 398 | // No need to check the return code, as it is the same as the last statement evaluation. 399 | sqlite3_finalize(mpStmt); 400 | 401 | // and delete the reference counter 402 | delete mpRefCount; 403 | mpRefCount = nullptr; 404 | mpStmt = nullptr; 405 | } 406 | // else, the finalization will be done later, by the last object 407 | } 408 | } 409 | 410 | 411 | } // namespace SQLite 412 | -------------------------------------------------------------------------------- /src/SQLiteCpp/Transaction.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction.cpp 3 | * @ingroup SQLiteCpp 4 | * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. 5 | * 6 | * Copyright (c) 2012-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 | * 8 | * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 | * or copy at http://opensource.org/licenses/MIT) 10 | */ 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | 17 | namespace SQLite 18 | { 19 | 20 | 21 | // Begins the SQLite transaction 22 | Transaction::Transaction(Database& aDatabase) : 23 | mDatabase(aDatabase), 24 | mbCommited(false) 25 | { 26 | mDatabase.exec("BEGIN"); 27 | } 28 | 29 | // Safely rollback the transaction if it has not been committed. 30 | Transaction::~Transaction() 31 | { 32 | if (false == mbCommited) 33 | { 34 | try 35 | { 36 | mDatabase.exec("ROLLBACK"); 37 | } 38 | catch (SQLite::Exception&) 39 | { 40 | // Never throw an exception in a destructor: error if already rollbacked, but no harm is caused by this. 41 | } 42 | } 43 | } 44 | 45 | // Commit the transaction. 46 | void Transaction::commit() 47 | { 48 | if (false == mbCommited) 49 | { 50 | mDatabase.exec("COMMIT"); 51 | mbCommited = true; 52 | } 53 | else 54 | { 55 | throw SQLite::Exception("Transaction already committed."); 56 | } 57 | } 58 | 59 | 60 | } // namespace SQLite 61 | -------------------------------------------------------------------------------- /trpglogger.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.489 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "trpglogger", "trpglogger\trpglogger.vcxproj", "{169F9DF4-45C2-4723-80B5-BD4A759DD93A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Debug|x64.ActiveCfg = Debug|x64 17 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Debug|x64.Build.0 = Debug|x64 18 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Debug|x86.ActiveCfg = Debug|Win32 19 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Debug|x86.Build.0 = Debug|Win32 20 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Release|x64.ActiveCfg = Release|x64 21 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Release|x64.Build.0 = Release|x64 22 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Release|x86.ActiveCfg = Release|Win32 23 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A9F5D2D1-21A5-4124-8939-F44F1E5F77A8} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /trpglogger.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | 2 -------------------------------------------------------------------------------- /trpglogger/APPINFO.h: -------------------------------------------------------------------------------- 1 | /* 2 | * _______ ________ ________ ________ __ 3 | * | __ \ |__ __| | _____| | _____| | | 4 | * | | | | | | | | | |_____ | | 5 | * | | | | | | | | | _____| |__| 6 | * | |__| | __| |__ | |_____ | |_____ __ 7 | * |_______/ |________| |________| |________| |__| 8 | * 9 | * Dice! QQ Dice Robot for TRPG 10 | * Copyright (C) 2018-2019 w4123溯洄 11 | * 12 | * This program is free software: you can redistribute it and/or modify it under the terms 13 | * of the GNU Affero General Public License as published by the Free Software Foundation, 14 | * either version 3 of the License, or (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 17 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 18 | * See the GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License along with this 21 | * program. If not, see . 22 | */ 23 | #pragma once 24 | #ifndef DICE_APPINFO 25 | #define DICE_APPINFO 26 | 27 | #define CQAPPID "com.w4123.trpglogger" 28 | #define CQAPPINFO CQAPIVERTEXT "," CQAPPID 29 | #endif /*DICE_APPINFO*/ 30 | -------------------------------------------------------------------------------- /trpglogger/CQP.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/CQP.lib -------------------------------------------------------------------------------- /trpglogger/EncodingConvert.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/EncodingConvert.cpp -------------------------------------------------------------------------------- /trpglogger/EncodingConvert.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/EncodingConvert.h -------------------------------------------------------------------------------- /trpglogger/GlobalVar.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/GlobalVar.cpp -------------------------------------------------------------------------------- /trpglogger/GlobalVar.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/GlobalVar.h -------------------------------------------------------------------------------- /trpglogger/MsgType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef TRPGLOGGER_MSGTYPE 3 | #define TRPGLOGGER_MSGTYPE 4 | 5 | enum class MsgType 6 | { 7 | Group, 8 | Discuss 9 | }; 10 | 11 | #endif /*TRPGLOGGER_MSGTYPE*/ -------------------------------------------------------------------------------- /trpglogger/S3PutObject.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/S3PutObject.cpp -------------------------------------------------------------------------------- /trpglogger/S3PutObject.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/S3PutObject.h -------------------------------------------------------------------------------- /trpglogger/SaveLog.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/SaveLog.cpp -------------------------------------------------------------------------------- /trpglogger/SaveLog.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/SaveLog.h -------------------------------------------------------------------------------- /trpglogger/com.w4123.trpglogger.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dice-Developer-Team/TrpgLogger/e86219719c345a698d45b537c9bb0895ee55a3b5/trpglogger/com.w4123.trpglogger.json -------------------------------------------------------------------------------- /trpglogger/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : 定义 DLL 应用程序的入口点。 2 | 3 | #include 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | 7 | BOOL APIENTRY DllMain( HMODULE hModule, 8 | DWORD ul_reason_for_call, 9 | LPVOID lpReserved 10 | ) 11 | { 12 | return TRUE; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /trpglogger/main.cpp: -------------------------------------------------------------------------------- 1 | // SNS.cpp : 定义 DLL 应用程序的导出函数。 2 | // 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define WIN32_LEAN_AND_MEAN 12 | #include 13 | 14 | #include "CQEVE_ALL.h" 15 | #include "APPINFO.h" 16 | #include "S3PutObject.h" 17 | #include "GlobalVar.h" 18 | #include "SaveLog.h" 19 | #include "MsgType.h" 20 | #include "EncodingConvert.h" 21 | 22 | void save(); 23 | 24 | EVE_Enable(eventEnable) 25 | { 26 | // 设置应用为已启用 27 | Enabled = true; 28 | 29 | // 初始化Aws API 30 | Aws::InitAPI(options); 31 | 32 | // 获取应用目录 33 | fileLoc = CQ::getAppDirectory(); 34 | 35 | // 获取可执行文件路径 36 | char fileName[MAX_PATH]; 37 | const int ret = GetModuleFileNameA(nullptr, fileName, MAX_PATH); 38 | if (ret == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) 39 | { 40 | return -1; 41 | } 42 | 43 | // 由可执行文件路径获取日志数据库路径 44 | std::string tempLoc(fileName); 45 | dbLoc_UTF8 = GBKToUTF8(tempLoc.substr(0, tempLoc.find_last_of('\\')) + "\\data\\" + std::to_string(CQ::getLoginQQ()) + "\\eventv2.db"); 46 | 47 | std::ifstream readSessionGroup(fileLoc + "Group.session"); 48 | if (readSessionGroup) 49 | { 50 | long long groupNum; 51 | time_t startTime; 52 | while (readSessionGroup >> groupNum >> startTime) 53 | { 54 | LogInfo[groupNum] = startTime; 55 | } 56 | } 57 | readSessionGroup.close(); 58 | 59 | std::ifstream readSessionDiscuss(fileLoc + "Discuss.session"); 60 | if (readSessionDiscuss) 61 | { 62 | long long discussNum; 63 | time_t startTime; 64 | while (readSessionDiscuss >> discussNum >> startTime) 65 | { 66 | LogInfoDiscuss[discussNum] = startTime; 67 | } 68 | } 69 | readSessionDiscuss.close(); 70 | return 0; 71 | } 72 | 73 | // 群聊部分 74 | EVE_GroupMsg_EX(eventGroupMsg) 75 | { 76 | std::string strAt = "[CQ:at,qq=" + std::to_string(CQ::getLoginQQ()) + "]"; 77 | if (eve.message.substr(0, 6) == "[CQ:at") 78 | { 79 | if (eve.message.substr(0, strAt.length()) != strAt) 80 | { 81 | return; 82 | } 83 | eve.message = eve.message.substr(strAt.length()); 84 | } 85 | while (eve.message[0] == ' ') 86 | { 87 | eve.message.erase(eve.message.begin()); 88 | } 89 | if (eve.message.substr(0, 2) == "。") 90 | { 91 | eve.message.erase(eve.message.begin()); 92 | eve.message[0] = '.'; 93 | } 94 | std::transform(eve.message.begin(), eve.message.end(), eve.message.begin(), [](unsigned char c) { return tolower(c); }); 95 | if (eve.message == ".log" || eve.message == ".logstart" || eve.message == ".log start" || eve.message == ".log on" || eve.message == ".logon") 96 | { 97 | eve.message_block(); 98 | if (LogInfo.count(eve.fromGroup)) 99 | { 100 | eve.sendMsg("正在进行日志记录, 无法再次开始!"); 101 | } 102 | else 103 | { 104 | eve.sendMsg("开始日志记录"); 105 | LogInfo[eve.fromGroup] = time(nullptr); 106 | save(); 107 | } 108 | } 109 | else if (eve.message == ".log stop" || eve.message == ".logstop" || eve.message == ".logend" || eve.message == ".log end" || eve.message == ".log off" || eve.message == ".logoff" || eve.message == ".log fin" || eve.message == ".logfin") 110 | { 111 | eve.message_block(); 112 | if (LogInfo.count(eve.fromGroup)) 113 | { 114 | // 开始保存日志 115 | eve.sendMsg("正在保存日志"); 116 | 117 | // 读取记录信息 118 | const time_t time_start(LogInfo[eve.fromGroup]); 119 | 120 | // 获取记录结束时间 121 | const time_t now = time(nullptr); 122 | 123 | // 拼接文件保存地址 124 | const std::string fileName = "group_" + std::to_string(eve.fromGroup) + "_" + std::to_string(time_start) + ".txt"; 125 | const std::string logLoc = fileLoc + fileName; 126 | 127 | // 保存文件 128 | const std::string save_log_res = saveLog(time_start, now, eve.fromGroup, MsgType::Group, logLoc); 129 | if (save_log_res=="SUCCESS") 130 | { 131 | eve.sendMsg("日志记录已结束,文件已保存,正在上传至服务器"); 132 | } 133 | else 134 | { 135 | eve.sendMsg("保存失败,您可以尝试稍后重试此命令再次保存\n错误信息: " + save_log_res); 136 | return; 137 | } 138 | 139 | // 上传文件 140 | const std::string put_obj_res = put_s3_object("dicelogger", fileName, logLoc, "ap-southeast-1"); 141 | if (put_obj_res == "SUCCESS") 142 | { 143 | eve.sendMsg("上传已完成,请访问 https://logpainter.kokona.tech/?s3=" + fileName + " 以查看记录"); 144 | LogInfo.erase(eve.fromGroup); 145 | save(); 146 | } 147 | else 148 | { 149 | eve.sendMsg("上传过程中发生错误,请联系管理员或稍后再次使用此命令重试上传\n错误信息: " + put_obj_res); 150 | } 151 | 152 | } 153 | else 154 | { 155 | eve.sendMsg("没有已开始的日志记录!"); 156 | } 157 | } 158 | else if (eve.message == ".log help" || eve.message == ".loghelp") 159 | { 160 | eve.message_block(); 161 | eve.sendMsg(TrpgLoggerVer + "\n.log \t 启动日志记录\n.log stop\t 停止日志记录\n.log help\t 日志记录帮助"); 162 | } 163 | } 164 | 165 | // 讨论组部分 166 | EVE_DiscussMsg_EX(eventDiscussMsg) 167 | { 168 | std::string strAt = "[CQ:at,qq=" + std::to_string(CQ::getLoginQQ()) + "]"; 169 | if (eve.message.substr(0, 6) == "[CQ:at") 170 | { 171 | if (eve.message.substr(0, strAt.length()) != strAt) 172 | { 173 | return; 174 | } 175 | eve.message = eve.message.substr(strAt.length()); 176 | } 177 | while (eve.message[0] == ' ') 178 | { 179 | eve.message.erase(eve.message.begin()); 180 | } 181 | if (eve.message.substr(0, 2) == "。") 182 | { 183 | eve.message.erase(eve.message.begin()); 184 | eve.message[0] = '.'; 185 | } 186 | std::transform(eve.message.begin(), eve.message.end(), eve.message.begin(), [](unsigned char c) { return tolower(c); }); 187 | if (eve.message == ".log" || eve.message == ".logstart" || eve.message == ".log start" || eve.message == ".log on" || eve.message == ".logon") 188 | { 189 | eve.message_block(); 190 | if (LogInfoDiscuss.count(eve.fromDiscuss)) 191 | { 192 | eve.sendMsg("正在进行日志记录, 无法再次开始!"); 193 | } 194 | else 195 | { 196 | eve.sendMsg("开始日志记录"); 197 | LogInfoDiscuss[eve.fromDiscuss] = time(nullptr); 198 | save(); 199 | } 200 | } 201 | else if (eve.message == ".log stop" || eve.message == ".logstop" || eve.message == ".logend" || eve.message == ".log end" || eve.message == ".log off" || eve.message == ".logoff" || eve.message == ".log fin" || eve.message == ".logfin") 202 | { 203 | eve.message_block(); 204 | if (LogInfoDiscuss.count(eve.fromDiscuss)) 205 | { 206 | // 开始保存日志 207 | eve.sendMsg("正在保存日志"); 208 | 209 | // 读取记录信息 210 | const time_t time_start(LogInfoDiscuss[eve.fromDiscuss]); 211 | 212 | // 获取记录结束时间 213 | const time_t now = time(nullptr); 214 | 215 | // 拼接文件保存地址 216 | const std::string fileName = "discuss_" + std::to_string(eve.fromDiscuss) + "_" + std::to_string(time_start) + ".txt"; 217 | const std::string logLoc = fileLoc + fileName; 218 | 219 | // 保存文件 220 | const std::string save_log_res = saveLog(time_start, now, eve.fromDiscuss, MsgType::Discuss, logLoc); 221 | if (save_log_res == "SUCCESS") 222 | { 223 | eve.sendMsg("日志记录已结束,文件已保存,正在上传至服务器"); 224 | } 225 | else 226 | { 227 | eve.sendMsg("保存失败,您可以尝试稍后重试此命令再次保存\n错误信息: " + save_log_res); 228 | return; 229 | } 230 | 231 | // 上传文件 232 | const std::string put_obj_res = put_s3_object("dicelogger", fileName, logLoc, "ap-southeast-1"); 233 | if (put_obj_res == "SUCCESS") 234 | { 235 | eve.sendMsg("上传已完成,请访问 https://logpainter.kokona.tech/?s3=" + fileName + " 以查看记录"); 236 | LogInfoDiscuss.erase(eve.fromDiscuss); 237 | save(); 238 | } 239 | else 240 | { 241 | eve.sendMsg("上传过程中发生错误,请联系管理员或稍后再次使用此命令重试上传\n错误信息: " + put_obj_res); 242 | } 243 | 244 | } 245 | else 246 | { 247 | eve.sendMsg("没有已开始的日志记录!"); 248 | } 249 | } 250 | else if (eve.message == ".log help" || eve.message == ".loghelp") 251 | { 252 | eve.message_block(); 253 | eve.sendMsg(TrpgLoggerVer + "\n.log \t 启动日志记录\n.log stop\t 停止日志记录\n.log help\t 日志记录帮助"); 254 | } 255 | } 256 | 257 | // 私聊消息部分, 仅返回帮助信息 258 | EVE_PrivateMsg_EX(eventPrivateMsg) 259 | { 260 | if (eve.message.substr(0, 2) == "。") 261 | { 262 | eve.message.erase(eve.message.begin()); 263 | eve.message[0] = '.'; 264 | } 265 | std::transform(eve.message.begin(), eve.message.end(), eve.message.begin(), [](unsigned char c) { return tolower(c); }); 266 | if(eve.message.substr(0,4) == ".log") 267 | { 268 | eve.message_block(); 269 | eve.sendMsg(TrpgLoggerVer + "\n.log \t 启动日志记录\n.log stop\t 停止日志记录\n.log help\t 日志记录帮助\n请注意命令仅会在群/讨论组中生效"); 270 | } 271 | } 272 | void save() 273 | { 274 | std::ofstream saveSessionGroup(fileLoc + "Group.session", std::ios::out | std::ios::trunc); 275 | if (saveSessionGroup) 276 | { 277 | for (const auto& ele : LogInfo) 278 | { 279 | saveSessionGroup << ele.first << " " << ele.second << std::endl; 280 | } 281 | } 282 | saveSessionGroup.close(); 283 | 284 | std::ofstream saveSessionDiscuss(fileLoc + "Discuss.session", std::ios::out | std::ios::trunc); 285 | if (saveSessionDiscuss) 286 | { 287 | for (const auto& ele : LogInfoDiscuss) 288 | { 289 | saveSessionDiscuss << ele.first << " " << ele.second << std::endl; 290 | } 291 | } 292 | saveSessionDiscuss.close(); 293 | } 294 | EVE_Disable(eventDisable) 295 | { 296 | // 设置应用为未启用 297 | Enabled = false; 298 | 299 | // 释放Aws API资源 300 | Aws::ShutdownAPI(options); 301 | 302 | // 保存Session信息并清空内存中的信息 303 | save(); 304 | LogInfo.clear(); 305 | LogInfoDiscuss.clear(); 306 | 307 | return 0; 308 | } 309 | 310 | EVE_Exit(eventExit) 311 | { 312 | // 如果应用未启用, 则在eventDisable中已经释放过资源了,无需再次释放 313 | if (Enabled) 314 | { 315 | return eventDisable(); 316 | } 317 | return 0; 318 | } 319 | 320 | MUST_AppInfo_RETURN(CQAPPID) -------------------------------------------------------------------------------- /trpglogger/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /trpglogger/trpglogger.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {169F9DF4-45C2-4723-80B5-BD4A759DD93A} 24 | Win32Proj 25 | trpglogger 26 | 10.0 27 | com.w4123.trpglogger 28 | 29 | 30 | 31 | DynamicLibrary 32 | true 33 | v142 34 | Unicode 35 | 36 | 37 | DynamicLibrary 38 | false 39 | v142 40 | true 41 | Unicode 42 | 43 | 44 | DynamicLibrary 45 | true 46 | v142 47 | Unicode 48 | 49 | 50 | DynamicLibrary 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | MultiThreadedDebug 77 | static 78 | static 79 | static 80 | 81 | 82 | true 83 | MultiThreadedDebug 84 | static 85 | static 86 | static 87 | 88 | 89 | false 90 | MultiThreaded 91 | static 92 | static 93 | static 94 | 95 | 96 | false 97 | MultiThreaded 98 | static 99 | static 100 | static 101 | 102 | 103 | 104 | NotUsing 105 | Level3 106 | Disabled 107 | true 108 | WIN32;SQLITE_ENABLE_COLUMN_METADATA;_DEBUG;trpglogger_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 109 | true 110 | ..\include;..\include\CQSDK\;..\include\SQLite\;%(AdditionalIncludeDirectories) 111 | MultiThreadedDebug 112 | 113 | 114 | Windows 115 | true 116 | %(AdditionalLibraryDirectories) 117 | 118 | 119 | 120 | 121 | NotUsing 122 | Level3 123 | Disabled 124 | true 125 | _DEBUG;trpglogger_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 126 | true 127 | ..\include;..\include\CQSDK;..\include\SQLite;%(AdditionalIncludeDirectories) 128 | MultiThreadedDebug 129 | 130 | 131 | Windows 132 | true 133 | %(AdditionalLibraryDirectories) 134 | 135 | 136 | 137 | 138 | NotUsing 139 | Level3 140 | MaxSpeed 141 | true 142 | true 143 | true 144 | WIN32;SQLITE_ENABLE_COLUMN_METADATA;NDEBUG;trpglogger_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 145 | true 146 | ..\include;..\include\CQSDK\;..\include\SQLite\;%(AdditionalIncludeDirectories) 147 | MultiThreaded 148 | 149 | 150 | Windows 151 | true 152 | true 153 | true 154 | %(AdditionalLibraryDirectories) 155 | 156 | 157 | 158 | 159 | NotUsing 160 | Level3 161 | MaxSpeed 162 | true 163 | true 164 | true 165 | NDEBUG;trpglogger_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 166 | true 167 | ..\include;..\include\CQSDK;..\include\SQLite;%(AdditionalIncludeDirectories) 168 | MultiThreaded 169 | 170 | 171 | Windows 172 | true 173 | true 174 | true 175 | %(AdditionalLibraryDirectories) 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /trpglogger/trpglogger.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {5e883e14-7481-4d5f-bde7-4e7e9ea260c6} 18 | 19 | 20 | {5da4c863-f9da-4578-a769-5c980f43bacb} 21 | 22 | 23 | {bec1e517-5f10-4dbc-a900-7b8418a6ed79} 24 | 25 | 26 | {8702cb9d-a18e-4a89-bbcf-110abe1973a8} 27 | 28 | 29 | {c503469a-61f8-42e9-97c4-55a80f2a643b} 30 | 31 | 32 | {27d5cd8d-dfb7-4d46-914f-bda141db60c5} 33 | 34 | 35 | 36 | 37 | 头文件\SQLiteCpp 38 | 39 | 40 | 头文件\SQLiteCpp 41 | 42 | 43 | 头文件\SQLiteCpp 44 | 45 | 46 | 头文件\SQLiteCpp 47 | 48 | 49 | 头文件\SQLiteCpp 50 | 51 | 52 | 头文件\SQLiteCpp 53 | 54 | 55 | 头文件\SQLiteCpp 56 | 57 | 58 | 头文件\SQLiteCpp 59 | 60 | 61 | 头文件\SQLiteCpp 62 | 63 | 64 | 头文件\SQLiteCpp 65 | 66 | 67 | 头文件 68 | 69 | 70 | 头文件 71 | 72 | 73 | 头文件 74 | 75 | 76 | 头文件 77 | 78 | 79 | 头文件 80 | 81 | 82 | 头文件\CQSDK 83 | 84 | 85 | 头文件\CQSDK 86 | 87 | 88 | 头文件\CQSDK 89 | 90 | 91 | 头文件\CQSDK 92 | 93 | 94 | 头文件\CQSDK 95 | 96 | 97 | 头文件\CQSDK 98 | 99 | 100 | 头文件\CQSDK 101 | 102 | 103 | 头文件\CQSDK 104 | 105 | 106 | 头文件\CQSDK 107 | 108 | 109 | 头文件\CQSDK 110 | 111 | 112 | 头文件\CQSDK 113 | 114 | 115 | 头文件\CQSDK 116 | 117 | 118 | 头文件\CQSDK 119 | 120 | 121 | 头文件\CQSDK 122 | 123 | 124 | 头文件\CQSDK 125 | 126 | 127 | 头文件\CQSDK 128 | 129 | 130 | 头文件\CQSDK 131 | 132 | 133 | 头文件\CQSDK 134 | 135 | 136 | 头文件\CQSDK 137 | 138 | 139 | 头文件\CQSDK 140 | 141 | 142 | 头文件\CQSDK 143 | 144 | 145 | 头文件\CQSDK 146 | 147 | 148 | 头文件\CQSDK 149 | 150 | 151 | 头文件 152 | 153 | 154 | 头文件\SQLite 155 | 156 | 157 | 158 | 159 | 源文件 160 | 161 | 162 | 源文件 163 | 164 | 165 | 源文件 166 | 167 | 168 | 源文件 169 | 170 | 171 | 源文件 172 | 173 | 174 | 源文件 175 | 176 | 177 | 源文件\CQSDK 178 | 179 | 180 | 源文件\CQSDK 181 | 182 | 183 | 源文件\CQSDK 184 | 185 | 186 | 源文件\CQSDK 187 | 188 | 189 | 源文件\CQSDK 190 | 191 | 192 | 源文件\CQSDK 193 | 194 | 195 | 源文件\SQLiteCpp 196 | 197 | 198 | 源文件\SQLiteCpp 199 | 200 | 201 | 源文件\SQLiteCpp 202 | 203 | 204 | 源文件\SQLiteCpp 205 | 206 | 207 | 源文件\SQLiteCpp 208 | 209 | 210 | 源文件\SQLiteCpp 211 | 212 | 213 | 源文件\SQLite 214 | 215 | 216 | 217 | 218 | 219 | 资源文件 220 | 221 | 222 | 223 | 224 | 资源文件 225 | 226 | 227 | -------------------------------------------------------------------------------- /trpglogger/trpglogger.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------