diff --git a/Dockerfile b/Dockerfile index e16d20e..efc62e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:alpine +FROM python:3.8-alpine RUN apk update && apk add --no-cache tzdata alpine-sdk libxml2 libxslt-dev COPY requirements.txt /requirements.txt diff --git a/README.md b/README.md index 80a2bed..5e200f7 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,21 @@ # YYeTsBot + 人人影视bot,[戳我使用](https://t.me/yyets_bot) 此机器人长期维护,如果遇到问题可以发送报告给我。 # 使用说明 -直接发送想要看的剧集名称就可以了,可选分享网页或者链接(ed2k和磁力链接) +直接发送想要看的剧集名称就可以了,可选分享网页或者链接(ed2k和磁力链接)。 + +**由于译名的不同,建议输入部分译名,然后从列表中进行选择。** # commands + ``` start - 开始使用 help - 帮助 credits - 致谢 ping - 运行状态 ``` + # 截图 ![](assets/1.jpg) @@ -18,40 +23,54 @@ ping - 运行状态 ![](assets/2.jpg) # 部署方法 -## 使用docker -```bash -docker run -d --restart=always -e TOKEN="TOKEN" bennythink/yyetsbot -``` -根据情况,还可以 `-e USERNAME="1234"`,USERNAME和PASSWORD是在人人影视的有效的用户名和密码 -也可以自己构建docker image: -```bash -docker build -t yyetsbot . -``` +## 使用docker + +参见 [这里](https://github.com/tgbot-collection/BotsRunner) ## 常规方式 + ### 1. 环境 -推荐使用Python 3.6+ + +推荐使用Python 3.6+,需要安装redis `apt install redis`,根据个人情况可以使用virtualenv + ```bash pip install -r requirements.py ``` + ### 2. 配置TOKEN -修改`config.py`,把TOKEN修改为你的bot token, USERNAME和PASSWORD是在人人影视的有效的用户名和密码 + +修改`config.py`,根据需求修改如下配置项 + +* TOKEN:bot token +* USERNAME:USERNAME和PASSWORD是在人人影视的有效的用户名和密码 +* PASSWORD :USERNAME和PASSWORD是在人人影视的有效的用户名和密码 +* PROXY :是否需要使用代理 格式 `socks5://userproxy:password@proxy_address:port` +* MAINTAINER:维护者的Telegram UserID +* REDIS:redis的地址,一般为localhost, 也可以使用环境变量,如 `export TOKEN="1234"` ### 3. 运行 + ```bash python /path/to/YYeTsBot/bot.py ``` + ### 4. systemd 单元文件 + 参考 `yyets.service` +# Help + +- [ ] test case... + # Credits + * [人人影视](http://www.zmz2019.com/) * [追新番](http://www.zhuixinfan.com/main.php) -* [FIX字幕侠](http://www.zimuxia.cn/) * [磁力下载站](http://oabt005.com/home.html) # License + [MIT](LICENSE) diff --git a/bot.py b/bot.py index e941e21..29da488 100644 --- a/bot.py +++ b/bot.py @@ -9,6 +9,8 @@ import time import re import os import logging +import json +import tempfile from urllib.parse import quote_plus @@ -17,7 +19,7 @@ from telebot import types, apihelper from tgbot_ping import get_runtime from html_request import get_search_html, analyse_search_html, get_detail_page -from utils import save_dump, upsert, get +from utils import save_dump, save_to_cache, get_from_cache from config import PROXY, TOKEN, SEARCH_URL, MAINTAINER logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(filename)s [%(levelname)s]: %(message)s') @@ -111,14 +113,13 @@ def send_search(message): bot.send_sticker(message.chat.id, sti) return - logging.info('Receiving message about %s from user %s(%s)', name, message.chat.username, - message.chat.id) + logging.info('Receiving message about %s from user %s(%s)', name, message.chat.username, message.chat.id) html = get_search_html(name) result = analyse_search_html(html) markup = types.InlineKeyboardMarkup() for url, detail in result.items(): - btn = types.InlineKeyboardButton(detail['name'], callback_data=url) + btn = types.InlineKeyboardButton(detail['name'], callback_data="choose%s" % url) markup.add(btn) if result: @@ -145,59 +146,46 @@ def send_search(message): save_dump(content) -@bot.callback_query_handler(func=lambda call: 'resource' in call.data) +@bot.callback_query_handler(func=lambda call: re.findall(r"choose(\S*)", call.data)) def choose_link(call): bot.send_chat_action(call.message.chat.id, 'typing') - resource_url = call.data - link = get_detail_page(resource_url) - upsert(call.id, link) + # call.data is url, http://www.rrys2020.com/resource/36588 + resource_url = re.findall(r"choose(\S*)", call.data)[0] + + link = get_from_cache(resource_url) + if not link: + link = get_detail_page(resource_url) + save_to_cache(resource_url, link) + markup = types.InlineKeyboardMarkup() - btn1 = types.InlineKeyboardButton("分享页面", callback_data="share%s" % call.id) - btn2 = types.InlineKeyboardButton("继续点按钮", callback_data="select%s" % call.id) + btn1 = types.InlineKeyboardButton("分享页面", callback_data="share%s" % resource_url) + btn2 = types.InlineKeyboardButton("我全都要", callback_data="all%s" % resource_url) markup.add(btn1, btn2) - bot.send_message(call.message.chat.id, "想要分享页面,还是继续点击按钮", reply_markup=markup) + bot.send_message(call.message.chat.id, "想要分享页面,还是我全都要?", reply_markup=markup) -@bot.callback_query_handler(func=lambda call: re.findall(r"share(\d*)", call.data)) +@bot.callback_query_handler(func=lambda call: re.findall(r"share(\S*)", call.data)) def share_page(call): bot.send_chat_action(call.message.chat.id, 'typing') - cid = re.findall(r"share(\d*)", call.data)[0] - result = get(cid) + resource_url = re.findall(r"share(\S*)", call.data)[0] + result = get_from_cache(resource_url) bot.send_message(call.message.chat.id, result['share']) -@bot.callback_query_handler(func=lambda call: re.findall(r"select(\d*)", call.data)) -def select_episode(call): +@bot.callback_query_handler(func=lambda call: re.findall(r"all(\S*)", call.data)) +def all_episode(call): + # just send a file bot.send_chat_action(call.message.chat.id, 'typing') - cid = re.findall(r"select(\d*)", call.data)[0] - result = get(cid) - markup = types.InlineKeyboardMarkup() + resource_url = re.findall(r"all(\S*)", call.data)[0] + result = get_from_cache(resource_url) - if not result['rss']: - btn = types.InlineKeyboardButton("点击打开分享网站", url=result['share']) - markup.add(btn) - bot.send_message(call.message.chat.id, "哎呀呀,这是个电影,恐怕没得选吧!", reply_markup=markup) - else: - for guid, detail in result['rss'].items(): - btn = types.InlineKeyboardButton(detail['title'], callback_data=f"cid{cid}guid{guid}") - markup.add(btn) - bot.send_message(call.message.chat.id, "选一集吧!", reply_markup=markup) + with tempfile.NamedTemporaryFile(mode='wb+', prefix=result["cnname"], suffix=".txt") as tmp: + bytes_data = json.dumps(result["all"], ensure_ascii=False, indent=4).encode('u8') + tmp.write(bytes_data) - -@bot.callback_query_handler(func=lambda call: re.findall(r"cid(\d*)guid(.*)", call.data)) -def send_link(call): - bot.send_chat_action(call.message.chat.id, 'typing') - data = re.findall(r"cid(\d*)guid(.*)", call.data)[0] - cid, guid = data[0], data[1] - links = get(cid)['rss'][guid] - ed2k, magnet, pan = "`{}`".format(links['ed2k']), "`{}`".format(links['magnet']), "`{}`".format(links['pan']) - bot.send_message(call.message.chat.id, f"{links['title']}的下载资源如下") - if ed2k != "``": - bot.send_message(call.message.chat.id, ed2k, parse_mode='markdown') - if magnet != "``": - bot.send_message(call.message.chat.id, magnet, parse_mode='markdown') - if pan != "``": - bot.send_message(call.message.chat.id, pan, parse_mode='markdown') + bot.send_chat_action(call.message.chat.id, 'upload_document') + with open(tmp.name, "rb") as f: + bot.send_document(call.message.chat.id, f) @bot.callback_query_handler(func=lambda call: call.data == 'fix') diff --git a/config.py b/config.py index cbbd5d8..fe71d5f 100644 --- a/config.py +++ b/config.py @@ -9,14 +9,19 @@ import os BASE_URL = "http://www.rrys2020.com" LOGIN_URL = "http://www.rrys2020.com/user/login" GET_USER = "http://www.rrys2020.com/user/login/getCurUserTopInfo" +# rss is unavailable as of 2021.01.10 RSS_URL = "http://rss.rrys.tv/rss/feed/{id}" RESOURCE_SCORE = "http://www.rrys2020.com/resource/getScore" # post rid=38000 SEARCH_URL = "http://www.rrys2020.com/search?keyword={kw}&type=resource" AJAX_LOGIN = "http://www.rrys2020.com/User/Login/ajaxLogin" SHARE_URL = "http://www.rrys2020.com/resource/ushare" SHARE_WEB = "http://got002.com/resource.html?code={code}" +# http://got002.com/api/v1/static/resource/detail?code=9YxN91 +SHARE_API = "http://got002.com/api/v1/static/resource/detail?code={code}" + TOKEN = os.environ.get("TOKEN") or "TOKEN" USERNAME = os.environ.get("USERNAME") or "USERNAME" PASSWORD = os.environ.get("PASSWORD") or "password" PROXY = os.environ.get("PROXY") MAINTAINER = os.environ.get("MAINTAINER") +REDIS = os.environ.get("REDIS") or "redis" diff --git a/html_request.py b/html_request.py index b992ae7..7a8cc57 100644 --- a/html_request.py +++ b/html_request.py @@ -10,7 +10,7 @@ import requests import feedparser from bs4 import BeautifulSoup -from config import SEARCH_URL, GET_USER, RSS_URL, BASE_URL, SHARE_WEB, SHARE_URL, RESOURCE_SCORE +from config import SEARCH_URL, GET_USER, RSS_URL, BASE_URL, SHARE_WEB, SHARE_URL, RESOURCE_SCORE, SHARE_API from utils import load_cookies, cookie_file, login logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(filename)s [%(levelname)s]: %(message)s') @@ -32,15 +32,22 @@ def get_search_html(kw: str) -> str: return r.text -def get_detail_page(url: str): +def get_detail_page(url: str) -> dict: + logging.info("loading detail page %s", url) + share_link, api_res = analysis_share_page(url) + cnname = api_res["data"]["info"]["cnname"] + logging.info("Share page complete for %s", cnname) + logging.info("Getting rss...") rss_url = RSS_URL.format(id=url.split("/")[-1]) rss_result = analyse_rss(rss_url) + logging.info("RSS complete...") - logging.info("loading detail page %s", url) - share_link = analysis_share_page(url) + # get name from here... + if not rss_result: + rss_result = api_res - return {"rss": rss_result, "share": share_link} + return {"all": rss_result, "share": share_link, "cnname": cnname} def analyse_search_html(html: str) -> dict: @@ -70,7 +77,7 @@ def analyse_rss(feed_url: str) -> dict: return result -def analysis_share_page(detail_url: str) -> str: +def analysis_share_page(detail_url: str) -> (str, dict): rid = detail_url.split('/')[-1] logging.info("rid is %s", rid) @@ -79,7 +86,10 @@ def analysis_share_page(detail_url: str) -> str: logging.info("Share code is %s", share_code) share_url = SHARE_WEB.format(code=share_code) logging.info("Share url %s", share_url) - return share_url + + # get api response + api_response = s.get(SHARE_API.format(code=share_code)).json() + return share_url, api_response def get_score(rid: str) -> float: diff --git a/requirements.txt b/requirements.txt index 1153734..cb1a90b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ beautifulsoup4 lxml feedparser pysocks -tgbot-ping \ No newline at end of file +tgbot-ping +redis \ No newline at end of file diff --git a/utils.py b/utils.py index 9f66d6c..318ed11 100644 --- a/utils.py +++ b/utils.py @@ -4,36 +4,38 @@ __author__ = 'Benny ' -import dbm import os import sys import pickle import json import logging import requests +import redis -from config import AJAX_LOGIN, USERNAME, PASSWORD +from config import AJAX_LOGIN, USERNAME, PASSWORD, REDIS + +r = redis.StrictRedis(host=REDIS, decode_responses=True) -db_path = os.path.join(os.path.dirname(__file__), 'data', 'yyets.dbm') -db = dbm.open(db_path, 'c') cookie_file = os.path.join(os.path.dirname(__file__), 'data', 'cookies.dump') -def batch_upsert(data: dict) -> None: - for k in data: - upsert(k, data[k]) +def save_to_cache(url: str, value: dict) -> None: + data = json.dumps(value, ensure_ascii=False) + r.set(url, data, ex=3600 * 12) -def upsert(key: str, value: dict) -> None: - db[key] = json.dumps(value, ensure_ascii=False) +def get_from_cache(url: str) -> dict: + logging.info("Reading data from cache %s", url) + from html_request import get_detail_page - -def get(key: str) -> dict: - return json.loads(db.get(key, '{}')) - - -def delete(key: str) -> None: - del db[key] + data = r.get(url) + if data: + logging.info("cache hit") + return json.loads(data) + else: + logging.info("cache miss") + save_to_cache(url, get_detail_page(url)) + return get_from_cache(url) def save_dump(err):