-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.go
150 lines (126 loc) · 3.64 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package ticktick
import (
"context"
"encoding/json"
"fmt"
"github.com/carlmjohnson/requests"
"github.com/tidwall/gjson"
)
const (
baseUrlV2Dida365 = "https://api.dida365.com/api/v2"
baseUrlV2Ticktick = "https://api.ticktick.com/api/v2"
baseUrlV2Test = "https://api.test.com/api/v2"
signinUrlEndpoint = "/user/signon?wc=true&remember=true" // POST
queryUnfinishedJobUrlEndpoint = "/batch/check/0" // GET
)
type Client struct {
UserName string
PassWord string
baseUrlV2 string
loginToken string
inboxId string
projectGroups []projectGroupsItem
projectName2Id map[string]string
id2ProjectName map[string]string
tasks []TaskItem
tags []string
}
type projectGroupsItem struct {
id string
name string
}
// create a new client, the server can be ticktick, dida365, test
func NewClient(userName, passWord, server string) (*Client, error) {
var baseUrlV2 string
switch server {
case "ticktick":
baseUrlV2 = baseUrlV2Ticktick
case "dida365":
baseUrlV2 = baseUrlV2Dida365
case "test":
baseUrlV2 = baseUrlV2Test
default:
return nil, fmt.Errorf("server name %v is not supported", server)
}
client := &Client{UserName: userName, PassWord: passWord, baseUrlV2: baseUrlV2, projectName2Id: make(map[string]string), id2ProjectName: make(map[string]string)}
if err := client.Init(); err != nil {
return nil, err
}
return client, nil
}
// init the client struct (login, sync)
func (c *Client) Init() error {
if err := c.GetToken(); err != nil {
return err
}
if err := c.Sync(); err != nil {
return err
}
return nil
}
// get the ticktick token
func (c *Client) GetToken() error {
body := map[string]string{
"username": c.UserName,
"password": c.PassWord,
}
var resp string
if err := requests.
URL(c.baseUrlV2+signinUrlEndpoint).
BodyJSON(&body).
ToString(&resp).
Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0").
Fetch(context.Background()); err != nil {
return err
}
loginToken := gjson.Get(resp, "token").String()
if loginToken == "" {
return fmt.Errorf("no token found in the response, full response json is %v", resp)
}
c.loginToken = loginToken
return nil
}
// fetch all the user contents
func (c *Client) Sync() error {
var resp string
if err := requests.
URL(c.baseUrlV2+queryUnfinishedJobUrlEndpoint).
Cookie("t", c.loginToken).
ToString(&resp).
Fetch(context.Background()); err != nil {
return err
}
// below we assume the apis are stable
c.inboxId = gjson.Get(resp, "inboxId").String()
c.projectGroups = nil
gjson.Get(resp, "projectGroups").ForEach(func(key, value gjson.Result) bool {
c.projectGroups = append(c.projectGroups, projectGroupsItem{
id: value.Get("id").String(),
name: value.Get("name").String(),
})
return true
})
c.projectName2Id = make(map[string]string)
c.projectName2Id["inbox"] = c.inboxId
c.id2ProjectName = make(map[string]string)
c.id2ProjectName[c.inboxId] = "inbox"
gjson.Get(resp, "projectProfiles").ForEach(func(key, value gjson.Result) bool {
c.projectName2Id[value.Get("name").String()] = value.Get("id").String()
c.id2ProjectName[value.Get("id").String()] = value.Get("name").String()
return true
})
c.tasks = nil
gjson.Get(resp, "syncTaskBean.update").ForEach(func(key, value gjson.Result) bool {
var t TaskItem
json.Unmarshal([]byte(value.Raw), &t)
t.ProjectName = c.id2ProjectName[t.ProjectId]
c.tasks = append(c.tasks, t)
return true
})
c.tags = nil
gjson.Get(resp, "tags").ForEach(func(key, value gjson.Result) bool {
c.tags = append(c.tags, value.Get("name").String())
return true
})
return nil
}