-
Notifications
You must be signed in to change notification settings - Fork 4
/
mangadex.go
225 lines (177 loc) · 5.38 KB
/
mangadex.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package mangadex
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strings"
"github.com/tavomoya/mangagram/models"
"github.com/PuerkitoBio/goquery"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Mangadex is a struct used to attach
// all functionality available within this
// manga source
type Mangadex struct {
DB *models.DatabaseConfig
ApiURL string
ViewMangaURL string
client *http.Client
}
// NewMangadex function returns a pointer to a Mangadex
// struct that can be used to call all of its methods
func NewMangadex(db *models.DatabaseConfig) *Mangadex {
jar, _ := cookiejar.New(nil)
return &Mangadex{
DB: db,
ApiURL: "https://mangadex.org/search?title=%s",
ViewMangaURL: "https://mangadex.org%s",
client: &http.Client{Jar: jar},
}
}
// ViewManga method returns a string with
// the Manga's URL
func (m *Mangadex) ViewManga() string {
return m.ViewMangaURL
}
// QueryManga method receives a string that refers to the Manga name, it then
// makes a call to the Mangadex API, and with the results it returns a
// pointer to a ApiQuerySuggestions struct
func (m *Mangadex) QueryManga(name string) *models.ApiQuerySuggestions {
if name == "" {
return nil
}
log.Println("Thename to query: ", name)
// Login
err := m.login()
if err != nil {
return nil
}
escapedName := url.PathEscape(name)
path := fmt.Sprintf(m.ApiURL, escapedName)
log.Println("the path: ", path)
req := m.getRequest(path)
res, err := m.client.Do(req)
if err != nil {
log.Println("Error getting to search path: ", err)
return nil
}
defer res.Body.Close()
page, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Println("There was an error getting to the search pahe: ", err)
return nil
}
suggestions := new(models.ApiQuerySuggestions)
page.Find("div a.manga_title").Each(func(idx int, selection *goquery.Selection) {
mangaTitle, _ := selection.Attr("title")
mangaURL, _ := selection.Attr("href")
manga := models.MangaSuggestions{
Data: mangaURL,
Value: mangaTitle,
}
suggestions.Suggestions = append(suggestions.Suggestions, manga)
})
return suggestions
}
func (m *Mangadex) GetLastMangaChapter(mangaURL string) (string, error) {
if mangaURL == "" {
log.Println("No manga supplied")
return "", nil
}
// Login
err := m.login()
if err != nil {
return "", nil
}
req := m.getRequest(mangaURL)
res, err := m.client.Do(req)
if err != nil {
log.Println("Error getting to search path: ", err)
return "", nil
}
defer res.Body.Close()
page, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Println("There was an error trying to get to the manga page: ", err)
return "", err
}
lastChapterURL := ""
page.Find("div.chapter-row").EachWithBreak(func(idx int, sel *goquery.Selection) bool {
lang, _ := sel.Attr("data-lang")
if lang == "1" {
lastChapterURL, _ = sel.Find("a.text-truncate").First().Attr("href")
return false
}
return true
})
return fmt.Sprintf(m.ViewMangaURL, lastChapterURL), nil
}
// Subscribe method receives a subscription model, this contains information
// about a User or Group that wants to receive alerts from a certain Manga title.
// The method will save this in a 'Subscription' collection in MongoDB, as well as
// set a value for the lastChapter of the Manga title.
func (m *Mangadex) Subscribe(subscription *models.Subscription) error {
// Validate subscription data
if subscription.MangaName == "" || subscription.MangaURL == "" {
log.Println("No manga supplied for subscription")
return errors.New("No manga supplied for subscription")
}
if subscription.ChatID == 0 {
log.Println("No Chat supplied for subscription")
return errors.New("No Chat supplied for subscription")
}
subscription.ID = primitive.NewObjectID()
subscription.MangaFeed = 5
subscription.LastChapterURL, _ = m.GetLastMangaChapter(subscription.MangaURL)
_, err := m.DB.MongoClient.Collection("subscription").InsertOne(m.DB.Ctx, subscription)
if err != nil && !strings.Contains(err.Error(), "subscription_unq") {
log.Println("There was an error creating new subscription: ", err)
return err
}
return nil
}
func (m *Mangadex) login() error {
loginURL := fmt.Sprintf(m.ViewMangaURL, "/ajax/actions.ajax.php?function=login")
username := os.Getenv("MANGADEX_USERNAME")
password := os.Getenv("MANGADEX_PASSWORD")
data := &bytes.Buffer{}
writer := multipart.NewWriter(data)
writer.WriteField("login_username", username)
writer.WriteField("login_password", password)
req, err := http.NewRequest("POST", loginURL, data)
if err != nil {
log.Println("Error creating login request: ", err)
return err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("User-Agent", "mangadex-api/4.0.0")
req.Header.Set("X-Requested-With", "XMLHttpRequest")
res, err := m.client.Do(req)
if err != nil {
log.Println("Error logging into Mangadex => ", err)
return err
}
defer res.Body.Close()
_, err = ioutil.ReadAll(res.Body)
if err != nil {
log.Println("Error reading mangadex response: ", err)
return err
}
return nil
}
func (m *Mangadex) getRequest(url string) *http.Request {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Println("Error creating login request: ", err)
return nil
}
req.Header.Set("User-Agent", "mangadex-api/4.0.0")
return req
}