\\n
(\\d*)(?:.|\\r|\\n)*(\\d*) | (?:.|\\r|\\n)*
(\\d*) | ");
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/controller/vo/ChartsVo.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.controller.vo;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | @Data
8 | public class ChartsVo {
9 | private List
xAxis;
10 | private List yAxis;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/controller/vo/PPChartVo.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.controller.vo;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 | @Data
7 | public class PPChartVo {
8 | private List xAxis;
9 | private List yAxis;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/enums/CompressLevelEnum.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.enums;
2 |
3 | public enum CompressLevelEnum {
4 | JPG,USHORT_555_RGB_PNG,不压缩,
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/enums/ParameterEnum.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.enums;
2 |
3 | public enum ParameterEnum {
4 | USERNAME,
5 | MODE,
6 | DAY,
7 | USER_ID,
8 | QQ,
9 | GROUPID,
10 | ROLE,
11 | FLAG,
12 | HOUR,
13 | NUM,
14 | SEARCH_PARAM,
15 | BOUND,
16 |
17 | USERNAME_LIST,
18 | FILENAME,
19 | URL,
20 | SECOND,
21 | AT,
22 | BEATMAP_ID
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/manager/ApiManager.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.manager;
2 |
3 | import com.google.common.util.concurrent.RateLimiter;
4 | import com.google.gson.*;
5 | import com.google.gson.reflect.TypeToken;
6 | import org.apache.http.NameValuePair;
7 | import org.apache.http.client.utils.URLEncodedUtils;
8 | import org.apache.http.message.BasicNameValuePair;
9 | import org.apache.logging.log4j.LogManager;
10 | import org.apache.logging.log4j.Logger;
11 | import org.springframework.stereotype.Component;
12 | import top.mothership.cabbage.constant.Overall;
13 | import top.mothership.cabbage.pojo.osu.*;
14 |
15 | import java.io.BufferedReader;
16 | import java.io.IOException;
17 | import java.io.InputStreamReader;
18 | import java.net.HttpURLConnection;
19 | import java.net.URL;
20 | import java.util.ArrayList;
21 | import java.util.Base64;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 |
25 | @Component
26 | public class ApiManager {
27 | private final String getUserURL = "https://osu.ppy.sh/api/get_user";
28 | private final String getBPURL = "https://osu.ppy.sh/api/get_user_best";
29 | private final String getMapURL = "https://osu.ppy.sh/api/get_beatmaps";
30 | private final String getRecentURL = "https://osu.ppy.sh/api/get_user_recent";
31 | private final String getScoreURL = "https://osu.ppy.sh/api/get_scores";
32 | private final String getMatchURL = "https://osu.ppy.sh/api/get_match";
33 |
34 | private final String getReplayURL = "https://osu.ppy.sh/api/get_replay";
35 |
36 | private final String key = Overall.CABBAGE_CONFIG.getString("apikey");
37 | private Logger logger = LogManager.getLogger(this.getClass());
38 |
39 |
40 |
41 | public Userinfo getUser(Integer mode, String username) {
42 | String result = accessAPI("user", username, "string", null, null, null, null, mode);
43 | Userinfo userFromAPI = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Userinfo.class);
44 | if (userFromAPI != null) {
45 | //请求API时加入mode的标记,并且修复Rank问题
46 | userFromAPI.setMode(mode);
47 | fixRank(userFromAPI);
48 | }
49 | return userFromAPI;
50 | }
51 |
52 | public Userinfo getUser(Integer mode, Integer userId) {
53 | String result = accessAPI("user", String.valueOf(userId), "id", null, null, null, null, mode);
54 | Userinfo userFromAPI = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Userinfo.class);
55 | if (userFromAPI != null) {
56 | //请求API时加入mode的标记,并且修复Rank问题
57 | userFromAPI.setMode(mode);
58 | fixRank(userFromAPI);
59 | }
60 | return userFromAPI;
61 | }
62 |
63 | public Beatmap getBeatmap(Integer bid) {
64 | String result = accessAPI("beatmap", null, null, String.valueOf(bid), null, null, null, null);
65 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Beatmap.class);
66 | }
67 |
68 | public List getBeatmaps(Integer sid) {
69 | String result = accessAPI("beatmaps", null, null, String.valueOf(sid), null, null, null, null);
70 | result = "[" + result + "]";
71 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, new TypeToken>() {}.getType());
72 | }
73 |
74 | public Beatmap getBeatmap(String hash) {
75 | String result = accessAPI("beatmapHash", null, null, null, String.valueOf(hash), null, null, null);
76 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Beatmap.class);
77 | }
78 |
79 | public List getBP(Integer mode, String username) {
80 | String result = accessAPI("bp", username, "string", null, null, null, null, mode);
81 | //由于这里用到List,手动补上双括号
82 | result = "[" + result + "]";
83 | List list = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create()
84 | .fromJson(result, new TypeToken>() {}.getType());
85 | for (Score s : list) {
86 | //2018-2-28 15:54:11哪怕是返回单模式的bp也设置模式,避免计算acc时候判断是单模式还是多模式
87 | s.setMode(mode.byteValue());
88 | }
89 | return list;
90 | }
91 |
92 | public List getBP(Integer mode, Integer userId) {
93 | String result = accessAPI("bp", String.valueOf(userId), "id", null, null, null, null, mode);
94 | //由于这里用到List,手动补上双括号
95 | result = "[" + result + "]";
96 | List list = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create()
97 | .fromJson(result, new TypeToken>() {
98 | }.getType());
99 | for (Score s : list) {
100 | s.setMode(mode.byteValue());
101 | }
102 | return list;
103 | }
104 |
105 | public List getBP(String username) {
106 | List resultList = new ArrayList<>();
107 | for (int i = 0; i < 4; i++) {
108 | String result = result = accessAPI("bp", username, "string", null, null, null, null, i);
109 | //由于这里用到List,手动补上双括号
110 | result = "[" + result + "]";
111 | List list = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create()
112 | .fromJson(result, new TypeToken>() {
113 | }.getType());
114 | for (Score s : list) {
115 | s.setMode((byte) i);
116 | }
117 | resultList.addAll(list);
118 | }
119 | return resultList;
120 | }
121 |
122 | public List> getBP(Integer userId) {
123 | List> resultList = new ArrayList<>();
124 | //小技巧,这里i设为byte
125 | for (int i = 0; i < 4; i++) {
126 | String result = accessAPI("bp", String.valueOf(userId), "id", null, null, null, null, i);
127 | //由于这里用到List,手动补上双括号
128 | result = "[" + result + "]";
129 | List list = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create()
130 | .fromJson(result, new TypeToken>() {
131 | }.getType());
132 | for (Score s : list) {
133 | s.setMode((byte) i);
134 | }
135 | resultList.add(list);
136 | }
137 | return resultList;
138 | }
139 |
140 | public Score getRecent(Integer mode, String username) {
141 | String result = accessAPI("recent", username, "string", null, null, null, null, mode);
142 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
143 | .setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Score.class);
144 | }
145 |
146 | public Score getRecent(Integer mode, Integer userId) {
147 | String result = accessAPI("recent", String.valueOf(userId), "id", null, null, null, null, mode);
148 |
149 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
150 | .setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Score.class);
151 | }
152 |
153 | // 用于获取所有的recent
154 | public List getRecents(Integer mode, String username) {
155 | String result = result = accessAPI("recents", username, "string", null, null, null, null, mode);
156 | result = "[" + result + "]";
157 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
158 | .setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, new TypeToken>() {
159 | }.getType());
160 | }
161 |
162 | public List getRecents(Integer mode, Integer userId) {
163 | String result = accessAPI("recents", String.valueOf(userId), "id", null, null, null, null, mode);
164 |
165 | result = "[" + result + "]";
166 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
167 | .setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, new TypeToken>() {
168 | }.getType());
169 | }
170 |
171 | public List getFirstScore(Integer mode, Integer bid, Integer rank) {
172 | String result = accessAPI("first", null, null, String.valueOf(bid), null, rank, null, mode);
173 | result = "[" + result + "]";
174 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create()
175 | .fromJson(result, new TypeToken>() {
176 | }.getType());
177 |
178 | }
179 |
180 | public List getScore(Integer mode, Integer bid, Integer uid) {
181 | String result = accessAPI("score", String.valueOf(uid), "id", String.valueOf(bid), null, null, null, mode);
182 | result = "[" + result + "]";
183 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setDateFormat("yyyy-MM-dd HH:mm:ss").create()
184 | .fromJson(result, new TypeToken>() {
185 | }.getType());
186 | }
187 |
188 | public Lobby getMatch(Integer mid) {
189 | String result = accessAPI("match", null, null, null, null, null, String.valueOf(mid), null);
190 | result = "{" + result + "}";
191 | logger.info(result);
192 | //他妈的ppysb,上面那些获取单个对象的时候给加个中括号,害的我得在下面删掉再在上面看情况加上
193 | return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
194 | //这个的API,date可能是null,而Gson2.8.1会有问题,因此升级Gson库
195 | .setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(result, Lobby.class);
196 | }
197 |
198 | public byte[] getReplay(Long scoreId) {
199 | RateLimiter rateLimiter = RateLimiter.create(0.17);
200 | if (!rateLimiter.tryAcquire()){
201 | logger.error("获取replay接口限速");
202 | return null;
203 | }
204 | String result = accessAPI("replay", null, null, null, null, null, String.valueOf(scoreId), null);
205 | result = "{" + result + "}";
206 | Replay jsonObject = new Gson().fromJson(result, Replay.class);
207 | String content = jsonObject.getContent();
208 | if (content == null){
209 | logger.error("获取replay失败,result:{}", jsonObject);
210 | return null;
211 | }
212 | return Base64.getDecoder().decode(content);
213 | }
214 |
215 | private void fixRank(Userinfo userFromAPI) {
216 | userFromAPI.setCountRankSs(userFromAPI.getCountRankSs() + userFromAPI.getCountRankSsh());
217 | userFromAPI.setCountRankS(userFromAPI.getCountRankS() + userFromAPI.getCountRankSh());
218 | }
219 |
220 | private String accessAPI(String apiType, String uid, String uidType, String bid, String hash, Integer rank, String mid, Integer mode) {
221 | String URL;
222 | String failLog;
223 | String output = null;
224 | HttpURLConnection httpConnection;
225 | List params = new LinkedList<>();
226 | params.add(new BasicNameValuePair("u", uid));
227 | switch (apiType) {
228 | case "user":
229 | URL = getUserURL + "?k=" + key + "&type=" + uidType + "&m=" + mode + "&" + URLEncodedUtils.format(params, "utf-8");
230 | failLog = "玩家" + uid + "请求API:get_user失败五次";
231 | break;
232 | case "bp":
233 | URL = getBPURL + "?k=" + key + "&m=" + mode + "&type=" + uidType + "&limit=100&" + URLEncodedUtils.format(params, "utf-8");
234 | failLog = "玩家" + uid + "请求API:get_user_best失败五次";
235 | break;
236 | case "beatmap":
237 | URL = getMapURL + "?k=" + key + "&b=" + bid;
238 | failLog = "谱面" + bid + "请求API:get_beatmaps失败五次";
239 | break;
240 | case "beatmaps":
241 | URL = getMapURL + "?k=" + key + "&s=" + bid;
242 | failLog = "谱面" + bid + "请求API:get_beatmaps失败五次";
243 | break;
244 | case "beatmapHash":
245 | URL = getMapURL + "?k=" + key + "&h=" + hash;
246 | failLog = "谱面" + bid + "请求API:get_beatmaps失败五次";
247 | break;
248 | case "recent":
249 | URL = getRecentURL + "?k=" + key + "&m=" + mode + "&type=" + uidType + "&limit=1&" + URLEncodedUtils.format(params, "utf-8");
250 | failLog = "玩家" + uid + "请求API:get_recent失败五次";
251 | break;
252 | case "recents":
253 | URL = getRecentURL + "?k=" + key + "&m=" + mode + "&type=" + uidType + "&limit=100&" + URLEncodedUtils.format(params, "utf-8");
254 | failLog = "玩家" + uid + "请求API:get_recent失败五次";
255 | break;
256 | case "first":
257 | URL = getScoreURL + "?k=" + key + "&m=" + mode + "&limit=" + rank + "&b=" + bid;
258 | failLog = "谱面" + bid + "请求API:get_scores失败五次";
259 | break;
260 | case "score":
261 | URL = getScoreURL + "?k=" + key + "&m=" + mode + "&type=" + uidType + "&u=" + uid + "&b=" + bid;
262 | failLog = "谱面" + bid + "请求API:get_scores失败五次";
263 | break;
264 | case "match":
265 | URL = getMatchURL + "?k=" + key + "&mp=" + mid;
266 | failLog = "谱面" + bid + "请求API:get_scores失败五次";
267 | break;
268 | case "replay":
269 | URL = getReplayURL + "?k=" + key + "&s=" + mid;
270 | failLog = "谱面" + bid + "请求API:get_replay失败五次";
271 | break;
272 | default:
273 | logger.info("apiType错误");
274 | return null;
275 |
276 | }
277 |
278 | int retry = 0;
279 | while (retry < 5) {
280 | try {
281 | httpConnection =
282 | (HttpURLConnection) new URL(URL).openConnection();
283 | //设置请求头
284 | httpConnection.setRequestMethod("GET");
285 | httpConnection.setRequestProperty("Accept", "application/json");
286 | httpConnection.setConnectTimeout((int) Math.pow(2, retry + 1) * 1000);
287 | httpConnection.setReadTimeout((int) Math.pow(2, retry + 1) * 1000);
288 | if (httpConnection.getResponseCode() != 200) {
289 | logger.info("HTTP GET请求失败: " + httpConnection.getResponseCode() + ",正在重试第" + (retry + 1) + "次");
290 | retry++;
291 | continue;
292 | }
293 | //读取返回结果
294 | BufferedReader responseBuffer =
295 | new BufferedReader(new InputStreamReader((httpConnection.getInputStream())));
296 | StringBuilder tmp2 = new StringBuilder();
297 | String tmp;
298 | while ((tmp = responseBuffer.readLine()) != null) {
299 | tmp2.append(tmp);
300 | }
301 | //去掉两侧的中括号
302 | output = tmp2.toString().substring(1, tmp2.toString().length() - 1);
303 | //手动关闭流
304 | httpConnection.disconnect();
305 | responseBuffer.close();
306 | break;
307 | } catch (IOException e) {
308 | logger.error("出现IO异常:" + e.getMessage() + ",正在重试第" + (retry + 1) + "次");
309 | retry++;
310 | }
311 | }
312 | if (retry == 5) {
313 | logger.error(failLog);
314 | return null;
315 | }
316 | return output;
317 | }
318 | }
319 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/manager/DayLilyManager.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.manager;
2 |
3 | import com.google.gson.GsonBuilder;
4 | import org.apache.logging.log4j.LogManager;
5 | import org.apache.logging.log4j.Logger;
6 | import org.springframework.stereotype.Component;
7 | import top.mothership.cabbage.pojo.coolq.CqMsg;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.IOException;
11 | import java.io.InputStreamReader;
12 | import java.io.OutputStream;
13 | import java.net.HttpURLConnection;
14 | import java.net.URL;
15 |
16 | @Component
17 | public class DayLilyManager {
18 | private final String baseURL = "http://123.206.100.246:23333/api/getresponse";
19 | private Logger logger = LogManager.getLogger(this.getClass());
20 |
21 | public void sendMsg(CqMsg cqMsg) {
22 | HttpURLConnection httpConnection;
23 | try {
24 | httpConnection =
25 | (HttpURLConnection) new URL(baseURL).openConnection();
26 | httpConnection.setRequestMethod("POST");
27 | httpConnection.setRequestProperty("Accept", "application/json");
28 | httpConnection.setRequestProperty("Content-Type", "application/json");
29 | httpConnection.setDoOutput(true);
30 |
31 | OutputStream os = httpConnection.getOutputStream();
32 | os.write(new GsonBuilder().disableHtmlEscaping().create().toJson(cqMsg).getBytes("UTF-8"));
33 | os.flush();
34 | os.close();
35 | BufferedReader responseBuffer =
36 | new BufferedReader(new InputStreamReader((httpConnection.getInputStream())));
37 | StringBuilder tmp2 = new StringBuilder();
38 | String tmp;
39 | while ((tmp = responseBuffer.readLine()) != null) {
40 | tmp2.append(tmp);
41 | }
42 | //这里不用用到下划线转驼峰
43 | logger.debug(tmp2.toString());
44 | } catch (IOException e) {
45 | e.printStackTrace();
46 | }
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/manager/OneBotManager.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.manager;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.google.gson.reflect.TypeToken;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Component;
8 | import top.mothership.cabbage.pojo.coolq.CqMsg;
9 | import top.mothership.cabbage.pojo.coolq.CqResponse;
10 | import top.mothership.cabbage.pojo.coolq.OneBotApiRequest;
11 | import top.mothership.cabbage.pojo.coolq.QQInfo;
12 | import top.mothership.cabbage.websocket.OneBotMessageHandler;
13 |
14 | import java.io.BufferedReader;
15 | import java.io.IOException;
16 | import java.io.InputStreamReader;
17 | import java.io.OutputStream;
18 | import java.net.HttpURLConnection;
19 | import java.net.URL;
20 | import java.nio.charset.StandardCharsets;
21 | import java.util.List;
22 | import java.util.Random;
23 |
24 | //将OneBot的WS API封装为接口,并托管到Spring
25 | @Component
26 | public class OneBotManager {
27 | @Autowired
28 | private OneBotMessageHandler handler;
29 |
30 |
31 | public CqResponse warn(String msg) {
32 | CqMsg cqMsg = new CqMsg();
33 | cqMsg.setMessageType("private");
34 | cqMsg.setUserId(1335734657L);
35 | cqMsg.setSelfId(1335734629L);
36 | cqMsg.setMessage(msg);
37 | return sendMsg(cqMsg);
38 | }
39 |
40 | public CqResponse warn(String msg, Exception e) {
41 | CqMsg cqMsg = new CqMsg();
42 | cqMsg.setMessageType("private");
43 | cqMsg.setUserId(1335734657L);
44 | cqMsg.setSelfId(1335734629L);
45 | cqMsg.setMessage(msg + " " + e.getMessage());
46 | return sendMsg(cqMsg);
47 | }
48 |
49 | public CqResponse sendMsg(CqMsg cqMsg) {
50 | String baseURL = null;
51 | switch (cqMsg.getSelfId().toString()) {
52 | case "1020640876":
53 | OneBotMessageHandler.sendMessage(cqMsg);
54 | break;
55 | case "1335734629":
56 | OneBotMessageHandler.sendMessage(cqMsg);
57 | return null;
58 | default:
59 | OneBotMessageHandler.sendMessage(cqMsg);
60 | return null;
61 | }
62 | String URL;
63 | switch (cqMsg.getMessageType()) {
64 | case "group":
65 | URL = baseURL + "/send_group_msg";
66 | break;
67 | case "discuss":
68 | URL = baseURL + "/send_discuss_msg";
69 | break;
70 | case "private":
71 | URL = baseURL + "/send_private_msg";
72 | break;
73 | case "smoke":
74 | URL = baseURL + "/set_group_ban";
75 | break;
76 | case "smokeAll":
77 | URL = baseURL + "/set_group_whole_ban";
78 | break;
79 | case "handleInvite":
80 | URL = baseURL + "/set_group_add_request";
81 | break;
82 | case "kick":
83 | URL = baseURL + "/set_group_kick";
84 | break;
85 | default:
86 | return null;
87 | }
88 | HttpURLConnection httpConnection;
89 | try {
90 | httpConnection =
91 | (HttpURLConnection) new URL(URL).openConnection();
92 | httpConnection.setRequestMethod("POST");
93 | httpConnection.setRequestProperty("Accept", "application/json");
94 | httpConnection.setRequestProperty("Content-Type", "application/json");
95 | httpConnection.setDoOutput(true);
96 | OutputStream os = httpConnection.getOutputStream();
97 | //防止转义
98 | //折腾了半天最后是少了UTF-8………………我tm想给自己一巴掌
99 | os.write(new GsonBuilder().disableHtmlEscaping().create().toJson(cqMsg).getBytes(StandardCharsets.UTF_8));
100 | os.flush();
101 | os.close();
102 | BufferedReader responseBuffer =
103 | new BufferedReader(new InputStreamReader((httpConnection.getInputStream())));
104 | StringBuilder tmp2 = new StringBuilder();
105 | String tmp;
106 | while ((tmp = responseBuffer.readLine()) != null) {
107 | tmp2.append(tmp);
108 | }
109 | //这里不用用到下划线转驼峰
110 | CqResponse response = new Gson().fromJson(tmp2.toString(), CqResponse.class);
111 | if (response.getRetCode() != 0 && cqMsg.getMessage().contains("base64")) {
112 | warn("图片发送失败,出现的QQ:" + cqMsg.getSelfId());
113 | }
114 | return response;
115 | } catch (IOException e) {
116 | e.printStackTrace();
117 | return null;
118 | }
119 |
120 | }
121 |
122 | /**
123 | * 获取单个群成员列表,仅针对原有2个QQ所在的群才有用
124 | * 目前用处是全员循环禁言(?什么傻逼功能),超管group info命令,2个chart群给MP系列主群加人用
125 | * 后续如果自由接入的话获取不到其他接入QQ的群信息(懒得写,得在消息入口把发送人QQ一直保持到调用时候)
126 | *
127 | * @param groupId 目标群
128 | * @return
129 | */
130 |
131 | public CqResponse> getGroupMembers(Long groupId) {
132 |
133 | CqMsg cqMsg = new CqMsg();
134 | cqMsg.setGroupId(groupId);
135 | cqMsg.setSelfId(1335734629L);
136 |
137 | OneBotApiRequest request = new OneBotApiRequest();
138 | request.setAction("get_group_member_list");
139 | request.setParams(cqMsg);
140 | request.setEcho(getId());
141 | String response = OneBotMessageHandler.callApi(request);
142 | CqResponse> data = new Gson().fromJson(response, new TypeToken>>() {
143 | }.getType());
144 |
145 | // 如果报错找不到
146 | if (data == null || data.getRetCode() != 0) {
147 | cqMsg.setSelfId(1020640876L);
148 | response = OneBotMessageHandler.callApi(request);
149 | data = new Gson().fromJson(response, new TypeToken>>() {
150 | }.getType());
151 | }
152 | return data;
153 |
154 | }
155 |
156 |
157 | /**
158 | * 获取单个群成员信息,仅针对原有2个QQ所在的群才有用
159 | * 后续如果自由接入的话获取不到其他接入QQ的群信息(懒得写,得在消息入口把发送人QQ一直保持到调用时候)
160 | * 目前调用方:超管group info命令,list msg命令,还有每天循环查2个主群名片是否包含osu ID
161 | *
162 | * @param groupId 目标群
163 | * @param userId 目标人
164 | * @return
165 | */
166 | public CqResponse getGroupMember(Long groupId, Long userId) {
167 |
168 |
169 | CqMsg cqMsg = new CqMsg();
170 | cqMsg.setGroupId(groupId);
171 | cqMsg.setUserId(userId);
172 | cqMsg.setSelfId(1335734629L);
173 |
174 | OneBotApiRequest request = new OneBotApiRequest();
175 | request.setAction("get_group_member_list");
176 | request.setParams(cqMsg);
177 | request.setEcho(getId());
178 | String response = OneBotMessageHandler.callApi(request);
179 | CqResponse data = new Gson().fromJson(response, new TypeToken>() {
180 | }.getType());
181 |
182 | if (data.getRetCode() != 0) {
183 | cqMsg.setSelfId(1020640876L);
184 | response = OneBotMessageHandler.callApi(request);
185 | data = new Gson().fromJson(response, new TypeToken>() {
186 | }.getType());
187 | }
188 | return data;
189 |
190 | }
191 |
192 | private String getId() {
193 | // 创建一个新的Random对象
194 | Random random = new Random();
195 |
196 | // 生成10位随机数
197 | long randomNumber = random.nextLong() % 10000000000L;
198 |
199 | // 确保随机数是10位的
200 | randomNumber = Math.abs(randomNumber);
201 | if (randomNumber < 1000000000L) {
202 | randomNumber += 1000000000L;
203 | }
204 | return System.currentTimeMillis() + "" + randomNumber;
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/manager/OsuApiV2Manager.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.manager;
2 |
3 | import org.apache.logging.log4j.LogManager;
4 | import org.apache.logging.log4j.Logger;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.core.ParameterizedTypeReference;
7 | import org.springframework.http.*;
8 | import org.springframework.stereotype.Component;
9 | import org.springframework.util.LinkedMultiValueMap;
10 | import org.springframework.util.MultiValueMap;
11 | import org.springframework.web.client.RestTemplate;
12 | import org.springframework.web.util.UriComponentsBuilder;
13 | import top.mothership.cabbage.constant.Overall;
14 | import top.mothership.cabbage.pojo.osu.apiv2.OAuthCredentials;
15 | import top.mothership.cabbage.pojo.osu.apiv2.request.UserScoresRequest;
16 | import top.mothership.cabbage.pojo.osu.apiv2.response.ApiV2Score;
17 | import top.mothership.cabbage.pojo.osu.apiv2.response.TokenResponse;
18 | import top.mothership.cabbage.util.qq.ImgUtil;
19 |
20 | import java.time.LocalDateTime;
21 | import java.util.List;
22 |
23 | @Component
24 |
25 | public class OsuApiV2Manager {
26 | private static final String OSU_TOKEN_URL = "https://osu.ppy.sh/oauth/token";
27 | private static final String OSU_API_BASE_URL = "https://osu.ppy.sh/api/v2";
28 | private Logger log = LogManager.getLogger(this.getClass());
29 |
30 |
31 | private final String CLIENT_ID = Overall.CABBAGE_CONFIG.getString("apiV2Id");
32 | private final String CLIENT_SECRET = Overall.CABBAGE_CONFIG.getString("apiV2Secret");
33 | private final String REFRESH_TOKEN = Overall.CABBAGE_CONFIG.getString("refreshToken");
34 |
35 |
36 | private RestTemplate restTemplate = new RestTemplate();
37 |
38 |
39 | private OAuthCredentials credentials = new OAuthCredentials();
40 |
41 | private void updateCredentials(TokenResponse tokenResponse) {
42 | credentials.setAccessToken(tokenResponse.getAccessToken());
43 | credentials.setExpiresIn(tokenResponse.getExpiresIn());
44 | credentials.setCreatedAt(LocalDateTime.now());
45 | }
46 |
47 | /**
48 | * 刷新访问令牌
49 | */
50 | public void refreshAccessToken() {
51 |
52 |
53 | HttpHeaders headers = new HttpHeaders();
54 | headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
55 |
56 | MultiValueMap requestBody = new LinkedMultiValueMap<>();
57 | requestBody.add("client_id", CLIENT_ID);
58 | requestBody.add("client_secret", CLIENT_SECRET);
59 | requestBody.add("refresh_token", REFRESH_TOKEN);
60 | requestBody.add("grant_type", "refresh_token");
61 |
62 | HttpEntity> request = new HttpEntity<>(requestBody, headers);
63 |
64 |
65 | ResponseEntity response = restTemplate.postForEntity(
66 | OSU_TOKEN_URL, request, TokenResponse.class);
67 |
68 | if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
69 | TokenResponse tokenResponse = response.getBody();
70 | updateCredentials(tokenResponse);
71 | log.info("更新API V2 Token成功");
72 | } else {
73 | log.error("更新API V2 Token 失败: {}", response.getStatusCode());
74 | }
75 |
76 | }
77 |
78 |
79 | /**
80 | * 获取有效的访问令牌
81 | */
82 | public String getValidAccessToken() {
83 | if (credentials == null) {
84 | throw new IllegalStateException("OAuth credentials not initialized");
85 | }
86 |
87 | if (credentials.isTokenExpired()) {
88 | refreshAccessToken();
89 | }
90 | log.info("获取API V2 Access Token成功:{}",credentials.getAccessToken());
91 | return credentials.getAccessToken();
92 | }
93 |
94 |
95 | /**
96 | * 获取用户最佳成绩
97 | */
98 | public List getUserBestScores(UserScoresRequest request) {
99 | return getUserScores(request, "best");
100 | }
101 |
102 | /**
103 | * 获取用户最近成绩
104 | */
105 | public List getUserRecentScores(UserScoresRequest request) {
106 | return getUserScores(request, "recent");
107 | }
108 |
109 | /**
110 | * 获取用户成绩通用方法
111 | */
112 | private List getUserScores(UserScoresRequest request, String type) {
113 | // 构建URL
114 | UriComponentsBuilder uriBuilder = UriComponentsBuilder
115 | .fromHttpUrl(OSU_API_BASE_URL + "/users/" + request.getUserId() + "/scores/" + type);
116 |
117 | // 添加可选参数
118 | if (request.getLimit() != null) {
119 | uriBuilder.queryParam("limit", request.getLimit());
120 | }
121 |
122 | if (request.getOffset() != null) {
123 | uriBuilder.queryParam("offset", request.getOffset());
124 | }
125 |
126 | if ("recent".equals(type) && request.getIncludeFails() != null) {
127 | uriBuilder.queryParam("include_fails", request.getIncludeFails() ? "1" : "0");
128 | }
129 |
130 | if (request.getMode() != null) {
131 | uriBuilder.queryParam("mode", request.getMode());
132 | }
133 |
134 | String url = uriBuilder.build().toUriString();
135 |
136 | // 准备请求头
137 | HttpHeaders headers = new HttpHeaders();
138 | headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
139 | headers.set("Authorization", "Bearer " + getValidAccessToken());
140 |
141 |
142 | HttpEntity> entity = new HttpEntity<>(headers);
143 |
144 |
145 | try {
146 | ResponseEntity> response = restTemplate.exchange(
147 | url,
148 | HttpMethod.GET,
149 | entity,
150 | new ParameterizedTypeReference>() {
151 | }
152 | );
153 |
154 | if (response.getStatusCode() == HttpStatus.OK) {
155 | List scores = response.getBody();
156 | log.info("Successfully retrieved {} user scores of type '{}'",
157 | scores != null ? scores.size() : 0, type);
158 | return scores;
159 | } else {
160 | log.error("Failed to get user scores: {}", response.getStatusCode());
161 | throw new RuntimeException("Failed to get user scores from osu! API");
162 | }
163 | } catch (Exception e) {
164 | System.out.println("发送HTTP请求" + url + entity);
165 | log.error("Error getting user scores: ", e);
166 | throw new RuntimeException("Error getting user scores from osu! API", e);
167 | }
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/mapper/RedisDAO.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.mapper;
2 |
3 |
4 | import com.google.gson.Gson;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.data.redis.core.RedisTemplate;
7 | import org.springframework.stereotype.Component;
8 | import top.mothership.cabbage.pojo.osu.Userinfo;
9 |
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /**
13 | * Created by QHS on 2017/5/28.
14 | */
15 | @Component
16 | public class RedisDAO {
17 |
18 | private final RedisTemplate redisTemplate;
19 |
20 | @Autowired
21 | public RedisDAO(RedisTemplate redisTemplate) {
22 | this.redisTemplate = redisTemplate;
23 | }
24 |
25 | public void add(Integer userId, Userinfo userinfo) {
26 | redisTemplate.opsForHash().put(String.valueOf(userId), String.valueOf(userinfo.getMode()), new Gson().toJson(userinfo));
27 | }
28 |
29 | public void add(String key, String value) {
30 | redisTemplate.opsForValue().set(key, value);
31 | }
32 |
33 | public void add(String key, String value, Long timeout, TimeUnit timeUnit) {
34 | redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
35 | }
36 |
37 | public Userinfo get(Integer userId, Integer mode) {
38 | return new Gson().fromJson((String) redisTemplate.opsForHash().get(String.valueOf(userId), String.valueOf(mode)), Userinfo.class);
39 | }
40 |
41 | public String get(String key) {
42 | return redisTemplate.opsForValue().get(key);
43 | }
44 |
45 | public void expire(Integer userId, final long timeout, final TimeUnit unit) {
46 | redisTemplate.expire(String.valueOf(userId), timeout, unit);
47 | }
48 |
49 | public void expire(String key, final long timeout, final TimeUnit unit) {
50 | redisTemplate.expire(key, timeout, unit);
51 | }
52 |
53 | public void flushDb() {
54 | redisTemplate.getConnectionFactory().getConnection().flushDb();
55 | }
56 | }
--------------------------------------------------------------------------------
/src/main/java/top/mothership/cabbage/mapper/ResDAO.java:
--------------------------------------------------------------------------------
1 | package top.mothership.cabbage.mapper;
2 |
3 | import org.apache.ibatis.annotations.Insert;
4 | import org.apache.ibatis.annotations.Mapper;
5 | import org.apache.ibatis.annotations.Param;
6 | import org.apache.ibatis.annotations.Select;
7 | import org.springframework.stereotype.Repository;
8 |
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | @Mapper
13 | @Repository
14 | public interface ResDAO {
15 |
16 |
17 | @Insert("INSERT INTO `osufile` VALUES (null,#{bid},#{data})")
18 | Integer addOsuFile(@Param("bid")Integer bid,@Param("data") String data);
19 |
20 | @Select("SELECT `data` FROM osufile WHERE `bid` = #{bid} ")
21 | String getOsuFileBybid(@Param("bid")Integer bid);
22 |
23 |
24 |
25 | @Insert("INSERT INTO `bgfile` VALUES (null,#{sid},#{name},#{data})")
26 | Integer addBG(@Param("sid")Integer sid, @Param("name") String name,@Param("data") byte[] data);
27 |
28 | @Select("SELECT `data` FROM `bgfile` WHERE `sid` = #{sid} AND `name` = #{name} ")
29 | //这里似乎不能用byte[]?
30 | Object getBGBySidAndName(@Param("sid")Integer sid,@Param("name")String name);
31 |
32 |
33 |
34 | @Select("SELECT `name`,`data` FROM `resource`")
35 | List