Commit 0a6260b2e0
Unsigned
Layout: unified · split
misc.go −106
| @@ -1,79 +1,14 @@ | ||
| 1 | 1 | package devianter |
| 2 | 2 | |
| 3 | 3 | import ( |
| 4 | "encoding/json" | |
| 5 | 4 | "errors" |
| 6 | "io" | |
| 7 | 5 | "log" |
| 8 | 6 | "math" |
| 9 | "net/http" | |
| 10 | 7 | u "net/url" |
| 11 | 8 | "strconv" |
| 12 | 9 | "strings" |
| 13 | 10 | ) |
| 14 | 11 | |
| 15 | // функция для высера ошибки в stderr | |
| 16 | func err(txt error) { | |
| 17 | if txt != nil { | |
| 18 | println(txt.Error()) | |
| 19 | } | |
| 20 | } | |
| 21 | ||
| 22 | // сокращение для вызова щенка и парсинга жсона | |
| 23 | func ujson(data string, output any) { | |
| 24 | input, e := puppy(data) | |
| 25 | err(e) | |
| 26 | ||
| 27 | eee := json.Unmarshal([]byte(input), output) | |
| 28 | err(eee) | |
| 29 | } | |
| 30 | ||
| 31 | /* REQUEST SECTION */ | |
| 32 | // структура для ответа сервера | |
| 33 | type reqrt struct { | |
| 34 | Body string | |
| 35 | Status int | |
| 36 | Cookies []*http.Cookie | |
| 37 | Headers http.Header | |
| 38 | } | |
| 39 | ||
| 40 | // функция для совершения запроса | |
| 41 | func request(uri string, other ...string) reqrt { | |
| 42 | var r reqrt | |
| 43 | ||
| 44 | // создаём новый запрос | |
| 45 | cli := &http.Client{} | |
| 46 | req, e := http.NewRequest("GET", uri, nil) | |
| 47 | err(e) | |
| 48 | ||
| 49 | req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0.0") | |
| 50 | ||
| 51 | // куки и UA-шник | |
| 52 | for num, rng := range other { | |
| 53 | switch num { | |
| 54 | case 1: | |
| 55 | req.Header.Set("User-Agent", rng) | |
| 56 | case 0: | |
| 57 | req.Header.Set("Cookie", rng) | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | resp, e := cli.Do(req) | |
| 62 | err(e) | |
| 63 | defer resp.Body.Close() | |
| 64 | ||
| 65 | body, e := io.ReadAll(resp.Body) | |
| 66 | err(e) | |
| 67 | ||
| 68 | // заполняем структуру | |
| 69 | r.Body = string(body) | |
| 70 | r.Cookies = resp.Cookies() | |
| 71 | r.Headers = resp.Header | |
| 72 | r.Status = resp.StatusCode | |
| 73 | ||
| 74 | return r | |
| 75 | } | |
| 76 | ||
| 77 | 12 | /* AVATARS AND EMOJIS */ |
| 78 | 13 | func AEmedia(name string, t rune) (string, error) { |
| 79 | 14 | // список всех возможных расширений |
| @@ -189,44 +124,3 @@ func SearchFunc(query string, page int, scope rune, user ...string) (ss Search, | ||
| 189 | 124 | |
| 190 | 125 | return |
| 191 | 126 | } |
| 192 | ||
| 193 | /* PUPPY aka DeviantArt API */ | |
| 194 | // получение или обновление токена | |
| 195 | var cookie string | |
| 196 | var token string | |
| 197 | ||
| 198 | func UpdateCSRF() error { | |
| 199 | if cookie == "" { | |
| 200 | req := request("https://www.deviantart.com/_puppy") | |
| 201 | ||
| 202 | for _, content := range req.Cookies { | |
| 203 | cookie = content.Raw | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | req := request("https://www.deviantart.com", cookie) | |
| 208 | if req.Status != 200 { | |
| 209 | return errors.New(req.Body) | |
| 210 | } | |
| 211 | token = req.Body[strings.Index(req.Body, "window.__CSRF_TOKEN__ = '")+25 : strings.Index(req.Body, "window.__XHR_LOCAL__")-3] | |
| 212 | ||
| 213 | return nil | |
| 214 | } | |
| 215 | ||
| 216 | func puppy(data string) (string, error) { | |
| 217 | var url strings.Builder | |
| 218 | url.WriteString("https://www.deviantart.com/_puppy/") | |
| 219 | url.WriteString(data) | |
| 220 | url.WriteString("&csrf_token=") | |
| 221 | url.WriteString(token) | |
| 222 | url.WriteString("&da_minor_version=20230710") | |
| 223 | ||
| 224 | body := request(url.String(), cookie) | |
| 225 | ||
| 226 | // если код ответа не 200, возвращается ошибка | |
| 227 | if body.Status != 200 { | |
| 228 | return "", errors.New(body.Body) | |
| 229 | } | |
| 230 | ||
| 231 | return body.Body, nil | |
| 232 | } | |
user-group.go +54 −23
| @@ -6,13 +6,14 @@ import ( | ||
| 6 | 6 | ) |
| 7 | 7 | |
| 8 | 8 | // структура группы или пользователя |
| 9 | type groups struct { | |
| 10 | GroupAbout struct { | |
| 11 | FoundatedAt time `json:"foundationTs"` | |
| 12 | Description Text | |
| 13 | } | |
| 14 | GroupAdmins struct { | |
| 15 | Results []struct { | |
| 9 | type GroupAbout struct { | |
| 10 | FoundatedAt time `json:"foundationTs"` | |
| 11 | Description Text | |
| 12 | } | |
| 13 | type GroupAdmins struct { | |
| 14 | Results []struct { | |
| 15 | TypeId int | |
| 16 | User struct { | |
| 16 | 17 | Username string |
| 17 | 18 | } |
| 18 | 19 | } |
| @@ -50,11 +51,32 @@ type GRuser struct { | ||
| 50 | 51 | Modules []struct { |
| 51 | 52 | Name string |
| 52 | 53 | ModuleData struct { |
| 53 | groups | |
| 54 | GroupAbout GroupAbout | |
| 55 | GroupAdmins GroupAdmins | |
| 54 | 56 | users |
| 57 | } | |
| 58 | } | |
| 59 | } | |
| 60 | } | |
| 61 | Extra struct { | |
| 62 | Tag string `json:"gruserTagline"` | |
| 63 | Stats struct { | |
| 64 | Deviations, Watchers, Watching, Pageviews, CommentsMade, Favourites, Friends int | |
| 65 | FeedComments int `json:"commentsReceivedProfile"` | |
| 66 | } | |
| 67 | } `json:"pageExtraData"` | |
| 68 | } | |
| 55 | 69 | |
| 70 | type Gallery struct { | |
| 71 | Gruser struct { | |
| 72 | ID int `json:"gruserId"` | |
| 73 | Page struct { | |
| 74 | Modules []struct { | |
| 75 | Name string | |
| 76 | ModuleData struct { | |
| 56 | 77 | // группы |
| 57 | 78 | Folders struct { |
| 79 | HasMore bool | |
| 58 | 80 | Results []struct { |
| 59 | 81 | FolderId int |
| 60 | 82 | Name string |
| @@ -63,6 +85,7 @@ type GRuser struct { | ||
| 63 | 85 | |
| 64 | 86 | // галерея |
| 65 | 87 | Folder struct { |
| 88 | HasMore bool | |
| 66 | 89 | Username string |
| 67 | 90 | Pages int `json:"totalPageCount"` |
| 68 | 91 | Deviations []Deviation |
| @@ -71,18 +94,13 @@ type GRuser struct { | ||
| 71 | 94 | } |
| 72 | 95 | } |
| 73 | 96 | } |
| 74 | Extra struct { | |
| 75 | Tag string `json:"gruserTagline"` | |
| 76 | Stats struct { | |
| 77 | Deviations, Watchers, Watching, Pageviews, CommentsMade, Favourites, Friends int | |
| 78 | FeedComments int `json:"commentsReceivedProfile"` | |
| 79 | } | |
| 80 | } `json:"pageExtraData"` | |
| 97 | HasMore bool | |
| 98 | Results []Deviation | |
| 81 | 99 | } |
| 82 | 100 | |
| 83 | 101 | type Group struct { |
| 84 | 102 | Name string // обязательно заполнить |
| 85 | Content GRuser | |
| 103 | Content Gallery | |
| 86 | 104 | } |
| 87 | 105 | |
| 88 | 106 | // подходит как группа, так и пользователь |
| @@ -93,14 +111,27 @@ func (s Group) GroupFunc() (g GRuser) { | ||
| 93 | 111 | } |
| 94 | 112 | |
| 95 | 113 | // гарелея пользователя или группы |
| 96 | func (s Group) Gallery(page int) (g Group) { | |
| 114 | func (s Group) Gallery(page int, folderid ...int) (g Group) { | |
| 97 | 115 | var url strings.Builder |
| 98 | url.WriteString("dauserprofile/init/gallery?username=") | |
| 99 | url.WriteString(s.Name) | |
| 100 | url.WriteString("&page=") | |
| 101 | url.WriteString(strconv.Itoa(page)) | |
| 102 | url.WriteString("&deviations_limit=50&with_subfolders=false") | |
| 116 | if folderid[0] > 0 { | |
| 117 | page-- | |
| 118 | url.WriteString("dashared/gallection/contents?username=") | |
| 119 | url.WriteString(s.Name) | |
| 120 | url.WriteString("&folderid=") | |
| 121 | url.WriteString(strconv.Itoa(folderid[0])) | |
| 122 | url.WriteString("&offset=") | |
| 123 | url.WriteString(strconv.Itoa(page * 50)) | |
| 124 | url.WriteString("&type=gallery&") | |
| 125 | } else { | |
| 126 | url.WriteString("dauserprofile/init/gallery?username=") | |
| 127 | url.WriteString(s.Name) | |
| 128 | url.WriteString("&page=") | |
| 129 | url.WriteString(strconv.Itoa(page)) | |
| 130 | url.WriteString("&deviations_") | |
| 131 | } | |
| 132 | url.WriteString("limit=50") | |
| 133 | url.WriteString("&with_subfolders=false") | |
| 103 | 134 | |
| 104 | ujson(url.String(), &g) | |
| 135 | ujson(url.String(), &g.Content) | |
| 105 | 136 | return |
| 106 | 137 | } |
util.go added +112
| @@ -0,0 +1,112 @@ | ||
| 1 | package devianter | |
| 2 | ||
| 3 | import ( | |
| 4 | "encoding/json" | |
| 5 | "errors" | |
| 6 | "io" | |
| 7 | "net/http" | |
| 8 | "strings" | |
| 9 | ) | |
| 10 | ||
| 11 | // функция для высера ошибки в stderr | |
| 12 | func err(txt error) { | |
| 13 | if txt != nil { | |
| 14 | println(txt.Error()) | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | // сокращение для вызова щенка и парсинга жсона | |
| 19 | func ujson(data string, output any) { | |
| 20 | input, e := puppy(data) | |
| 21 | err(e) | |
| 22 | ||
| 23 | eee := json.Unmarshal([]byte(input), output) | |
| 24 | err(eee) | |
| 25 | } | |
| 26 | ||
| 27 | /* REQUEST SECTION */ | |
| 28 | // структура для ответа сервера | |
| 29 | type reqrt struct { | |
| 30 | Body string | |
| 31 | Status int | |
| 32 | Cookies []*http.Cookie | |
| 33 | Headers http.Header | |
| 34 | } | |
| 35 | ||
| 36 | // функция для совершения запроса | |
| 37 | func request(uri string, other ...string) reqrt { | |
| 38 | var r reqrt | |
| 39 | ||
| 40 | // создаём новый запрос | |
| 41 | cli := &http.Client{} | |
| 42 | req, e := http.NewRequest("GET", uri, nil) | |
| 43 | err(e) | |
| 44 | ||
| 45 | req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0.0") | |
| 46 | ||
| 47 | // куки и UA-шник | |
| 48 | for num, rng := range other { | |
| 49 | switch num { | |
| 50 | case 1: | |
| 51 | req.Header.Set("User-Agent", rng) | |
| 52 | case 0: | |
| 53 | req.Header.Set("Cookie", rng) | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | resp, e := cli.Do(req) | |
| 58 | err(e) | |
| 59 | defer resp.Body.Close() | |
| 60 | ||
| 61 | body, e := io.ReadAll(resp.Body) | |
| 62 | err(e) | |
| 63 | ||
| 64 | // заполняем структуру | |
| 65 | r.Body = string(body) | |
| 66 | r.Cookies = resp.Cookies() | |
| 67 | r.Headers = resp.Header | |
| 68 | r.Status = resp.StatusCode | |
| 69 | ||
| 70 | return r | |
| 71 | } | |
| 72 | ||
| 73 | /* PUPPY aka DeviantArt API */ | |
| 74 | // получение или обновление токена | |
| 75 | var cookie string | |
| 76 | var token string | |
| 77 | ||
| 78 | func UpdateCSRF() error { | |
| 79 | if cookie == "" { | |
| 80 | req := request("https://www.deviantart.com/_puppy") | |
| 81 | ||
| 82 | for _, content := range req.Cookies { | |
| 83 | cookie = content.Raw | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | req := request("https://www.deviantart.com", cookie) | |
| 88 | if req.Status != 200 { | |
| 89 | return errors.New(req.Body) | |
| 90 | } | |
| 91 | token = req.Body[strings.Index(req.Body, "window.__CSRF_TOKEN__ = '")+25 : strings.Index(req.Body, "window.__XHR_LOCAL__")-3] | |
| 92 | ||
| 93 | return nil | |
| 94 | } | |
| 95 | ||
| 96 | func puppy(data string) (string, error) { | |
| 97 | var url strings.Builder | |
| 98 | url.WriteString("https://www.deviantart.com/_puppy/") | |
| 99 | url.WriteString(data) | |
| 100 | url.WriteString("&csrf_token=") | |
| 101 | url.WriteString(token) | |
| 102 | url.WriteString("&da_minor_version=20230710") | |
| 103 | ||
| 104 | body := request(url.String(), cookie) | |
| 105 | ||
| 106 | // если код ответа не 200, возвращается ошибка | |
| 107 | if body.Status != 200 { | |
| 108 | return "", errors.New(body.Body) | |
| 109 | } | |
| 110 | ||
| 111 | return body.Body, nil | |
| 112 | } | |