Skip to content

Commit 7c9d372

Browse files
committed
[feat] first version
1 parent dd22b84 commit 7c9d372

File tree

8 files changed

+274
-13
lines changed

8 files changed

+274
-13
lines changed

.github/workflows/today.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: "leetcode-question-today"
2+
3+
on:
4+
schedule:
5+
- cron: "0 12 * * *"
6+
workflow_dispatch:
7+
8+
jobs:
9+
today:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v2
14+
with:
15+
fetch-depth: 0
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v2
19+
with:
20+
go-version: 1.16
21+
22+
- name: run cmd
23+
run: |
24+
go run main.go -slack ${{ secrets.SLACK_URL }}
25+
26+
- name: echo
27+
run: |
28+
echo "notify ok"

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
.vscode/
33

44
.DS_Store
5-
go.sum
5+
go.sum
6+
7+
leetcode-question-today.exe

README.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
1+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->-- START doctoc generated TOC please keep comment here to allow auto update -->
22
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
33
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
44

@@ -8,14 +8,11 @@
88

99
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
1010

11-
## go-template
11+
## leetcode-question-today
1212

13-
### feature
13+
leetcode 每日一题推送
1414

15-
- go file add license
16-
- go file format and auto import
17-
- markdown file add toc
15+
### acknowledgement
1816

19-
### TODO
20-
21-
- [ ] Makefile
17+
- graphql
18+
- leetcode

api/today.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"github.com/machinebox/graphql"
6+
)
7+
8+
func GetTodayQuestion(ctx context.Context) (*QuestionTodayResp, error) {
9+
// create a client (safe to share across requests)
10+
client := graphql.NewClient("https://leetcode.cn/graphql/")
11+
12+
// make a request
13+
req := graphql.NewRequest(QuestionQuery)
14+
15+
// set header fields
16+
req.Header.Set("Cache-Control", "no-cache")
17+
req.Header.Set("content-type", "application/json")
18+
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36")
19+
req.Header.Set("referer", "https://leetcode.cn/problemset/all/")
20+
req.Header.Set("origin", "https://leetcode.cn")
21+
22+
// run it and capture the response
23+
var resp QuestionTodayResp
24+
if err := client.Run(ctx, req, &resp); err != nil {
25+
return nil, err
26+
}
27+
28+
return &resp, nil
29+
}

api/types.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package api
2+
3+
const (
4+
LeetcodeCn = "https://leetcode.cn"
5+
Leetcode = "https://leetcode.com"
6+
)
7+
8+
// QuestionQuery graphql query
9+
const QuestionQuery = `
10+
query questionOfToday {
11+
todayRecord {
12+
date
13+
userStatus
14+
question {
15+
questionId
16+
frontendQuestionId: questionFrontendId
17+
difficulty
18+
title
19+
titleCn: translatedTitle
20+
titleSlug
21+
paidOnly: isPaidOnly
22+
freqBar
23+
isFavor
24+
acRate
25+
status
26+
solutionNum
27+
hasVideoSolution
28+
topicTags {
29+
name
30+
nameTranslated: translatedName
31+
id
32+
}
33+
extra {
34+
topCompanyTags {
35+
imgUrl
36+
slug
37+
numSubscribed
38+
}
39+
}
40+
}
41+
lastSubmission {
42+
id
43+
}
44+
}
45+
}
46+
`
47+
48+
// QuestionTodayResp 注意与官网直接 restful api 请求返回的少一个 data 字段嵌套
49+
type QuestionTodayResp struct {
50+
TodayRecord []struct {
51+
Date string `json:"date"`
52+
UserStatus string `json:"userStatus"`
53+
Question struct {
54+
QuestionID string `json:"questionId"`
55+
FrontendQuestionID string `json:"frontendQuestionId"`
56+
Difficulty string `json:"difficulty"`
57+
Title string `json:"title"`
58+
TitleCn string `json:"titleCn"`
59+
TitleSlug string `json:"titleSlug"`
60+
PaidOnly bool `json:"paidOnly"`
61+
FreqBar interface{} `json:"freqBar"`
62+
IsFavor bool `json:"isFavor"`
63+
AcRate float64 `json:"acRate"`
64+
Status interface{} `json:"status"`
65+
SolutionNum int `json:"solutionNum"`
66+
HasVideoSolution bool `json:"hasVideoSolution"`
67+
TopicTags []struct {
68+
Name string `json:"name"`
69+
NameTranslated string `json:"nameTranslated"`
70+
ID string `json:"id"`
71+
} `json:"topicTags"`
72+
Extra struct {
73+
TopCompanyTags []struct {
74+
ImgURL string `json:"imgUrl"`
75+
Slug string `json:"slug"`
76+
NumSubscribed int `json:"numSubscribed"`
77+
} `json:"topCompanyTags"`
78+
} `json:"extra"`
79+
} `json:"question"`
80+
LastSubmission interface{} `json:"lastSubmission"`
81+
} `json:"todayRecord"`
82+
}

example/graphql_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package example_test
2+
3+
import (
4+
"context"
5+
"github.com/machinebox/graphql"
6+
"leetcode-question-today/api"
7+
"log"
8+
"testing"
9+
)
10+
11+
func TestGraphql(t *testing.T) {
12+
// create a client (safe to share across requests)
13+
client := graphql.NewClient("https://leetcode.cn/graphql/")
14+
15+
// make a request
16+
req := graphql.NewRequest(api.QuestionQuery)
17+
18+
// set header fields
19+
req.Header.Set("Cache-Control", "no-cache")
20+
req.Header.Set("content-type", "application/json")
21+
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36")
22+
req.Header.Set("referer", "https://leetcode.cn/problemset/all/")
23+
req.Header.Set("origin", "https://leetcode.cn")
24+
25+
// define a Context for the request
26+
ctx := context.Background()
27+
28+
// run it and capture the response
29+
var respData api.QuestionTodayResp
30+
if err := client.Run(ctx, req, &respData); err != nil {
31+
log.Fatal(err)
32+
}
33+
34+
t.Logf("%+v\n", respData)
35+
}

go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module leetcode-question-today
2+
3+
go 1.16
4+
5+
require (
6+
github.com/cloud-org/msgpush v0.0.3 // indirect
7+
github.com/machinebox/graphql v0.2.2
8+
github.com/matryer/is v1.4.0 // indirect
9+
github.com/pkg/errors v0.9.1 // indirect
10+
)

main.go

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,87 @@
2424

2525
package main
2626

27-
import "fmt"
27+
import (
28+
"context"
29+
"flag"
30+
"fmt"
31+
"github.com/cloud-org/msgpush"
32+
"leetcode-question-today/api"
33+
"log"
34+
"os"
35+
"strings"
36+
)
37+
38+
var (
39+
slack string // slack 通知链接
40+
wecom string // wecom 通知链接
41+
help bool // 帮助
42+
)
43+
44+
func init() {
45+
flag.StringVar(&slack, "slack", "", "slack webhook url")
46+
flag.StringVar(&wecom, "wecom", "", "wecom webhook token")
47+
flag.BoolVar(&help, "h", false, "帮助")
48+
flag.Usage = usage
49+
}
50+
51+
func usage() {
52+
fmt.Fprintf(os.Stdout, `leetcode-question-today - leetcode 每日一题推送
53+
Usage: leetcode-question-today [-h help]
54+
Options:
55+
`)
56+
flag.PrintDefaults()
57+
}
2858

2959
func main() {
30-
// you can delete this after clone template
31-
fmt.Println("hello world")
60+
flag.Parse()
61+
if help {
62+
flag.PrintDefaults()
63+
return
64+
}
65+
66+
// 获取每日一题,如果有则推送即可
67+
resp, err := api.GetTodayQuestion(context.TODO())
68+
if err != nil {
69+
log.Printf("获取每日一题发生错误: %v\n", err)
70+
return
71+
}
72+
73+
if len(resp.TodayRecord) <= 0 {
74+
log.Printf("todayRecord 长度为 0,请检查\n")
75+
return
76+
}
77+
78+
today := resp.TodayRecord[0]
79+
80+
msgTemplate := `每日一题(%s)
81+
Title: %s
82+
Tags: %s
83+
Link: %s
84+
LinkCN: %s`
85+
date := today.Date
86+
title := fmt.Sprintf("%s(%s)", today.Question.TitleCn, today.Question.Title)
87+
tags := make([]string, 0)
88+
for _, tag := range today.Question.TopicTags {
89+
tags = append(tags, fmt.Sprintf("%s(%s)", tag.NameTranslated, tag.Name))
90+
}
91+
tagsValue := strings.Join(tags, "、")
92+
link := fmt.Sprintf("%s/%s", api.Leetcode, today.Question.TitleSlug)
93+
linkCn := fmt.Sprintf("%s/%s", api.LeetcodeCn, today.Question.TitleSlug)
94+
95+
content := fmt.Sprintf(msgTemplate, date, title, tagsValue, link, linkCn)
96+
97+
log.Println(content)
98+
99+
if slack != "" {
100+
s := msgpush.NewSlack(slack)
101+
_ = s.Send(content)
102+
}
103+
104+
if wecom != "" {
105+
w := msgpush.NewWeCom(wecom)
106+
_ = w.SendText(content)
107+
}
108+
109+
return
32110
}

0 commit comments

Comments
 (0)