diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ff566f..f39c83e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### 新功能 -- 增加Parse Target功能 [@felinae98](https://github.com/felinae98) ([#72](https://github.com/felinae98/nonebot-bison/pull/72)) +- 增加 Parse Target 功能 [@felinae98](https://github.com/felinae98) ([#72](https://github.com/felinae98/nonebot-bison/pull/72)) ## v0.5.3 diff --git a/docs/usage/README.md b/docs/usage/README.md index 4caefa8..647da48 100644 --- a/docs/usage/README.md +++ b/docs/usage/README.md @@ -136,6 +136,8 @@ sidebar: auto 启用此功能时,可能会因为待推送图片过大/过多而导致文字消息与合并转发图片消息推送间隔过大(选择模式`1`时),请谨慎考虑开启。或者选择模式`2`,使图文消息一同合并转发(可能会使消息推送延迟过长) ::: +- `BISON_PROXY`: 使用的代理连接,形如`http://:`(可选) + ## 使用 ::: warning diff --git a/src/plugins/nonebot_bison/platform/arknights.py b/src/plugins/nonebot_bison/platform/arknights.py index 4a5c523..721a425 100644 --- a/src/plugins/nonebot_bison/platform/arknights.py +++ b/src/plugins/nonebot_bison/platform/arknights.py @@ -1,12 +1,12 @@ import json from typing import Any -import httpx from bs4 import BeautifulSoup as bs from nonebot.plugin import require from ..post import Post from ..types import Category, RawPost, Target +from ..utils import http_client from .platform import CategoryNotSupport, NewMessage, StatusChange @@ -26,7 +26,7 @@ class Arknights(NewMessage): return "明日方舟游戏信息" async def get_sub_list(self, _) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: raw_data = await client.get( "https://ak-conf.hypergryph.com/config/prod/announce_meta/IOS/announcement.meta.json" ) @@ -44,7 +44,7 @@ class Arknights(NewMessage): async def parse(self, raw_post: RawPost) -> Post: announce_url = raw_post["webUrl"] text = "" - async with httpx.AsyncClient() as client: + async with http_client() as client: raw_html = await client.get(announce_url) soup = bs(raw_html.text, "html.parser") pics = [] @@ -99,7 +99,7 @@ class AkVersion(StatusChange): return "明日方舟游戏信息" async def get_status(self, _): - async with httpx.AsyncClient() as client: + async with http_client() as client: res_ver = await client.get( "https://ak-conf.hypergryph.com/config/prod/official/IOS/version" ) @@ -155,7 +155,7 @@ class MonsterSiren(NewMessage): return "明日方舟游戏信息" async def get_sub_list(self, _) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: raw_data = await client.get("https://monster-siren.hypergryph.com/api/news") return raw_data.json()["data"]["list"] @@ -170,7 +170,7 @@ class MonsterSiren(NewMessage): async def parse(self, raw_post: RawPost) -> Post: url = f'https://monster-siren.hypergryph.com/info/{raw_post["cid"]}' - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get( f'https://monster-siren.hypergryph.com/api/news/{raw_post["cid"]}' ) @@ -207,7 +207,7 @@ class TerraHistoricusComic(NewMessage): return "明日方舟游戏信息" async def get_sub_list(self, _) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: raw_data = await client.get( "https://terra-historicus.hypergryph.com/api/recentUpdate" ) diff --git a/src/plugins/nonebot_bison/platform/bilibili.py b/src/plugins/nonebot_bison/platform/bilibili.py index 9b26684..c880a46 100644 --- a/src/plugins/nonebot_bison/platform/bilibili.py +++ b/src/plugins/nonebot_bison/platform/bilibili.py @@ -2,11 +2,10 @@ import json import re from typing import Any, Optional -import httpx - from ..post import Post from ..types import Category, RawPost, Tag, Target -from .platform import CategoryNotSupport, NewMessage, Platform +from ..utils import http_client +from .platform import CategoryNotSupport, NewMessage class Bilibili(NewMessage): @@ -30,7 +29,7 @@ class Bilibili(NewMessage): parse_target_promot = "请输入用户主页的链接" async def get_target_name(self, target: Target) -> Optional[str]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get( "https://api.bilibili.com/x/space/acc/info", params={"mid": target} ) @@ -47,10 +46,10 @@ class Bilibili(NewMessage): ): return Target(match.group(1)) else: - raise Platform.ParseTargetException() + raise self.ParseTargetException() async def get_sub_list(self, target: Target) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: params = {"host_uid": target, "offset": 0, "need_top": 0} res = await client.get( "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history", diff --git a/src/plugins/nonebot_bison/platform/ff14.py b/src/plugins/nonebot_bison/platform/ff14.py index 68be182..0cbc92e 100644 --- a/src/plugins/nonebot_bison/platform/ff14.py +++ b/src/plugins/nonebot_bison/platform/ff14.py @@ -1,9 +1,8 @@ from typing import Any -import httpx - from ..post import Post from ..types import RawPost, Target +from ..utils import http_client from .platform import NewMessage @@ -23,7 +22,7 @@ class FF14(NewMessage): return "最终幻想XIV官方公告" async def get_sub_list(self, _) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: raw_data = await client.get( "https://ff.web.sdo.com/inc/newdata.ashx?url=List?gameCode=ff&category=5309,5310,5311,5312,5313&pageIndex=0&pageSize=5" ) diff --git a/src/plugins/nonebot_bison/platform/ncm_artist.py b/src/plugins/nonebot_bison/platform/ncm_artist.py index 9895dde..00d329e 100644 --- a/src/plugins/nonebot_bison/platform/ncm_artist.py +++ b/src/plugins/nonebot_bison/platform/ncm_artist.py @@ -1,10 +1,9 @@ import re from typing import Any, Optional -import httpx - from ..post import Post from ..types import RawPost, Target +from ..utils import http_client from .platform import NewMessage @@ -22,7 +21,7 @@ class NcmArtist(NewMessage): parse_target_promot = "请输入歌手主页(包含数字ID)的链接" async def get_target_name(self, target: Target) -> Optional[str]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get( "https://music.163.com/api/artist/albums/{}".format(target), headers={"Referer": "https://music.163.com/"}, @@ -43,7 +42,7 @@ class NcmArtist(NewMessage): raise self.ParseTargetException() async def get_sub_list(self, target: Target) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get( "https://music.163.com/api/artist/albums/{}".format(target), headers={"Referer": "https://music.163.com/"}, diff --git a/src/plugins/nonebot_bison/platform/ncm_radio.py b/src/plugins/nonebot_bison/platform/ncm_radio.py index 973cc1b..14b439e 100644 --- a/src/plugins/nonebot_bison/platform/ncm_radio.py +++ b/src/plugins/nonebot_bison/platform/ncm_radio.py @@ -1,10 +1,9 @@ import re from typing import Any, Optional -import httpx - from ..post import Post from ..types import RawPost, Target +from ..utils import http_client from .platform import NewMessage @@ -22,7 +21,7 @@ class NcmRadio(NewMessage): parse_target_promot = "请输入主播电台主页(包含数字ID)的链接" async def get_target_name(self, target: Target) -> Optional[str]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.post( "http://music.163.com/api/dj/program/byradio", headers={"Referer": "https://music.163.com/"}, @@ -44,7 +43,7 @@ class NcmRadio(NewMessage): raise self.ParseTargetException() async def get_sub_list(self, target: Target) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.post( "http://music.163.com/api/dj/program/byradio", headers={"Referer": "https://music.163.com/"}, diff --git a/src/plugins/nonebot_bison/platform/rss.py b/src/plugins/nonebot_bison/platform/rss.py index 330d93d..ed09e8a 100644 --- a/src/plugins/nonebot_bison/platform/rss.py +++ b/src/plugins/nonebot_bison/platform/rss.py @@ -2,11 +2,11 @@ import calendar from typing import Any, Optional import feedparser -import httpx from bs4 import BeautifulSoup as bs from ..post import Post from ..types import RawPost, Target +from ..utils import http_client from .platform import NewMessage @@ -23,7 +23,7 @@ class Rss(NewMessage): has_target = True async def get_target_name(self, target: Target) -> Optional[str]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get(target, timeout=10.0) feed = feedparser.parse(res.text) return feed["feed"]["title"] @@ -35,7 +35,7 @@ class Rss(NewMessage): return post.id async def get_sub_list(self, target: Target) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get(target, timeout=10.0) feed = feedparser.parse(res) entries = feed.entries diff --git a/src/plugins/nonebot_bison/platform/weibo.py b/src/plugins/nonebot_bison/platform/weibo.py index f482d80..f7973a3 100644 --- a/src/plugins/nonebot_bison/platform/weibo.py +++ b/src/plugins/nonebot_bison/platform/weibo.py @@ -3,13 +3,13 @@ import re from datetime import datetime from typing import Any, Optional -import httpx from bs4 import BeautifulSoup as bs from nonebot.log import logger from ..post import Post from ..types import * -from .platform import NewMessage, Platform +from ..utils import http_client +from .platform import NewMessage class Weibo(NewMessage): @@ -31,7 +31,7 @@ class Weibo(NewMessage): parse_target_promot = "请输入用户主页(包含数字UID)的链接" async def get_target_name(self, target: Target) -> Optional[str]: - async with httpx.AsyncClient() as client: + async with http_client() as client: param = {"containerid": "100505" + target} res = await client.get( "https://m.weibo.cn/api/container/getIndex", params=param @@ -49,10 +49,10 @@ class Weibo(NewMessage): # 都2202年了应该不会有http了吧,不过还是防一手 return Target(match.group(1)) else: - raise Platform.ParseTargetException() + raise self.ParseTargetException() async def get_sub_list(self, target: Target) -> list[RawPost]: - async with httpx.AsyncClient() as client: + async with http_client() as client: params = {"containerid": "107603" + target} res = await client.get( "https://m.weibo.cn/api/container/getIndex?", params=params, timeout=4.0 @@ -138,7 +138,7 @@ class Weibo(NewMessage): retweeted = True pic_num = info["retweeted_status"]["pic_num"] if retweeted else info["pic_num"] if info["isLongText"] or pic_num > 9: - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get( "https://m.weibo.cn/detail/{}".format(info["mid"]), headers=header ) diff --git a/src/plugins/nonebot_bison/plugin_config.py b/src/plugins/nonebot_bison/plugin_config.py index c6b5c71..dfd43f3 100644 --- a/src/plugins/nonebot_bison/plugin_config.py +++ b/src/plugins/nonebot_bison/plugin_config.py @@ -1,3 +1,5 @@ +from typing import Optional + import nonebot from pydantic import BaseSettings @@ -15,6 +17,7 @@ class PlugConfig(BaseSettings): bison_use_pic_merge: int = 0 # 多图片时启用图片合并转发(仅限群) # 0:不启用;1:首条消息单独发送,剩余照片合并转发;2以及以上:所有消息全部合并转发 bison_resend_times: int = 0 + bison_proxy: Optional[str] class Config: extra = "ignore" diff --git a/src/plugins/nonebot_bison/post.py b/src/plugins/nonebot_bison/post.py index ceca521..869db75 100644 --- a/src/plugins/nonebot_bison/post.py +++ b/src/plugins/nonebot_bison/post.py @@ -3,13 +3,12 @@ from functools import reduce from io import BytesIO from typing import Optional, Union -import httpx from nonebot.adapters.onebot.v11.message import Message, MessageSegment from nonebot.log import logger from PIL import Image from .plugin_config import plugin_config -from .utils import parse_text +from .utils import http_client, parse_text @dataclass @@ -34,7 +33,7 @@ class Post: async def _pic_url_to_image(self, data: Union[str, bytes]) -> Image.Image: pic_buffer = BytesIO() if isinstance(data, str): - async with httpx.AsyncClient() as client: + async with http_client() as client: res = await client.get(data) pic_buffer.write(res.content) else: diff --git a/src/plugins/nonebot_bison/utils.py b/src/plugins/nonebot_bison/utils/__init__.py similarity index 94% rename from src/plugins/nonebot_bison/utils.py rename to src/plugins/nonebot_bison/utils/__init__.py index 8050d59..73c8baa 100644 --- a/src/plugins/nonebot_bison/utils.py +++ b/src/plugins/nonebot_bison/utils/__init__.py @@ -8,7 +8,10 @@ from nonebot.adapters.onebot.v11.message import MessageSegment from nonebot.log import default_format, logger from nonebot.plugin import require -from .plugin_config import plugin_config +from ..plugin_config import plugin_config +from .http import http_client + +__all__ = ["http_client", "Singleton", "parse_text", "html_to_text"] class Singleton(type): diff --git a/src/plugins/nonebot_bison/utils/http.py b/src/plugins/nonebot_bison/utils/http.py new file mode 100644 index 0000000..f46af30 --- /dev/null +++ b/src/plugins/nonebot_bison/utils/http.py @@ -0,0 +1,12 @@ +import functools + +import httpx + +from ..plugin_config import plugin_config + +if plugin_config.bison_proxy: + http_client = functools.partial( + httpx.AsyncClient, proxies=plugin_config.bison_proxy + ) +else: + http_client = httpx.AsyncClient diff --git a/tests/test_proxy.py b/tests/test_proxy.py new file mode 100644 index 0000000..ec4a22e --- /dev/null +++ b/tests/test_proxy.py @@ -0,0 +1,20 @@ +import pytest +from nonebug import App +from nonebug.fixture import nonebug_init + + +async def test_without_proxy(app: App): + from nonebot_bison.utils import http_client + + c = http_client() + assert not c._mounts + + +@pytest.mark.parametrize( + "nonebug_init", [{"bison_proxy": "http://example.com"}], indirect=True +) +async def test_with_proxy(app: App): + from nonebot_bison.utils import http_client + + c = http_client() + assert c._mounts