parent
15f5f1f6fc
commit
a5f06fc08c
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"userid": "foobar123",
|
||||||
|
"email": "114514@1919.com",
|
||||||
|
"password": "810810",
|
||||||
|
|
||||||
|
"callback_url": "http://localhost:1145",
|
||||||
|
"proxy": "socks5://localhost:7890",
|
||||||
|
|
||||||
|
"slow_interval": 600,
|
||||||
|
"slow_hours": [1, 2, 3, 4, 5, 6],
|
||||||
|
|
||||||
|
"task_list": [
|
||||||
|
{
|
||||||
|
"name": "lovelive",
|
||||||
|
"qq": "1145141919",
|
||||||
|
"url": "https://x.com/i/lists/1873733459036000393",
|
||||||
|
"interval": 42,
|
||||||
|
"filter": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"userid": "foobar123",
|
|
||||||
"email": "114514@1919.com",
|
|
||||||
"password": "810810",
|
|
||||||
"callback_url": "http://localhost:114514/xxx",
|
|
||||||
"proxy": "socks5://localhost:7890",
|
|
||||||
"check_interval": 42,
|
|
||||||
"check_interval_slow": 600,
|
|
||||||
"slow_hours": [0, 1, 2, 3, 4, 5, 6],
|
|
||||||
"check_list": {
|
|
||||||
"1145141919": {
|
|
||||||
"url": "https://x.com/i/lists/1873731145141919810",
|
|
||||||
"interval": 42,
|
|
||||||
"filter": [
|
|
||||||
"only_image",
|
|
||||||
"only_origin"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,76 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
from apscheduler.triggers.interval import IntervalTrigger
|
||||||
|
from loguru import logger
|
||||||
|
from quart import Quart, jsonify, request
|
||||||
|
|
||||||
|
from src.twitter import task_handler
|
||||||
|
|
||||||
|
app = Quart(__name__)
|
||||||
|
scheduler = AsyncIOScheduler()
|
||||||
|
|
||||||
|
logger.remove()
|
||||||
|
logger.add(sys.stdout, level="INFO",
|
||||||
|
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level}</level> | <cyan>{name}:{function}:{line}</cyan> - <level>{message}</level>")
|
||||||
|
class InterceptHandler(logging.Handler):
|
||||||
|
def emit(self, record):
|
||||||
|
try:
|
||||||
|
level = logger.level(record.levelname).name
|
||||||
|
except ValueError:
|
||||||
|
level = record.levelno
|
||||||
|
logger.opt(depth=6, exception=record.exc_info).log(level, record.getMessage())
|
||||||
|
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
|
||||||
|
logging.getLogger("apscheduler").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||||
|
#logger.disable("hypercorn")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.before_serving
|
||||||
|
async def startup():
|
||||||
|
with open("./config/config.json", "r", encoding="utf-8") as f: config = json.load(f)
|
||||||
|
for task in config.get("task_list", []):
|
||||||
|
logger.info(f"Added task {task['name']} ({task['interval']}s): {task['url']}")
|
||||||
|
await add_task(task)
|
||||||
|
scheduler.start()
|
||||||
|
logger.info("Scheduler started")
|
||||||
|
|
||||||
|
@app.after_serving
|
||||||
|
async def shutdown():
|
||||||
|
scheduler.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def add_task(task_args):
|
||||||
|
if not "url" in task_args: raise ValueError("Task must have a URL")
|
||||||
|
task_name = task_args.get("name", task_args["url"])
|
||||||
|
interval = task_args.get("interval", 42)
|
||||||
|
task_args["filter"] = task_args.get("filter", [])
|
||||||
|
|
||||||
|
if scheduler.get_job(task_name):
|
||||||
|
scheduler.remove_job(task_name)
|
||||||
|
trigger = IntervalTrigger(seconds=interval)
|
||||||
|
scheduler.add_job(task_handler, trigger, args=[task_args], id=task_name, replace_existing=True)
|
||||||
|
|
||||||
|
async def list_task():
|
||||||
|
jobs = scheduler.get_jobs()
|
||||||
|
return [{"id": job.id, "next_run": str(job.next_run_time)} for job in jobs]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from hypercorn.asyncio import serve
|
||||||
|
from hypercorn.config import Config
|
||||||
|
|
||||||
|
config = Config()
|
||||||
|
config.bind = ["127.0.0.1:7217"]
|
||||||
|
config.accesslog = logging.getLogger('hypercorn.access')
|
||||||
|
config.errorlog = logging.getLogger('hypercorn.error')
|
||||||
|
asyncio.run(serve(app, config))
|
||||||
|
#app.run(port=7217)
|
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
def filter_tweets(tweets, filter_list):
|
||||||
|
|
||||||
|
if "only_image" in filter_list:
|
||||||
|
tweets = [t for t in tweets if t["media"]]
|
||||||
|
|
||||||
|
if "only_origin" in filter_list:
|
||||||
|
tweets = [t for t in tweets if (not t["quoted"]) and (not t["retweeted"])]
|
||||||
|
|
||||||
|
return tweets
|
@ -0,0 +1,68 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pprint
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from .twi_api import get_list, login
|
||||||
|
from .twi_filter import filter_tweets
|
||||||
|
from .twi_parser import parse_timeline
|
||||||
|
|
||||||
|
LATEST_TWEET_ID_DICT = {}
|
||||||
|
LATEST_TWEET_TS_DICT = {}
|
||||||
|
def check_new_tweets(tweets, list_id):
|
||||||
|
if not tweets: return []
|
||||||
|
new_tweets = []
|
||||||
|
|
||||||
|
if list_id in LATEST_TWEET_ID_DICT:
|
||||||
|
for tweet in tweets:
|
||||||
|
if tweet["rest_id"] == LATEST_TWEET_ID_DICT[list_id]: break
|
||||||
|
if tweet["timestamp"] < LATEST_TWEET_TS_DICT[list_id]: break
|
||||||
|
# if time.time() - tweet["timestamp"] > 1200: break
|
||||||
|
new_tweets.append(tweet)
|
||||||
|
|
||||||
|
LATEST_TWEET_ID_DICT[list_id] = tweets[0]["rest_id"]
|
||||||
|
LATEST_TWEET_TS_DICT[list_id] = tweets[0]["timestamp"]
|
||||||
|
return new_tweets
|
||||||
|
|
||||||
|
|
||||||
|
LATEST_CHECK_TIME = defaultdict(float)
|
||||||
|
async def task_handler(args):
|
||||||
|
with open("./config/config.json", "r", encoding="utf-8") as f: config = json.load(f)
|
||||||
|
slow_interval = config.get("slow_interval", 600)
|
||||||
|
slow_hours = config.get("slow_hours", [0, 1, 2, 3, 4, 5, 6])
|
||||||
|
if datetime.now().hour in slow_hours:
|
||||||
|
if time.time() - LATEST_CHECK_TIME[args["name"]] < slow_interval:
|
||||||
|
logger.info(f"Task {args['name']} skipped")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"Task args: {args}")
|
||||||
|
if not os.path.exists("./config/headers.json"):
|
||||||
|
logger.error("Headers not found, logging in to Twitter")
|
||||||
|
login()
|
||||||
|
LATEST_CHECK_TIME[args["name"]] = time.time()
|
||||||
|
|
||||||
|
list_id = int(args["url"].split("/")[-1])
|
||||||
|
data = get_list(list_id)
|
||||||
|
if data:
|
||||||
|
tweets = parse_timeline(data)
|
||||||
|
new_tweets = check_new_tweets(tweets, list_id)
|
||||||
|
new_tweets = filter_tweets(new_tweets, args["filter"])
|
||||||
|
if new_tweets:
|
||||||
|
json_data = { args["qq"]: new_tweets }
|
||||||
|
logger.info("\n" + pprint.pformat(json_data))
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.post(config["callback_url"],
|
||||||
|
json=json_data, timeout=10)
|
||||||
|
logger.info(resp.content)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
|
logger.info(f"Task {args['name']} finished")
|
||||||
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
|||||||
import json
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
from collections import defaultdict
|
|
||||||
from datetime import datetime
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
from twi_api import get_list, login
|
|
||||||
from twi_parser import parse_timeline
|
|
||||||
|
|
||||||
LATEST_TWEET_ID_DICT = {}
|
|
||||||
LATEST_TWEET_TS_DICT = {}
|
|
||||||
def check_new_tweets(tweets, url):
|
|
||||||
global LATEST_TWEET_ID_DICT
|
|
||||||
|
|
||||||
new_tweets = []
|
|
||||||
if url in LATEST_TWEET_ID_DICT:
|
|
||||||
for tweet in tweets:
|
|
||||||
if tweet["rest_id"] == LATEST_TWEET_ID_DICT[url]:
|
|
||||||
break
|
|
||||||
if tweet["timestamp"] < LATEST_TWEET_TS_DICT[url]:
|
|
||||||
break
|
|
||||||
if time.time() - tweet["timestamp"] > 1200:
|
|
||||||
break
|
|
||||||
new_tweets.append(tweet)
|
|
||||||
|
|
||||||
LATEST_TWEET_ID_DICT[url] = tweets[0]["rest_id"]
|
|
||||||
LATEST_TWEET_TS_DICT[url] = tweets[0]["timestamp"]
|
|
||||||
return new_tweets
|
|
||||||
|
|
||||||
def filter_tweets(tweets, filter_list):
|
|
||||||
|
|
||||||
if "only_image" in filter_list:
|
|
||||||
tweets = [t for t in tweets if t["media"]]
|
|
||||||
|
|
||||||
if "only_origin" in filter_list:
|
|
||||||
tweets = [t for t in tweets if (not t["quoted"]) and (not t["retweeted"])]
|
|
||||||
|
|
||||||
return tweets
|
|
||||||
|
|
||||||
def check_timeline(config):
|
|
||||||
list_id = int(config["url"].split("/")[-1])
|
|
||||||
data = get_list(list_id)
|
|
||||||
if data:
|
|
||||||
tweets = parse_timeline(data)
|
|
||||||
new_tweets = check_new_tweets(tweets, config["url"])
|
|
||||||
return filter_tweets(new_tweets, config["filter"])
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
with open("config.json", 'r') as f:
|
|
||||||
config = json.load(f)
|
|
||||||
check_list = config.get("check_list", [])
|
|
||||||
check_interval = config.get("check_interval", 42)
|
|
||||||
check_interval_slow = config.get("check_interval_slow", 600)
|
|
||||||
slow_hours = config.get("slow_hours", [0, 1, 2, 3, 4, 5, 6])
|
|
||||||
last_check_time = defaultdict(float)
|
|
||||||
|
|
||||||
while 1:
|
|
||||||
if not os.path.exists("headers.json"):
|
|
||||||
login()
|
|
||||||
|
|
||||||
json_data = {}
|
|
||||||
for group_id, group_config in check_list.items():
|
|
||||||
group_interval = group_config.get("interval", check_interval)
|
|
||||||
|
|
||||||
if time.time() - last_check_time[group_id] > group_interval:
|
|
||||||
new_tweets = check_timeline(group_config)
|
|
||||||
if new_tweets:
|
|
||||||
json_data[group_id] = new_tweets
|
|
||||||
last_check_time[group_id] = time.time()
|
|
||||||
|
|
||||||
if json_data:
|
|
||||||
pprint(json_data)
|
|
||||||
try:
|
|
||||||
resp = requests.post(config["callback_url"],
|
|
||||||
json=json_data, timeout=10)
|
|
||||||
logger.info(resp.content)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(str(e))
|
|
||||||
|
|
||||||
if datetime.now().hour in slow_hours:
|
|
||||||
time.sleep(check_interval_slow)
|
|
||||||
else:
|
|
||||||
time.sleep(check_interval)
|
|
||||||
|
|
||||||
# with open("lovelive.json", 'r', encoding="utf8") as f: pprint(parse_timeline(json.load(f)))
|
|
Loading…
Reference in new issue