Commit 42ea2980f9
Unsigned
Layout: unified · split
.vscode/launch.json deleted −16
| @@ -1,16 +0,0 @@ | |||
| 1 | { | ||
| 2 | // Use IntelliSense to learn about possible attributes. | ||
| 3 | // Hover to view descriptions of existing attributes. | ||
| 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
| 5 | "version": "0.2.0", | ||
| 6 | "configurations": [ | ||
| 7 | { | ||
| 8 | "name": "Launch Package", | ||
| 9 | "type": "go", | ||
| 10 | "request": "launch", | ||
| 11 | "mode": "debug", | ||
| 12 | "program": "${fileDirname}", | ||
| 13 | "showLog": true | ||
| 14 | } | ||
| 15 | ] | ||
| 16 | }\ No newline at end of file | ||
.vscode/settings.json deleted −5
| @@ -1,5 +0,0 @@ | |||
| 1 | { | ||
| 2 | "go.toolsEnvVars": { | ||
| 3 | "GOROOT": "" | ||
| 4 | } | ||
| 5 | }\ No newline at end of file | ||
README.md +2 −2
| @@ -1,9 +1,9 @@ | |||
| 1 | # Devianter | 1 | # Devianter |
| 2 | 2 | ||
| 3 | [](https://nogithub.codeberg.page) [](https://pkg.go.dev/git.macaw.me/skunky/devianter) [](https://www.gnu.org/licenses/agpl-3.0) | 3 | [](https://nogithub.codeberg.page) [](https://pkg.go.dev/git.macaw.me/skunky/devianter) |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | A DeviantART API library for Go. | 6 | A DeviantART guest API library for Go. |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | I'll probably write up some documentation, but more on that later. | 9 | I'll probably write up some documentation, but more on that later. |
comments.go +3 −3
| @@ -11,7 +11,7 @@ type Thread struct { | |||
| 11 | ID int `json:"commentId"` | 11 | ID int `json:"commentId"` |
| 12 | Parent int `json:"parentId"` | 12 | Parent int `json:"parentId"` |
| 13 | 13 | ||
| 14 | Posted time | 14 | Posted timeStamp |
| 15 | Author bool `json:"isAuthorHighlited"` | 15 | Author bool `json:"isAuthorHighlited"` |
| 16 | 16 | ||
| 17 | Desctiption string | 17 | Desctiption string |
| @@ -35,7 +35,7 @@ type Comments struct { | |||
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | // 1 - комментарии поста; 4 - комментарии на стене группы или пользователя | 37 | // 1 - комментарии поста; 4 - комментарии на стене группы или пользователя |
| 38 | func CommentsFunc(postid string, cursor string, page int, typ int) (cmmts Comments) { | 38 | func GetComments(postid string, cursor string, page int, typ int) (cmmts Comments) { |
| 39 | for x := 0; x <= page; x++ { | 39 | for x := 0; x <= page; x++ { |
| 40 | ujson( | 40 | ujson( |
| 41 | "dashared/comments/thread?typeid="+strconv.Itoa(typ)+ | 41 | "dashared/comments/thread?typeid="+strconv.Itoa(typ)+ |
| @@ -60,7 +60,7 @@ func CommentsFunc(postid string, cursor string, page int, typ int) (cmmts Commen | |||
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | e := json.Unmarshal([]byte(m), &content) | 62 | e := json.Unmarshal([]byte(m), &content) |
| 63 | err(e) | 63 | try(e) |
| 64 | 64 | ||
| 65 | for _, a := range content.Blocks { | 65 | for _, a := range content.Blocks { |
| 66 | cmmts.Thread[i].Comment = a.Text | 66 | cmmts.Thread[i].Comment = a.Text |
deviantion.go +23 −9
| @@ -4,26 +4,26 @@ import ( | |||
| 4 | "encoding/json" | 4 | "encoding/json" |
| 5 | "strconv" | 5 | "strconv" |
| 6 | "strings" | 6 | "strings" |
| 7 | timelib "time" | 7 | "time" |
| 8 | ) | 8 | ) |
| 9 | 9 | ||
| 10 | // хрень для парсинга времени публикации | 10 | // хрень для парсинга времени публикации |
| 11 | type time struct { | 11 | type timeStamp struct { |
| 12 | timelib.Time | 12 | time.Time |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | func (t *time) UnmarshalJSON(b []byte) (err error) { | 15 | func (t *timeStamp) UnmarshalJSON(b []byte) (err error) { |
| 16 | if b[0] == '"' && b[len(b)-1] == '"' { | 16 | if b[0] == '"' && b[len(b)-1] == '"' { |
| 17 | b = b[1 : len(b)-1] | 17 | b = b[1 : len(b)-1] |
| 18 | } | 18 | } |
| 19 | t.Time, err = timelib.Parse("2006-01-02T15:04:05-0700", string(b)) | 19 | t.Time, err = time.Parse("2006-01-02T15:04:05-0700", string(b)) |
| 20 | return | 20 | return |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | // самая главная структура для поста | 23 | // самая главная структура для поста |
| 24 | type Deviation struct { | 24 | type Deviation struct { |
| 25 | Title, Url, License string | 25 | Title, Url, License string |
| 26 | PublishedTime time | 26 | PublishedTime timeStamp |
| 27 | ID int `json:"deviationId"` | 27 | ID int `json:"deviationId"` |
| 28 | 28 | ||
| 29 | NSFW bool `json:"isMature"` | 29 | NSFW bool `json:"isMature"` |
| @@ -82,7 +82,7 @@ type Post struct { | |||
| 82 | 82 | ||
| 83 | ParsedComments []struct { | 83 | ParsedComments []struct { |
| 84 | Author string | 84 | Author string |
| 85 | Posted time | 85 | Posted timeStamp |
| 86 | Replies, Likes int | 86 | Replies, Likes int |
| 87 | } | 87 | } |
| 88 | 88 | ||
| @@ -90,12 +90,25 @@ type Post struct { | |||
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | // преобразование урла в правильный | 92 | // преобразование урла в правильный |
| 93 | func UrlFromMedia(m Media) string { | 93 | func UrlFromMedia(m Media, thumb ...int) string { |
| 94 | var url strings.Builder | 94 | var url strings.Builder |
| 95 | |||
| 96 | subtractWidthHeight := func(to int, target ...*int) { | ||
| 97 | for i, l := 0, len(target); i < l; i++ { | ||
| 98 | for x := *target[i]; x > to; x -= to { | ||
| 99 | *target[i] = x | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 95 | for _, t := range m.Types { | 104 | for _, t := range m.Types { |
| 96 | if t.T == "fullview" { | 105 | if t.T == "fullview" { |
| 97 | url.WriteString(m.BaseUri) | 106 | url.WriteString(m.BaseUri) |
| 98 | if m.BaseUri[len(m.BaseUri)-3:] != "gif" && t.W*t.H < 33177600 { | 107 | if m.BaseUri[len(m.BaseUri)-3:] != "gif" && t.W*t.H < 33177600 { |
| 108 | if len(thumb) != 0 { | ||
| 109 | subtractWidthHeight(thumb[0], &t.W, &t.H) | ||
| 110 | } | ||
| 111 | |||
| 99 | url.WriteString("/v1/fit/w_") | 112 | url.WriteString("/v1/fit/w_") |
| 100 | url.WriteString(strconv.Itoa(t.W)) | 113 | url.WriteString(strconv.Itoa(t.W)) |
| 101 | url.WriteString(",h_") | 114 | url.WriteString(",h_") |
| @@ -110,11 +123,12 @@ func UrlFromMedia(m Media) string { | |||
| 110 | } | 123 | } |
| 111 | } | 124 | } |
| 112 | } | 125 | } |
| 126 | |||
| 113 | return url.String() | 127 | return url.String() |
| 114 | } | 128 | } |
| 115 | 129 | ||
| 116 | // для работы функции нужно ID поста и имя пользователя. | 130 | // для работы функции нужно ID поста и имя пользователя. |
| 117 | func DeviationFunc(id string, user string) Post { | 131 | func GetDeviation(id string, user string) Post { |
| 118 | var st Post | 132 | var st Post |
| 119 | ujson( | 133 | ujson( |
| 120 | "dadeviation/init?deviationid="+id+"&username="+user+"&type=art&include_session=false&expand=deviation.related&preload=true", | 134 | "dadeviation/init?deviationid="+id+"&username="+user+"&type=art&include_session=false&expand=deviation.related&preload=true", |
misc.go +20 −18
| @@ -4,13 +4,16 @@ import ( | |||
| 4 | "errors" | 4 | "errors" |
| 5 | "log" | 5 | "log" |
| 6 | "math" | 6 | "math" |
| 7 | u "net/url" | 7 | "net/url" |
| 8 | "strconv" | 8 | "strconv" |
| 9 | "strings" | 9 | "strings" |
| 10 | ) | 10 | ) |
| 11 | 11 | ||
| 12 | /* AVATARS AND EMOJIS */ | 12 | /* AVATARS AND EMOJIS */ |
| 13 | func AEmedia(name string, t rune) (string, error) { | 13 | func AEmedia(name string, t rune) (string, error) { |
| 14 | if len(name) < 2 { | ||
| 15 | return "", errors.New("name must be specified") | ||
| 16 | } | ||
| 14 | // список всех возможных расширений | 17 | // список всех возможных расширений |
| 15 | var extensions = [3]string{ | 18 | var extensions = [3]string{ |
| 16 | ".jpg", | 19 | ".jpg", |
| @@ -25,9 +28,10 @@ func AEmedia(name string, t rune) (string, error) { | |||
| 25 | switch t { | 28 | switch t { |
| 26 | case 'a': | 29 | case 'a': |
| 27 | b.WriteString("https://a.deviantart.net/avatars-big/") | 30 | b.WriteString("https://a.deviantart.net/avatars-big/") |
| 28 | b.WriteString(name[:1]) | 31 | name_without_dashes := strings.ReplaceAll(name, "-", "_") |
| 32 | b.WriteString(name_without_dashes[:1]) | ||
| 29 | b.WriteString("/") | 33 | b.WriteString("/") |
| 30 | b.WriteString(name[1:2]) | 34 | b.WriteString(name_without_dashes[1:2]) |
| 31 | b.WriteString("/") | 35 | b.WriteString("/") |
| 32 | case 'e': | 36 | case 'e': |
| 33 | b.WriteString("https://e.deviantart.net/emoticons/") | 37 | b.WriteString("https://e.deviantart.net/emoticons/") |
| @@ -60,7 +64,7 @@ type DailyDeviations struct { | |||
| 60 | Deviations []Deviation | 64 | Deviations []Deviation |
| 61 | } | 65 | } |
| 62 | 66 | ||
| 63 | func DailyDeviationsFunc(page int) (dd DailyDeviations) { | 67 | func GetDailyDeviations(page int) (dd DailyDeviations) { |
| 64 | ujson("dabrowse/networkbar/rfy/deviations?page="+strconv.Itoa(page), &dd) | 68 | ujson("dabrowse/networkbar/rfy/deviations?page="+strconv.Itoa(page), &dd) |
| 65 | return | 69 | return |
| 66 | } | 70 | } |
| @@ -74,23 +78,21 @@ type Search struct { | |||
| 74 | ResultsGalleryTemp []Deviation `json:"results"` | 78 | ResultsGalleryTemp []Deviation `json:"results"` |
| 75 | } | 79 | } |
| 76 | 80 | ||
| 77 | func SearchFunc(query string, page int, scope rune, user ...string) (ss Search, e error) { | 81 | func PerformSearch(query string, page int, scope rune, user ...string) (ss Search, e error) { |
| 78 | var url strings.Builder | 82 | var buildurl strings.Builder |
| 79 | e = nil | 83 | e = nil |
| 80 | 84 | ||
| 81 | // о5 построение ссылок. | 85 | // о5 построение ссылок. |
| 82 | switch scope { | 86 | switch scope { |
| 83 | case 'a': // поиск артов по названию | 87 | case 'a': // поиск артов по названию |
| 84 | url.WriteString("dabrowse/search/all?q=") | 88 | buildurl.WriteString("dabrowse/search/all?q=") |
| 85 | case 't': // поиск артов по тегам | 89 | case 't': // поиск артов по тегам |
| 86 | url.WriteString("dabrowse/networkbar/tag/deviations?tag=") | 90 | buildurl.WriteString("dabrowse/networkbar/tag/deviations?tag=") |
| 87 | case 'g': // поиск артов пользователя или группы | 91 | case 'g': // поиск артов пользователя или группы |
| 88 | if user != nil { | 92 | if user != nil { |
| 89 | url.WriteString("dashared/gallection/search?username=") | 93 | buildurl.WriteString("dashared/gallection/search?username=") |
| 90 | for _, a := range user { | 94 | buildurl.WriteString(user[0]) |
| 91 | url.WriteString(a) | 95 | buildurl.WriteString("&type=gallery&order=most-recent&init=true&limit=50&q=") |
| 92 | } | ||
| 93 | url.WriteString("&type=gallery&order=most-recent&init=true&limit=50&q=") | ||
| 94 | } else { | 96 | } else { |
| 95 | e = errors.New("missing username (last argument)") | 97 | e = errors.New("missing username (last argument)") |
| 96 | return | 98 | return |
| @@ -99,16 +101,16 @@ func SearchFunc(query string, page int, scope rune, user ...string) (ss Search, | |||
| 99 | log.Fatalln("Invalid type.\n- 'a' -- all;\n- 't' -- tag;\n- 'g' - gallery.") | 101 | log.Fatalln("Invalid type.\n- 'a' -- all;\n- 't' -- tag;\n- 'g' - gallery.") |
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | url.WriteString(u.QueryEscape(query)) | 104 | buildurl.WriteString(url.QueryEscape(query)) |
| 103 | if scope != 'g' { // если область поиска не равна поиску по группам, то активируется этот код | 105 | if scope != 'g' { // если область поиска не равна поиску по группам, то активируется этот код |
| 104 | url.WriteString("&page=") | 106 | buildurl.WriteString("&page=") |
| 105 | } else { // иначе вместо страницы будет оффсет и страница умножится на 50 | 107 | } else { // иначе вместо страницы будет оффсет и страница умножится на 50 |
| 106 | url.WriteString("&offset=") | 108 | buildurl.WriteString("&offset=") |
| 107 | page = 50 * page | 109 | page = 50 * page |
| 108 | } | 110 | } |
| 109 | url.WriteString(strconv.Itoa(page)) | 111 | buildurl.WriteString(strconv.Itoa(page)) |
| 110 | 112 | ||
| 111 | ujson(url.String(), &ss) | 113 | ujson(buildurl.String(), &ss) |
| 112 | 114 | ||
| 113 | if scope == 'g' { | 115 | if scope == 'g' { |
| 114 | ss.Results = ss.ResultsGalleryTemp | 116 | ss.Results = ss.ResultsGalleryTemp |
todo.md deleted −2
| @@ -1,2 +0,0 @@ | |||
| 1 | - groups | ||
| 2 | - images in comments\ No newline at end of file | ||
user-group.go +43 −35
| @@ -1,44 +1,12 @@ | |||
| 1 | package devianter | 1 | package devianter |
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "errors" | ||
| 4 | "strconv" | 5 | "strconv" |
| 5 | "strings" | 6 | "strings" |
| 6 | ) | 7 | ) |
| 7 | 8 | ||
| 8 | // структура группы или пользователя | 9 | // структура группы или пользователя |
| 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 { | ||
| 17 | Username string | ||
| 18 | } | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | type About struct { | ||
| 23 | Country, Website, WebsiteLabel, Gender string | ||
| 24 | RegDate int64 `json:"deviantFor"` | ||
| 25 | Description Text `json:"textContent"` | ||
| 26 | |||
| 27 | SocialLinks []struct { | ||
| 28 | Value string | ||
| 29 | } | ||
| 30 | Interests []struct { | ||
| 31 | Label, Value string | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | type users struct { | ||
| 36 | About About | ||
| 37 | CoverDeviation struct { | ||
| 38 | Deviation Deviation `json:"coverDeviation"` | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | type GRuser struct { | 10 | type GRuser struct { |
| 43 | ErrorDescription string | 11 | ErrorDescription string |
| 44 | Owner struct { | 12 | Owner struct { |
| @@ -106,14 +74,21 @@ type Group struct { | |||
| 106 | } | 74 | } |
| 107 | 75 | ||
| 108 | // подходит как группа, так и пользователь | 76 | // подходит как группа, так и пользователь |
| 109 | func (s Group) GroupFunc() (g GRuser) { | 77 | func (s Group) GetGroup() (g GRuser, err error) { |
| 78 | if s.Name == "" { | ||
| 79 | return g, errors.New("missing Name field") | ||
| 80 | } | ||
| 110 | ujson("dauserprofile/init/about?username="+s.Name, &g) | 81 | ujson("dauserprofile/init/about?username="+s.Name, &g) |
| 111 | 82 | ||
| 112 | return | 83 | return |
| 113 | } | 84 | } |
| 114 | 85 | ||
| 115 | // гарелея пользователя или группы | 86 | // гарелея пользователя или группы |
| 116 | func (s Group) Gallery(page int, folderid ...int) (g Group) { | 87 | func (s Group) GetGallery(page int, folderid ...int) (g Group, err error) { |
| 88 | if s.Name == "" { | ||
| 89 | return g, errors.New("missing Name field") | ||
| 90 | } | ||
| 91 | |||
| 117 | var url strings.Builder | 92 | var url strings.Builder |
| 118 | if folderid[0] > 0 { | 93 | if folderid[0] > 0 { |
| 119 | page-- | 94 | page-- |
| @@ -137,3 +112,36 @@ func (s Group) Gallery(page int, folderid ...int) (g Group) { | |||
| 137 | ujson(url.String(), &g.Content) | 112 | ujson(url.String(), &g.Content) |
| 138 | return | 113 | return |
| 139 | } | 114 | } |
| 115 | |||
| 116 | type GroupAbout struct { | ||
| 117 | FoundatedAt timeStamp `json:"foundationTs"` | ||
| 118 | Description Text | ||
| 119 | } | ||
| 120 | type GroupAdmins struct { | ||
| 121 | Results []struct { | ||
| 122 | TypeId int | ||
| 123 | User struct { | ||
| 124 | Username string | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | type About struct { | ||
| 130 | Country, Website, WebsiteLabel, Gender string | ||
| 131 | RegDate int64 `json:"deviantFor"` | ||
| 132 | Description Text `json:"textContent"` | ||
| 133 | |||
| 134 | SocialLinks []struct { | ||
| 135 | Value string | ||
| 136 | } | ||
| 137 | Interests []struct { | ||
| 138 | Label, Value string | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | type users struct { | ||
| 143 | About About | ||
| 144 | CoverDeviation struct { | ||
| 145 | Deviation Deviation `json:"coverDeviation"` | ||
| 146 | } | ||
| 147 | } | ||
util.go +14 −16
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | ) | 9 | ) |
| 10 | 10 | ||
| 11 | // функция для высера ошибки в stderr | 11 | // функция для высера ошибки в stderr |
| 12 | func err(txt error) { | 12 | func try(txt error) { |
| 13 | if txt != nil { | 13 | if txt != nil { |
| 14 | println(txt.Error()) | 14 | println(txt.Error()) |
| 15 | } | 15 | } |
| @@ -17,11 +17,9 @@ func err(txt error) { | |||
| 17 | 17 | ||
| 18 | // сокращение для вызова щенка и парсинга жсона | 18 | // сокращение для вызова щенка и парсинга жсона |
| 19 | func ujson(data string, output any) { | 19 | func ujson(data string, output any) { |
| 20 | input, e := puppy(data) | 20 | input, err := puppy(data) |
| 21 | err(e) | 21 | try(err) |
| 22 | 22 | try(json.Unmarshal([]byte(input), output)) | |
| 23 | eee := json.Unmarshal([]byte(input), output) | ||
| 24 | err(eee) | ||
| 25 | } | 23 | } |
| 26 | 24 | ||
| 27 | /* REQUEST SECTION */ | 25 | /* REQUEST SECTION */ |
| @@ -34,32 +32,32 @@ type reqrt struct { | |||
| 34 | } | 32 | } |
| 35 | 33 | ||
| 36 | // функция для совершения запроса | 34 | // функция для совершения запроса |
| 35 | var UserAgent string | ||
| 36 | |||
| 37 | func request(uri string, other ...string) reqrt { | 37 | func request(uri string, other ...string) reqrt { |
| 38 | var r reqrt | 38 | var r reqrt |
| 39 | 39 | ||
| 40 | // создаём новый запрос | 40 | // создаём новый запрос |
| 41 | cli := &http.Client{} | 41 | cli := &http.Client{} |
| 42 | req, e := http.NewRequest("GET", uri, nil) | 42 | req, e := http.NewRequest("GET", uri, nil) |
| 43 | err(e) | 43 | try(e) |
| 44 | 44 | ||
| 45 | req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0.0") | 45 | req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0.0") |
| 46 | 46 | ||
| 47 | // куки и UA-шник | 47 | // куки и UA-шник |
| 48 | for num, rng := range other { | 48 | if UserAgent != "" { |
| 49 | switch num { | 49 | req.Header.Set("User-Agent", UserAgent) |
| 50 | case 1: | 50 | } |
| 51 | req.Header.Set("User-Agent", rng) | 51 | if len(other) != 0 { |
| 52 | case 0: | 52 | req.Header.Set("Cookie", other[0]) |
| 53 | req.Header.Set("Cookie", rng) | ||
| 54 | } | ||
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | resp, e := cli.Do(req) | 55 | resp, e := cli.Do(req) |
| 58 | err(e) | 56 | try(e) |
| 59 | defer resp.Body.Close() | 57 | defer resp.Body.Close() |
| 60 | 58 | ||
| 61 | body, e := io.ReadAll(resp.Body) | 59 | body, e := io.ReadAll(resp.Body) |
| 62 | err(e) | 60 | try(e) |
| 63 | 61 | ||
| 64 | // заполняем структуру | 62 | // заполняем структуру |
| 65 | r.Body = string(body) | 63 | r.Body = string(body) |