Skip to content

Commit

Permalink
support nicovideo OTP authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
iamtakingiteasy committed Mar 2, 2022
1 parent a290f50 commit 6ee1e03
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 7 deletions.
2 changes: 1 addition & 1 deletion cmd/jaroidfedi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func handleDefault(c *binconfig, fedipost *app.Fedipost) {
}

func startReporter() mediaservice.Reporter {
reporter := mediaservice.NewReporter(0, 10)
reporter := mediaservice.NewReporter(0, 10, os.Stdin)

go func() {
for s := range reporter.Messages() {
Expand Down
4 changes: 2 additions & 2 deletions discordbot/modules/nico/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,9 @@ func (mod *module) commandDownload(ctx *router.Context) error {
func queuedMessage(id, content string, pos int64) string {
if pos > 0 {
return fmt.Sprintf("%s queued at position %d", id, pos)
} else {
return fmt.Sprintf("%s %s", id, content)
}

return fmt.Sprintf("%s %s", id, content)
}

func (mod *module) parseNicoDownloadArgs(ctx *router.Context) (format, subs string, post, preview bool) {
Expand Down
4 changes: 2 additions & 2 deletions discordbot/modules/nico/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (mod *module) updateMessage(guildID, channelID, messageID, line string) {
}

func (mod *module) listFormatsVideo(task *TaskList) (err error) {
reporter := mediaservice.NewReporter(time.Second*10, 1)
reporter := mediaservice.NewReporter(time.Second*10, 1, nil)

go func() {
for r := range reporter.Messages() {
Expand Down Expand Up @@ -224,7 +224,7 @@ func (mod *module) downloadVideo(ctx context.Context, id string, task *TaskDownl
output := nicopost.SaveFilepath(mod.config.Config.Private.Nicovideo.Directory, task.VideoURL, task.Format)

opts := &mediaservice.SaveOptions{
Reporter: mediaservice.NewReporter(time.Second*10, 1),
Reporter: mediaservice.NewReporter(time.Second*10, 1, nil),
}

if task.Subs != "" {
Expand Down
85 changes: 84 additions & 1 deletion integration/nicovideo/download.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nicovideo

import (
"bufio"
"bytes"
"context"
"encoding/json"
Expand All @@ -22,6 +23,7 @@ import (

var (
dataAPIDataRegex = regexp.MustCompile(`data-api-data="([^"]+)"`)
otpRegexp = regexp.MustCompile(`action="(/mfa[^"]*)"`)
)

// BoolYesNo formats boolean as "yes" and "no" strings during json un/marshalling
Expand Down Expand Up @@ -340,7 +342,7 @@ func (client *Client) fetchInitPage(ctx context.Context, url string, reporter me
if succ {
bs, err = client.getPage(ctx, url)
if err != nil {
return nil, err
bs = nil
}
}
}
Expand Down Expand Up @@ -377,9 +379,90 @@ func (client *Client) auth(reporter mediaservice.Reporter) (bool, error) {
return false, fmt.Errorf("invalid credentials")
}

bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false, err
}

if strings.Contains(string(bs), `name="otp"`) {
return client.otpAction(string(bs), reporter)
}

return succ, nil
}

func (client *Client) otpAction(s string, reporter mediaservice.Reporter) (bool, error) {
if !reporter.CanRead() {
return false, nil
}

matches := otpRegexp.FindAllStringSubmatch(s, -1)

if len(matches) == 0 {
return false, nil
}

u, err := url.Parse(client.LoginURI)
if err != nil {
return false, nil
}

reporter.Submit("Nicovideo has requested one-time password to perform login.", true)
reporter.Submit("Please check your EMail and input 6-digit code on next line:", true)

var otp string

for {
otp, err = bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return false, err
}

otp = strings.TrimSpace(otp)

if len(otp) == 0 {
return false, nil
}

if len(otp) == 6 {
if _, err = strconv.ParseInt(otp, 10, 64); err == nil {
break
}
}

fmt.Println("Did not recognized input, enter 6-digit code or empty string to skip")
}

u.Path = ""
u.RawQuery = ""

targetURL := u.String() + matches[0][1]

resp, err := client.HTTPClient.PostForm(targetURL, url.Values{
"otp": []string{otp},
"loginBtn": []string{"Login"},
"is_mfa_trusted_device": []string{"true"},
"device_name": []string{"jaroid"},
})
if err != nil {
client.Auth.invalid = true

reporter.Submit("Invalid credentials", true)

return false, err
}

defer func() {
_ = resp.Body.Close()
}()

if resp.StatusCode/100 == 2 {
return true, nil
}

return false, nil
}

func (client *Client) fetchAPIData(ctx context.Context, url string, reporter mediaservice.Reporter) (*APIData, error) {
initpage, err := client.fetchInitPage(ctx, url, reporter)
if err != nil {
Expand Down
34 changes: 33 additions & 1 deletion mediaservice/reporter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mediaservice

import (
"bufio"
"io"
"sync"
"time"
)
Expand All @@ -13,6 +15,8 @@ type Reporter interface {
Messages() <-chan string
Submit(msg string, force bool)
Close()
CanRead() bool
ReadLine() (string, error)
}

type dummyReporter struct {
Expand All @@ -30,24 +34,40 @@ func (*dummyReporter) Close() {

}

func (*dummyReporter) CanRead() bool {
return false
}

func (*dummyReporter) ReadLine() (string, error) {
return "", nil
}

// NewDummyReporter returns new dummy reporter implementation
func NewDummyReporter() Reporter {
return &dummyReporter{}
}

// NewReporter returns new reporter instance with provided rate limit and buffer size for Messages() channel
func NewReporter(rate time.Duration, buf int) Reporter {
func NewReporter(rate time.Duration, buf int, reader io.Reader) Reporter {
var br *bufio.Reader

if reader != nil {
br = bufio.NewReader(reader)
}

rep := &reporterImpl{
m: &sync.Mutex{},
messages: make(chan string, buf),
rate: rate,
reader: br,
}

return rep
}

type reporterImpl struct {
m *sync.Mutex
reader *bufio.Reader
messages chan string
rate time.Duration
last int64
Expand Down Expand Up @@ -104,3 +124,15 @@ func (r *reporterImpl) Submit(msg string, force bool) {

r.messages <- msg
}

func (r *reporterImpl) CanRead() bool {
return r.reader != nil
}

func (r *reporterImpl) ReadLine() (string, error) {
if !r.CanRead() {
return "", nil
}

return r.reader.ReadString('\n')
}

0 comments on commit 6ee1e03

Please sign in to comment.