opq-osc / OPQ-SetuBot

基于botoy和OPQBot的色图机器人
MIT License
212 stars 34 forks source link

pixiv token 刷新失败 #50

Closed test-black closed 3 years ago

test-black commented 3 years ago

已配置代理且能访问外网,p 站是不是又改刷新地址了?

snipaste_20211013_234527

snipaste_20211013_234559

yuban10703 commented 3 years ago

image 刷新没问题,你看看你.PixivToken.json文件里的token正常么

test-black commented 3 years ago

我用 wiki 那个脚本重新跑了一次 token 丢进去还是报这个错误

test-black commented 3 years ago

v2ray 开了一个10808 端口的 socks5 代理和 10809 的 http 代理 snipaste_20211014_011639

botoy.json文件相较之前没有变化(之前刷新无问题),但出现提到的问题 snipaste_20211014_011311

如果改用 http 代理则无问题 snipaste_20211014_011506

yuban10703 commented 3 years ago

增加了错误提示.你看看报错啥...

test-black commented 3 years ago

snipaste_20211014_102401

yuban10703 commented 3 years ago

不知道什么问题,我这里测试socks和http都正常...

test-black commented 3 years ago

我重新克隆仓库能复现错误,你那边也试试?

yuban10703 commented 3 years ago
# -*- coding: utf-8 -*-
# @Time    : 2021/6/20 21:01
# @Author  : yuban10703

import hashlib
import json
import random
import re
import sys
import time
import uuid
from datetime import datetime, timedelta
from pathlib import Path
from typing import List

import httpx
from botoy import logger
from botoy.schedule import scheduler
from retrying import retry

from ._proxies import proxies, async_transport, transport
from ..model import FinishSetuData, GetSetuConfig

class PixivToken:
    def __init__(self):
        self.tokenPath = Path(__file__).parent.parent / ".PixivToken.json"
        self.tokendata = {}
        self.Client = httpx.Client(proxies=proxies, transport=transport)

    def headers(self):
        hash_secret = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c"
        X_Client_Time = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S+08:00")
        X_Client_Hash = hashlib.md5(
            (X_Client_Time + hash_secret).encode("utf-8")
        ).hexdigest()
        headers = {
            "User-Agent": "PixivAndroidApp/5.0.197 (Android 10; Redmi 4)",
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept-Language": "zh_CN_#Hans",
            "App-OS": "android",
            "App-OS-Version": "10",
            "App-Version": "5.0.197",
            "X-Client-Time": X_Client_Time,
            "X-Client-Hash": X_Client_Hash,
            "Host": "oauth.secure.pixiv.net",
            "Accept-Encoding": "gzip",
        }
        return headers

    @retry(stop_max_attempt_number=2, wait_random_max=3000)
    def refresh_token(self):
        logger.info("尝试刷新Pixiv_token")
        data = {
            "client_id": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
            "client_secret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
            "grant_type": "refresh_token",
            "refresh_token": self.tokendata["refresh_token"],
            "device_token": self.tokendata["device_token"]
            if "device_token" in self.tokendata.keys()
            else uuid.uuid4().hex,
            "get_secure_url": "true",
            "include_policy": "true",
        }
        self.tokendata = self.Client.post(
            url="https://oauth.secure.pixiv.net/auth/token",
            data=data,
            headers=self.headers()
        ).json()
        self.tokendata["time"] = time.time()
        logger.success("刷新token成功~")
        self.saveToken()

    def continue_refresh_token(self):
        self.refresh_token()
        nextTime = int(
            self.tokendata["expires_in"] - (time.time() - self.tokendata["time"])
        )
        self.addJob(nextTime)
        return

    def saveToken(self):
        with open(self.tokenPath, "w", encoding="utf-8") as f:
            json.dump(self.tokendata, f, indent=4, ensure_ascii=False)
        logger.success("PixivToken已保存到.PixivToken.json")
        return

    def addJob(self, next_time: int):
        logger.info("离下次刷新还有:{}s".format(next_time))
        scheduler.add_job(
            self.continue_refresh_token,
            next_run_time=datetime.now() + timedelta(seconds=next_time - 1),
            misfire_grace_time=30,
        )

    def main(self):
        try:
            with open(self.tokenPath, "r", encoding="utf-8") as f:
                self.tokendata = json.load(f)
                logger.success("读取.PixivToken.json成功~")
        except Exception as e:
            logger.error(".PixivToken.json载入失败,请检查内容并重新启动~\r\n{}".format(e))
            sys.exit(0)
        if self.tokendata["refresh_token"] == "":
            logger.error("PixivToken不存在")
            sys.exit(0)
        if "time" not in self.tokendata.keys():  # 没time字段就是第一次启动
            self.continue_refresh_token()
            return
        if time.time() - self.tokendata["time"] >= int(
                self.tokendata["expires_in"]
        ):  # 停止程序后再次启动时间后的间隔时间超过刷新间隔
            self.continue_refresh_token()
            return
        self.addJob(
            int(self.tokendata["expires_in"] - (time.time() - self.tokendata["time"]))
        )

pixivToken = PixivToken()
pixivToken.main()

class Pixiv:
    def __init__(self, config: GetSetuConfig):
        self.config = config

    async def get(self):  # p站热度榜
        tags = self.config.tags.copy()
        if self.config.level == 1:  # R18 only
            tags.append("R-18")
        elif self.config.level == 2:  # all
            if random.choice([True, False]):
                tags.append("R-18")
        url = "https://app-api.pixiv.net/v1/search/popular-preview/illust"
        params = {
            "filter": "for_android",
            "include_translated_tag_results": "true",
            "merge_plain_keyword_results": "true",
            "word": " ".join(tags),
            "search_target": "partial_match_for_tags",
        }  # 精确:exact_match_for_tags,部分:partial_match_for_tags
        headers = pixivToken.headers()
        headers["Host"] = "app-api.pixiv.net"
        headers["Authorization"] = "Bearer {}".format(
            pixivToken.tokendata["access_token"]
        )
        try:
            async with httpx.AsyncClient(
                    proxies=proxies, transport=async_transport
            ) as client:
                res = await client.get(url, params=params, headers=headers, timeout=10)
            data = res.json()
        except Exception as e:
            logger.warning("Pixiv热度榜获取失败~:\r\n{}".format(e))
            return []
        else:
            if res.status_code == 200:
                data_finally = self.process_data(data)
                if len(data_finally) <= self.config.toGetNum - self.config.doneNum:
                    return data_finally
                else:
                    return random.sample(
                        self.process_data(data),
                        self.config.toGetNum - self.config.doneNum,
                    )
            else:
                logger.warning("Pixiv热度榜异常:{}\r\n{}".format(res.status_code, data))
                return []

    def buildOriginalUrl(self, original_url: str, page: int) -> str:
        def changePage(matched):
            if page > 1:
                return "-%s" % (int(matched[0][-1]) + 1)
            else:
                return ""

        msg_changeHost = re.sub(r"//.*/", r"//pixiv.re/", original_url)
        return re.sub(r"_p\d+", changePage, msg_changeHost)

    def process_data(self, data) -> List[FinishSetuData]:
        dataList = []
        for d in data["illusts"]:
            if d["x_restrict"] == 2:  # R18G
                continue
            if self.config.level == 0 and d["x_restrict"] == 1:  # 未开启R18
                continue
            if d["page_count"] != 1:  # 多页画廊
                OriginalUrl = d["meta_pages"][0]["image_urls"]["original"]
            else:
                OriginalUrl = d["meta_single_page"]["original_image_url"]
            dataList.append(
                FinishSetuData(
                    title=d["title"],
                    picID=d["id"],
                    picWebUrl="www.pixiv.net/artworks/" + str(d["id"]),
                    page="0",
                    author=d["user"]["name"],
                    authorID=d["user"]["id"],
                    authorWebUrl="www.pixiv.net/users/" + str(d["user"]["id"]),
                    picOriginalUrl=OriginalUrl,
                    picLargeUrl=d["image_urls"]["large"].replace("_webp", ""),
                    picMediumUrl=d["image_urls"]["medium"].replace("_webp", ""),
                    picOriginalUrl_Msg=self.buildOriginalUrl(
                        OriginalUrl, d["page_count"]
                    ),
                    tags=",".join([i["name"] for i in d["tags"]]),
                )
            )
        return dataList

    async def main(self) -> List[FinishSetuData]:
        if self.config.toGetNum - self.config.doneNum <= 0:
            return []
        if len(self.config.tags) == 0:
            return []
        return await self.get()

你用这个替换OPQ-SetuBot\plugins\bot_Setu\APIS\pixiv.py, 再看看报错

test-black commented 3 years ago
Traceback (most recent call last):
  File "/home/pi/setubot/Q3/OPQ-SetuBot/bot.py", line 8, in <module>
    bot = AsyncBotoy(
  File "/home/pi/.local/lib/python3.9/site-packages/botoy/client.py", line 107, in __init__
    self.plugMgr.load_plugins()
  File "/home/pi/.local/lib/python3.9/site-packages/botoy/plugin.py", line 164, in load_plugins
    plugin.load()
  File "/home/pi/.local/lib/python3.9/site-packages/botoy/plugin.py", line 67, in load
    self.module = importlib.import_module(self.import_path)
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu_Cmd/__init__.py", line 4, in <module>
    from .command import CMD
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu_Cmd/command.py", line 7, in <module>
    from ..bot_Setu.database import getGroupConfig
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/__init__.py", line 8, in <module>
    from .setu import Setu
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/setu.py", line 14, in <module>
    from .APIS import Lolicon, Pixiv, Yuban
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/APIS/__init__.py", line 5, in <module>
    from .pixiv import Pixiv
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/APIS/pixiv.py", line 121, in <module>
    pixivToken.main()
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/APIS/pixiv.py", line 108, in main
    self.continue_refresh_token()
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/APIS/pixiv.py", line 75, in continue_refresh_token
    self.refresh_token()
  File "/home/pi/.local/lib/python3.9/site-packages/retrying.py", line 49, in wrapped_f
    return Retrying(*dargs, **dkw).call(f, *args, **kw)
  File "/home/pi/.local/lib/python3.9/site-packages/retrying.py", line 212, in call
    raise attempt.get()
  File "/home/pi/.local/lib/python3.9/site-packages/retrying.py", line 247, in get
    six.reraise(self.value[0], self.value[1], self.value[2])
  File "/home/pi/.local/lib/python3.9/site-packages/six.py", line 719, in reraise
    raise value
  File "/home/pi/.local/lib/python3.9/site-packages/retrying.py", line 200, in call
    attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
  File "/home/pi/setubot/Q3/OPQ-SetuBot/plugins/bot_Setu/APIS/pixiv.py", line 65, in refresh_token
    self.tokendata = self.Client.post(
  File "/home/pi/.local/lib/python3.9/site-packages/httpx/_client.py", line 1095, in post
    return self.request(
  File "/home/pi/.local/lib/python3.9/site-packages/httpx/_client.py", line 792, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
  File "/home/pi/.local/lib/python3.9/site-packages/httpx/_client.py", line 877, in send
    response = self._send_handling_auth(
  File "/home/pi/.local/lib/python3.9/site-packages/httpx/_client.py", line 905, in _send_handling_auth
    response = self._send_handling_redirects(
  File "/home/pi/.local/lib/python3.9/site-packages/httpx/_client.py", line 942, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/pi/.local/lib/python3.9/site-packages/httpx/_client.py", line 978, in _send_single_request
    response = transport.handle_request(request)
TypeError: handle_request() missing 1 required positional argument: 'url'
yuban10703 commented 3 years ago

刚刚更新了下包,复现了,我看看..

yuban10703 commented 3 years ago

pip install httpx==0.19.0

yuban10703 commented 3 years ago

先用旧版本httpx就正常了

test-black commented 3 years ago

问题解决