Golang配合QQ機器人獲取Pixiv ea7e6c5a5f673669f0d56d8f39056eae每日列表併發送澀圖(未完)

TODO\color{#EE2C2C}{TODO}
CQHttpQ\color{#6495ED}{-通過CQHttp插件連接酷Q}
使go\color{#6495ED}{-解決使用go程序發出的請求無法通過代理的問題}

根據分析好的安卓App Pivix用戶登錄response來編寫解析json需要的結構體

package fvckPixiv

type UserProfileImageUrls struct {
	Px16x16   string `json:"px_16x16"`
	Px170x170 string `json:"px_170x170"`
	Px50x50   string `json:"px_50x50"`
}

type PixOAuthHandler struct {
	Account                 string                 `json:"account"`
	Id                      string                 `json:"id"`
	IsMailAuthorized        bool                   `json:"is_mail_authorized"`
	IsPremium               bool                   `json:"is_premium"`
	MailAddress             string                 `json:"mail_address"`
	Name                    string                 `json:"name"`
	ProfileImageUrls        UserProfileImageUrls   `json:"profile_image_urls"`
	RequirePolicyAgreement  bool                   `json:"require_policy_agreement"`
	XRestrict               int                    `json:"x_restrict"`
}

type PixLoginResponseHandler struct {
	AccessToken  string `json:"access_token"`
	DeviceToken  string `json:"device_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Scope        string `json:"scope"`
	TokenType    string `json:"token_type"`
}

type PixLoginResponse struct {
	Response PixLoginResponseHandler `json:"response"`
}

生成構建post請求的參數需要的參數X-Client-Hash和X-Client-Time

package fvckPixiv
import (
	"crypto/md5"
	"encoding/hex"
	"strings"
	"time"
)

var HashSalt string = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c"

func getXClientHashAndTime() (string, string)  {
	var result  string
	var curTime string = getCurFormatTime()
	
	data        := []byte(curTime+HashSalt)
	encodedData := md5.Sum(data)
	
	for _, b := range encodedData {
		temp    := make([]byte, 1)
		temp[0] = byte(int(b) & 0xff)
		hexString := hex.EncodeToString(temp)
		if len(hexString) < 2 {
			hexString = "0" + hexString
		}
		result = result+hexString
	}
	
	return result, curTime
}

func getCurFormatTime() string {
	rawTime := time.Now().Format("2006-01-02 15:04:05")
	var curTime string
	
	for index, timeMsg := range strings.Split(rawTime, " ") {
		if index == 0 {
			curTime = timeMsg
		} else if index == 1 {
			curTime = curTime + "T" + timeMsg + "+08:00"
		}
	}
	return curTime
}

模擬登錄&發送請求

package fvckPixiv

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

type PixClient struct {
	clnt             http.Client
	pixLoginResponse PixLoginResponse
}

func NewPixivClient() *PixClient {
	transport := &http.Transport {
		TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
		Dial: func(netw, addr string) (net.Conn, error) {
			deadline := time.Now().Add(60 * time.Second)
			c, err := net.DialTimeout(netw, addr, time.Second*60)
			if err != nil {
				panic(err)
			}
			
			err = c.SetDeadline(deadline)
			return c, err
		},
		ResponseHeaderTimeout: time.Second*600,
	}
	return &PixClient {
		clnt:             http.Client{Transport: transport},
		pixLoginResponse: PixLoginResponse{},
	}
}

func (client *PixClient) PixLogin() {
	
	info := "password=此處是密碼&client_id=MOBrBDS8blbauoSck0ZfDbtuzpyT&get_secure_url=true&username=此處是用戶名&include_policy=true&client_secret=lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj&device_token=pixiv&grant_type=password"
	
	request, err := http.NewRequest("POST",
		"https://oauth.secure.pixiv.net/auth/token",
		strings.NewReader(info))
	if err != nil {
		log.Println(err)
	}
	
	clientHash, curTime := getXClientHashAndTime()
	
	request.Header.Set("User-Agent", "PixivAndroidApp/5.0.155 (Android 5.1.1; OPPO R17)")
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	request.Header.Set("App-OS", "Android")
	request.Header.Set("App-OS-Version", "5.1.1")
	request.Header.Set("App-Version", "5.0.166")
	request.Header.Set("X-Client-Hash", clientHash)
	request.Header.Set("X-Client-Time", curTime)
	request.Header.Set("Connection", "Keep-Alive")
	
	for key, value := range request.Header {
		fmt.Println(key, value)
	}
	
	resp, err := client.clnt.Do(request)
	if err != nil {
		panic(err)
	}
	
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	} else {
		log.Println(string(data))
	}
	err = json.Unmarshal(data, &client.pixLoginResponse)
	if err != nil {
		panic(err)
	}
	
}

func (client *PixClient) PixivGetR18Daily(userAuthorization string) {
	
	req, err := http.NewRequest("GET",
		"https://app-api.pixiv.net/v1/illust/ranking?filter=for_android&mode=day_r18",
		strings.NewReader(""))
	
	if err != nil {panic(err)}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Authorization", "Bearer " + client.pixLoginResponse.Response.AccessToken)
	req.Header.Set("User-Agent", "PixivAndroidApp/5.0.155 (Android 5.1.1; OPPO R17)")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Cache-Control", "no-cache")
	
	
	resp, err := client.clnt.Do(req)
	if err != nil {panic(err)}
	
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {panic(err)}
	fmt.Println(content)
}


func (client *PixClient) HSGet(url string) (*http.Response, error) {
	
	resp, err := client.clnt.Get(url)
	if err != nil {return nil, err}
	
	return resp, nil
}

func (client *PixClient) HSPost(url string, args string) (*http.Response, error) {
	
	var rsp io.Reader
	resp, err := client.clnt.Post(url, args, rsp)
	if err != nil {
		log.Println(err)
	}
	
	return resp, nil
}

下載文件到本地

package downloader

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

func IsFileExist(filename string, fileSize int64) bool {
	
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		log.Println(info)
		return false
	}
	
	if fileSize == info.Size() {
		log.Println("文件已存在!", info.Name(), info.Size(), info.ModTime())
		return true
	}
	del := os.Remove(filename)
	if del != nil {
		log.Println(del)
	}
	return false
	
}

func DownloadFile(url string, localPath string,
	feedbacks ...func(length, downLen int64)) error {
	
	var (
		fsize   int64
		buf   = make([]byte, 128*1024)
		written int64
	)
	tmpFilePath := localPath + ".download"
	log.Println(tmpFilePath)
	
	client := new(http.Client)
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	
	fsize, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 32)
	if err != nil {
		log.Println(err)
	}
	if IsFileExist(localPath, fsize) {
		return err
	}
	
	log.Println("fsize", fsize)
	file, err := os.Create(tmpFilePath)
	if err != nil {
		return err
	}
	defer file.Close()
	
	if resp.Body == nil {
		return errors.New("body is null")
	}
	defer resp.Body.Close()
	
	for {
		nr, er := resp.Body.Read(buf)
		if nr > 0 {
			nw, ew := file.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF {
				err = er
			}
			break
		}
		for _, feedback := range feedbacks {
			feedback(fsize, written)
		}
	}
	log.Println(err)
	if err == nil {
		file.Close()
		err = os.Rename(tmpFilePath, localPath)
		fmt.Println(err)
	}
	return err
}

機器人檢測消息

package main

import (
	"fmt"
	"log"
	
	"CQApp/src/kkpackage"
	"github.com/catsworld/qq-bot-api"
)

var bot  *qqbotapi.BotAPI


func main() {
	var err error
	bot, err = qqbotapi.NewBotAPI("", "ws://39.106.219.180:6700", "CQHTTP_SECRET")
	if err != nil {
		log.Fatal(err)
	}
	bot.Debug = true
	
	conf := qqbotapi.NewUpdate(0)
	conf.PreloadUserInfo = true
	updates, err := bot.GetUpdatesChan(conf)
	
	var keyWords = [1]string {
		"KKP",
	}
	
	for update := range updates {
		if update.Message == nil {
			continue
		}
		fmt.Println(update)
		log.Printf("[%s] %s\n", update.Message.From.String(), update.Message.Text)
		
		var flag int = -1
		for index, str := range keyWords {
			if len(str) > 2 && str == update.Message.Text[0:3] {
				flag = index
				break
			}
		}
		switch flag {
		case 0:
			go kkpackage.KKP(bot, update)
			break
		default:
			fmt.Println("Unknown operation")
			break
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章