对接LeetCode-完善提交代码校验指令
对接LeetCode-完善提交代码校验指令
预期校验之后的模板显示内容
对应的模板代码
String template = """
===============================提交结果==========================
题目: %s.%s
提交时间: %s
执行结果: %s
执行用时: %s ms,在所有 Java 提交中击败了%s %%的用户
内存消耗: %s , 在所有 Java 提交中击败了%s %%的用户
提交的代码:
%s
===============================================================
""";
具体实现
@OnPrivate
@Filter(value = "check_leetcode_problem\s(?<problem>.{1,100})\s#(?<code>[\s\S]+)#$", matchType = MatchType.REGEX_MATCHES, trim = true)
public void doPrivateMsg(PrivateMsg event, MsgSender sender, @FilterValue("problem") String problem, @FilterValue("code") String code) {
AccountInfo accountInfo = event.getAccountInfo();
if (ownerCode.equals(accountInfo.getAccountCode())) {
System.out.println(code);
System.out.println(problem);
Map<String, Object> submitRequestMap = new HashMap<>(8);
/**
* TODO
*/
String frontId = "142";
submitRequestMap.put("question_id", frontId);
submitRequestMap.put("lang", "java");
submitRequestMap.put("false", false);
submitRequestMap.put("test_judger", "amet ea ut laboris ad");
submitRequestMap.put("questionSlug", "problem");
submitRequestMap.put("typed_code", code);
// 发送http请求
String submitJson = HttpClient.textBody(LeetCodeConstants.BASIC_URI + "/problems/" + problem + "/submit/")
.json(JSON.toJSONString(submitRequestMap))
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asString();
JSON.DEFAULT_PARSER_FEATURE &= ~Feature.UseBigDecimal.getMask();
Map<?, ?> submitMap = JSON.parseObject(submitJson, Map.class);
String submissionId = String.valueOf(submitMap.get("submission_id"));
String statusCode;
Map<?, ?> checkMap;
do {
String checkJson = HttpClient.get(LeetCodeConstants.BASIC_URI + "/submissions/detail/" + submissionId + "/check/")
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asString();
checkMap = JSON.parseObject(checkJson, Map.class);
statusCode = String.valueOf(checkMap.get("status_code"));
System.out.println(statusCode);
} while (!statusCode.equals("10"));
String template = """
===============================提交结果==========================
题目: %s.%s
提交时间: %s
执行结果: %s
执行用时: %s ms,在所有 Java 提交中击败了%s %%的用户
内存消耗: %s , 在所有 Java 提交中击败了%s %%的用户
提交的代码:
%s
===============================================================
""";
// 提交时间
long taskFinishTime = Long.parseLong(String.valueOf(checkMap.get("task_finish_time")));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String taskFinishTimeStr = sdf.format(new Date(taskFinishTime));
// 执行结果
String runSuccess = String.valueOf(checkMap.get("run_success"));
// 执行用时
Integer displayRuntime = Integer.valueOf(String.valueOf(checkMap.get("display_runtime")));
// 执行用时击败了多少用户的百分比
String runtimePercentile = String.valueOf(checkMap.get("runtime_percentile"));
// 内存消耗
String statusMemory = String.valueOf(checkMap.get("status_memory"));
// 内存消耗击败了多少用户的百分比
String memoryPercentile = String.valueOf(checkMap.get("memory_percentile"));
sender.SENDER.sendPrivateMsgAsync(ownerCode, String.format(template, frontId, problem, taskFinishTimeStr, runSuccess, displayRuntime, runtimePercentile, statusMemory, memoryPercentile, code));
}
}
获取LeetCode题目内容
对接获取题目内容API
POST /graphql
请求参数
{
"query": " query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) { problemsetQuestionList( categorySlug: $categorySlug limit: $limit skip: $skip filters: $filters ) { hasMore total questions { acRate difficulty freqBar frontendQuestionId isFavor paidOnly solutionNum status title titleCn titleSlug topicTags { name nameTranslated id slug } extra { hasVideoSolution topCompanyTags { imgUrl slug numSubscribed } } } } } ",
"variables": {
"categorySlug": "",
"skip": 0,
"limit": 50,
"filters": {}
}
}
响应参数内容
{
"data": {
"problemsetQuestionList": {
"__typename": "QuestionListNode",
"questions": [
{
"__typename": "QuestionLightNode",
"acRate": 0.5241663396330561,
"difficulty": "EASY",
"freqBar": 0,
"paidOnly": false,
"status": "NOT_STARTED",
"frontendQuestionId": "1",
"isFavor": false,
"solutionNum": 17134,
"title": "Two Sum",
"titleCn": "两数之和",
"titleSlug": "two-sum",
"topicTags": [
{
"id": "wg0rh",
"name": "Array",
"slug": "array",
"nameTranslated": "数组",
"__typename": "CommonTagNode"
},
{
"id": "wzve3",
"name": "Hash Table",
"slug": "hash-table",
"nameTranslated": "哈希表",
"__typename": "CommonTagNode"
}
],
"extra": {
"companyTagNum": 391,
"hasVideoSolution": true,
"topCompanyTags": [
{
"imgUrl": "https://pic.leetcode-cn.com/5352d58e4b296e0704f20cfd99ecaec7af71d079b015923c72928650312c55b8-Messages Image(3349252251).png",
"slug": "amazon",
"__typename": "CommonTagNode"
},
{
"imgUrl": "https://pic.leetcode-cn.com/45a64add888e66ff6d3c551bed948528715996937b877aaf6fdc08eae74789f5-google-logo-png-open-2000.png",
"slug": "google",
"__typename": "CommonTagNode"
},
{
"imgUrl": "https://pic.leetcode-cn.com/cca55ecdfb504378955a9bf4f2f33897bb84f04f9cbf84ea96321ce5d959ee13-adobe1.png",
"slug": "adobe",
"__typename": "CommonTagNode"
}
],
"__typename": "QuestionExtraInfoNode"
}
}
],
"hasMore": true,
"total": 2585
}
}
}
编写获取题目内容API测试用例
@Test
public void testGetQuestions() throws InterruptedException {
String queryString = " query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) { problemsetQuestionList( categorySlug: $categorySlug limit: $limit skip: $skip filters: $filters ) { hasMore total questions { acRate difficulty freqBar frontendQuestionId isFavor paidOnly solutionNum status title titleCn titleSlug topicTags { name nameTranslated id slug } extra { hasVideoSolution topCompanyTags { imgUrl slug numSubscribed } } } } } ";
Map<String, Object> requestMap = Maps.newLinkedHashMapWithExpectedSize(2);
Map<String, Object> variablesMap = Maps.newLinkedHashMapWithExpectedSize(4);
requestMap.put("query", queryString);
variablesMap.put("categorySlug", "");
variablesMap.put("skip", "0");
variablesMap.put("limit", "1");
variablesMap.put("filters", new HashMap<String, String>());
requestMap.put("variables", variablesMap);
String graphqlJson = HttpClient.textBody(LeetCodeConstants.BASIC_URI + "/graphql")
.json(JSON.toJSONString(requestMap))
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asString();
System.out.println(graphqlJson);
}
Docker-Compose部署Redis
将需要映射的内容提交创建好
配置redis.conf
appendonly yes
# 密码
requirepass 123456
# 支持失效监听
notify-keyspace-events Ex
编写docker脚本
version: '3'
services:
redis:
image: arm64v8/redis:5
privileged: true
container_name: redis_6379
restart: always
ports:
- 56379:6379
volumes:
- ./redis_56379/data:/data
- ./redis_56379/conf/redis.conf:/usr/local/bin/redis.conf
command: [ "redis-server" ,"/usr/local/bin/redis.conf" ]
在终端启动redis
docker-compose -f redis.yml -p redis up -d
编写接收类
@Data
@Accessors(chain = true)
@ToString()
public class LeetCodeProblem implements Serializable {
/**
* 题号
*/
private String frontendQuestionId;
/**
* 题目中文名称
*/
private String titleCn;
/**
* 题目英文名称
*/
private String title;
/**
* 难易程度
*/
private String difficulty;
/**
* 对应的是指令的problem
*/
private String titleSlug;
/**
* 属于什么类型的题目: [链表,数学...]
*/
private List<String> nameTranslatedList;
}
编写接收类相关测试用例
@Test
public void testGetQuestions() throws InterruptedException {
String queryString = " query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) { problemsetQuestionList( categorySlug: $categorySlug limit: $limit skip: $skip filters: $filters ) { hasMore total questions { acRate difficulty freqBar frontendQuestionId isFavor paidOnly solutionNum status title titleCn titleSlug topicTags { name nameTranslated id slug } extra { hasVideoSolution topCompanyTags { imgUrl slug numSubscribed } } } } } ";
Map<String, Object> requestMap = Maps.newLinkedHashMapWithExpectedSize(2);
Map<String, Object> variablesMap = Maps.newLinkedHashMapWithExpectedSize(4);
requestMap.put("query", queryString);
variablesMap.put("categorySlug", "");
variablesMap.put("skip", "0");
variablesMap.put("limit", "2585");
variablesMap.put("filters", new HashMap<String, String>());
requestMap.put("variables", variablesMap);
JSONObject graphqlJsonObject = HttpClient.textBody(LeetCodeConstants.BASIC_URI + "/graphql")
.json(JSON.toJSONString(requestMap))
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asBean(JSONObject.class);
JSONObject dataJsonObject = graphqlJsonObject.getJSONObject("data");
JSONObject problemsetQuestionListJsonObject = dataJsonObject.getJSONObject("problemsetQuestionList");
JSONArray questionsJsonArray = problemsetQuestionListJsonObject.getJSONArray("questions");
List<LeetCodeProblem> problems = Lists.newArrayListWithCapacity(2585);
List<Future<LeetCodeProblem>> futureList = Lists.newArrayListWithCapacity(2585);
for (int i = 0; i < questionsJsonArray.size(); i++) {
final int idx = i;
Future<LeetCodeProblem> future = executorService.submit(() -> {
LeetCodeProblem problem = new LeetCodeProblem();
JSONObject curJsonObject = questionsJsonArray.getJSONObject(idx);
String frontendQuestionId = String.valueOf(curJsonObject.get("frontendQuestionId"));
String titleCn = String.valueOf(curJsonObject.get("titleCn"));
String title = String.valueOf(curJsonObject.get("title"));
String difficulty = String.valueOf(curJsonObject.get("difficulty"));
String titleSlug = String.valueOf(curJsonObject.get("titleSlug"));
List<String> nameTranslatedList = new ArrayList<>();
JSONArray topicTags = curJsonObject.getJSONArray("topicTags");
for (int i1 = 0; i1 < topicTags.size(); i1++) {
JSONObject jsonObject = topicTags.getJSONObject(i1);
String nameTranslated = String.valueOf(jsonObject.get("nameTranslated"));
nameTranslatedList.add(nameTranslated);
}
return problem
.setFrontendQuestionId(frontendQuestionId)
.setTitleCn(titleCn)
.setTitle(title)
.setDifficulty(difficulty)
.setTitleSlug(titleSlug)
.setNameTranslatedList(nameTranslatedList);
});
futureList.add(future);
}
for (Future<LeetCodeProblem> future : futureList) {
try {
LeetCodeProblem problem = future.get();
problems.add(problem);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.println(problems);
}
引入Redis进行存储数据
参考文档
/**
* 题目集合Key
*/
String PROBLEMS_SET_KEY = "PROBLEMS_LIST_KEY";
@Resource
private RedisTemplate<String, LeetCodeProblem> redisTemplate;
@Test
public void testGetQuestions() throws InterruptedException {
String queryString = " query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) { problemsetQuestionList( categorySlug: $categorySlug limit: $limit skip: $skip filters: $filters ) { hasMore total questions { acRate difficulty freqBar frontendQuestionId isFavor paidOnly solutionNum status title titleCn titleSlug topicTags { name nameTranslated id slug } extra { hasVideoSolution topCompanyTags { imgUrl slug numSubscribed } } } } } ";
Map<String, Object> requestMap = Maps.newLinkedHashMapWithExpectedSize(2);
Map<String, Object> variablesMap = Maps.newLinkedHashMapWithExpectedSize(4);
requestMap.put("query", queryString);
variablesMap.put("categorySlug", "");
variablesMap.put("skip", "0");
variablesMap.put("limit", "2585");
variablesMap.put("filters", new HashMap<String, String>());
requestMap.put("variables", variablesMap);
JSONObject graphqlJsonObject = HttpClient.textBody(LeetCodeConstants.BASIC_URI + "/graphql")
.json(JSON.toJSONString(requestMap))
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asBean(JSONObject.class);
JSONObject dataJsonObject = graphqlJsonObject.getJSONObject("data");
JSONObject problemsetQuestionListJsonObject = dataJsonObject.getJSONObject("problemsetQuestionList");
JSONArray questionsJsonArray = problemsetQuestionListJsonObject.getJSONArray("questions");
// List<LeetCodeProblem> problems = Lists.newArrayListWithCapacity(2585);
List<Future<LeetCodeProblem>> futureList = Lists.newArrayListWithCapacity(2585);
for (int i = 0; i < questionsJsonArray.size(); i++) {
final int idx = i;
Future<LeetCodeProblem> future = executorService.submit(() -> {
LeetCodeProblem problem = new LeetCodeProblem();
JSONObject curJsonObject = questionsJsonArray.getJSONObject(idx);
String frontendQuestionId = String.valueOf(curJsonObject.get("frontendQuestionId"));
String titleCn = String.valueOf(curJsonObject.get("titleCn"));
String title = String.valueOf(curJsonObject.get("title"));
String difficulty = String.valueOf(curJsonObject.get("difficulty"));
String titleSlug = String.valueOf(curJsonObject.get("titleSlug"));
List<String> nameTranslatedList = new ArrayList<>();
JSONArray topicTags = curJsonObject.getJSONArray("topicTags");
for (int i1 = 0; i1 < topicTags.size(); i1++) {
JSONObject jsonObject = topicTags.getJSONObject(i1);
String nameTranslated = String.valueOf(jsonObject.get("nameTranslated"));
nameTranslatedList.add(nameTranslated);
}
return problem
.setFrontendQuestionId(frontendQuestionId)
.setTitleCn(titleCn)
.setTitle(title)
.setDifficulty(difficulty)
.setTitleSlug(titleSlug)
.setNameTranslatedList(nameTranslatedList);
});
futureList.add(future);
}
for (Future<LeetCodeProblem> future : futureList) {
try {
LeetCodeProblem problem = future.get();
redisTemplate.opsForSet().add(LeetCodeConstants.PROBLEMS_SET_KEY, problem);
// problems.add(problem);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// System.out.println(problems);
}
完善指令
/**
* @author yqlin
* @date 2022/4/8 21:10
* @description
*/
@Slf4j
@Component("simpleBotLeetCodeEvent")
public class SimpleBotLeetCodeEvent {
Map<String, LeetCodeProblem> problemsMap;
@Value("${qq.owner}")
private String ownerCode;
@Value("${qq.group}")
private String groupCode;
/***
* 指令实现: check_leetcode_problem [题目名] #代码#
* check_leetcode_problem reverse-linked-list #
* public class Solution {
* public ListNode detectCycle(ListNode head) {
* if (head == null) return null;
* ListNode fast = head, slow = head;
* while (fast != null && fast.next != null) {
* slow = slow.next;
* fast = fast.next.next;
* if (fast == slow) {
* break;
* }
* }
* // 上面的代码类似 hasCycle 函数
* if (fast == null || fast.next == null) {
* // fast 遇到空指针说明没有环
* return null;
* }
* // 将快指针置回头部,慢指针每次走一步,快指针也每次走一步,直到相遇
* fast = head;
* // 快慢指针同步前进,相交点就是环起点
* while (fast != slow) {
* fast = fast.next;
* slow = slow.next;
* }
* return fast;
* }
* }
* #
*/
@PostConstruct
public void initProblems() {
Set<LeetCodeProblem> members = redisTemplate.opsForSet().members(LeetCodeConstants.PROBLEMS_SET_KEY);
if (CollectionUtils.isEmpty(members)) {
return;
}
problemsMap = members.stream().collect(Collectors.toMap(LeetCodeProblem::getTitleSlug, k -> k, (v1, v2) -> v1));
log.info("leetcode题目加载成功,加载题目数量: {}......", members.size());
}
@Resource
private RedisTemplate<String, LeetCodeProblem> redisTemplate;
@OnPrivate
@Filter(value = "check_leetcode_problem\s(?<problem>.{1,100})\s#(?<code>[\s\S]+)#$", matchType = MatchType.REGEX_MATCHES, trim = true)
public void doPrivateMsg(PrivateMsg event, MsgSender sender, @FilterValue("problem") String problem, @FilterValue("code") String code) {
AccountInfo accountInfo = event.getAccountInfo();
if (ownerCode.equals(accountInfo.getAccountCode())) {
System.out.println(code);
System.out.println(problem);
/**
* TOOD
*/
LeetCodeProblem problemInfo = problemsMap.get(problem);
if (Objects.isNull(problemInfo)) {
sender.SENDER.sendPrivateMsgAsync(ownerCode, "当前题目不存在", code);
}
Map<String, Object> submitRequestMap = new HashMap<>(8);
submitRequestMap.put("question_id", problemInfo.getFrontendQuestionId());
submitRequestMap.put("lang", "java");
submitRequestMap.put("false", false);
submitRequestMap.put("test_judger", "amet ea ut laboris ad");
submitRequestMap.put("questionSlug", "problem");
submitRequestMap.put("typed_code", code);
// 发送http请求
String submitJson = HttpClient.textBody(LeetCodeConstants.BASIC_URI + "/problems/" + problem + "/submit/")
.json(JSON.toJSONString(submitRequestMap))
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asString();
JSON.DEFAULT_PARSER_FEATURE &= ~Feature.UseBigDecimal.getMask();
Map<?, ?> submitMap = JSON.parseObject(submitJson, Map.class);
String submissionId = String.valueOf(submitMap.get("submission_id"));
String statusCode;
Map<?, ?> checkMap;
do {
String checkJson = HttpClient.get(LeetCodeConstants.BASIC_URI + "/submissions/detail/" + submissionId + "/check/")
.header("cookie", LeetCodeConstants.COOKIE)
.header("origin", LeetCodeConstants.BASIC_URI)
.asString();
checkMap = JSON.parseObject(checkJson, Map.class);
statusCode = String.valueOf(checkMap.get("status_code"));
System.out.println(statusCode);
} while (!statusCode.equals("10"));
String template = """
===============================提交结果==========================
题目: %s.%s
提交时间: %s
执行结果: %s
执行用时: %s ms,在所有 Java 提交中击败了%s %%的用户
内存消耗: %s , 在所有 Java 提交中击败了%s %%的用户
提交的代码:
%s
===============================================================
""";
// 提交时间
long taskFinishTime = Long.parseLong(String.valueOf(checkMap.get("task_finish_time")));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String taskFinishTimeStr = sdf.format(new Date(taskFinishTime));
// 执行结果
String runSuccess = String.valueOf(checkMap.get("run_success"));
// 执行用时
Integer displayRuntime = Integer.valueOf(String.valueOf(checkMap.get("display_runtime")));
// 执行用时击败了多少用户的百分比
String runtimePercentile = String.valueOf(checkMap.get("runtime_percentile"));
// 内存消耗
String statusMemory = String.valueOf(checkMap.get("status_memory"));
// 内存消耗击败了多少用户的百分比
String memoryPercentile = String.valueOf(checkMap.get("memory_percentile"));
sender.SENDER.sendPrivateMsgAsync(ownerCode, String.format(template, problemInfo.getFrontendQuestionId(), problem, taskFinishTimeStr, runSuccess, displayRuntime, runtimePercentile, statusMemory, memoryPercentile, code));
}
}
}
TOOD: 代码异常问题解决
详情可以回复:”QQ机器人专栏“ 以及 ”QQ机器人专栏教程“