vlambda博客
学习文章列表

对接LeetCode-完善提交代码校验指令

对接LeetCode-完善提交代码校验指令

预期校验之后的模板显示内容

image.png

对应的模板代码

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);
}
对接LeetCode-完善提交代码校验指令
image.png

Docker-Compose部署Redis

将需要映射的内容提交创建好

配置redis.conf

appendonly yes
# 密码
requirepass 123456
# 支持失效监听
notify-keyspace-events Ex
对接LeetCode-完善提交代码校验指令
image.png



编写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
对接LeetCode-完善提交代码校验指令
image.png


对接LeetCode-完善提交代码校验指令
image.png

编写接收类

@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);
    }
image.png

完善指令

/**
 * @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));

        }
    }
}
image.png

TOOD: 代码异常问题解决

详情可以回复:”QQ机器人专栏“ 以及 ”QQ机器人专栏教程“