优化脚本

This commit is contained in:
weiqun
2025-02-13 16:25:44 +08:00
parent d008f0b2f6
commit e0eb4e7f6b
4 changed files with 368 additions and 196 deletions

View File

@@ -14,6 +14,7 @@ from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad, unpad from Crypto.Util.Padding import pad, unpad
from aiohttp import ClientSession, TCPConnector from aiohttp import ClientSession, TCPConnector
import httpx import httpx
import logging
diffValue = 2 diffValue = 2
filename='Cache.js' filename='Cache.js'
@@ -77,34 +78,49 @@ custom_client = httpx.Client(
) )
) )
# 添加日志记录
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def initCookie(getUrl='https://wapact.189.cn:9001/gateway/standQuery/detailNew/exchange'): def initCookie(getUrl='https://wapact.189.cn:9001/gateway/standQuery/detailNew/exchange'):
global js_code_ym, fileContent try:
cookie = '' global js_code_ym, fileContent
response = custom_client.post(getUrl) response = custom_client.post(getUrl)
content = response.text.split(' content="')[2].split('" r=')[0] response.raise_for_status()
code1 = response.text.split('$_ts=window')[1].split('</script><script type="text/javascript"')[0]
code1Content = '$_ts=window' + code1 # 解析响应内容
Url = response.text.split('$_ts.lcd();</script><script type="text/javascript" charset="utf-8" src="')[1].split('" r=')[0] content = response.text.split(' content="')[2].split('" r=')[0]
urls = getUrl.split('/') code1 = response.text.split('$_ts=window')[1].split('</script><script type="text/javascript"')[0]
rsurl = urls[0] + '//' + urls[2] + Url code1Content = '$_ts=window' + code1
filename = 'Cache.js' Url = response.text.split('$_ts.lcd();</script><script type="text/javascript" charset="utf-8" src="')[1].split('" r=')[0]
if fileContent == '':
if not os.path.exists(filename): # 处理文件缓存
fileRes = custom_client.get(rsurl) if not fileContent:
fileContent = fileRes.text fileRes = custom_client.get(Url)
if fileRes.status_code == 200: if fileRes.status_code == 200:
fileContent = fileRes.text
with open(filename, 'w', encoding='utf-8') as file: with open(filename, 'w', encoding='utf-8') as file:
file.write(fileRes.text) file.write(fileRes.text)
else: else:
print(f"Failed to download {rsurl}. Status code: {fileRes.status_code}") logger.error(f"Failed to download {Url}. Status code: {fileRes.status_code}")
if response.headers['Set-Cookie']: return None
cookie = response.headers['Set-Cookie'].split(';')[0].split('=')[1]
runJs = js_code_ym.replace('content_code', content).replace("'ts_code'", code1Content + fileContent) # 处理cookies
execjsRun = RefererCookie(runJs) cookie = response.headers.get('Set-Cookie', '').split(';')[0].split('=')[1]
return { runJs = js_code_ym.replace('content_code', content).replace("'ts_code'", code1Content + fileContent)
'cookie': cookie, execjsRun = RefererCookie(runJs)
'execjsRun': execjsRun
} return {
'cookie': cookie,
'execjsRun': execjsRun
}
except Exception as e:
logger.error(f"初始化cookies失败: {str(e)}")
return None
def RefererCookie(runJs): def RefererCookie(runJs):
try: try:

View File

@@ -2,14 +2,14 @@
新电信抢话费 新电信抢话费
群里发的,未测试好,自测 群里发的,未测试好,自测
修改内容如下 修改内容如下"
1.删除内置的一个手机账号 1.删除内置的一个手机账号
2.修改环境变量名保持和拉菲电信金豆本环境变量一致 2.修改环境变量名保持和拉菲电信金豆本环境变量一致
3.恢复瑞数通杀.js调用地址确实也不知道是啥。398、399行注释 3.恢复瑞数通杀.js调用地址确实也不知道是啥。398、399行注释
环境变量chinaTelecomAccount值为账号#密码 环境变量chinaTelecomAccount值为账号#密码
cron: 57 9,13,23 * * * cron: 56 59 09,13 * * *
const $ = new Env("新电信抢话费"); const $ = new Env("新电信抢话费");
""" """
@@ -40,11 +40,27 @@ from Crypto.Util.Padding import pad, unpad
from aiohttp import ClientSession, TCPConnector from aiohttp import ClientSession, TCPConnector
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import subprocess import subprocess
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('chinatelecom.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
run_num=os.environ.get('reqNUM') or "2" run_num=os.environ.get('reqNUM') or "2"
MAX_RETRIES = 3 # 添加配置类
RATE_LIMIT = 10 # 每秒请求数限制 class Config:
MAX_RETRIES = 3
RATE_LIMIT = 10
REQUEST_TIMEOUT = 30
BASE_URL = "https://wapact.189.cn:9001"
USER_AGENT = "Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36"
class RateLimiter: class RateLimiter:
def __init__(self, rate_limit): def __init__(self, rate_limit):
@@ -427,27 +443,89 @@ async def qgNight(phone, ticket, timeDiff,isTrue):
async def qgDay(phone, ticket, timeDiff, isTrue): async def qgDay(phone, ticket, timeDiff, isTrue):
async with AsyncSessionManager() as s: async with AsyncSessionManager() as s:
pass pass
async def main(timeDiff,isTRUE,hour):
tasks = [] # 修改 RequestUtil 类
PHONES=os.environ.get('chinaTelecomAccount') class RequestUtil:
phone_list = PHONES.split('\n') def __init__(self):
for phoneV in phone_list: # 在创建 ClientSession 时通过 headers 参数设置请求头
value = phoneV.split('#') self.session = aiohttp.ClientSession(headers={"User-Agent": Config.USER_AGENT})
phone, password = value[0], value[1] self.rate_limiter = RateLimiter(Config.RATE_LIMIT)
printn(f'{get_first_three(phone)}开始登录')
ticket = userLoginNormal(phone,password) async def safe_request(self, method, url, **kwargs):
if ticket: for attempt in range(Config.MAX_RETRIES):
# hour=datetime.datetime.now().hour try:
# hour=23 await self.rate_limiter.acquire()
if hour > 15: async with self.session.request(method, url, timeout=Config.REQUEST_TIMEOUT, **kwargs) as response:
tasks.append(qgNight(phone, ticket, timeDiff, isTRUE)) response.raise_for_status()
# await asyncio.sleep(0.1) return await response.json()
else:#十点//十四点场次 except Exception as e:
tasks.append(qgDay(phone, ticket, timeDiff, isTRUE)) if attempt == Config.MAX_RETRIES - 1:
# await asyncio.sleep(0.1) raise
else: await asyncio.sleep(2 ** attempt)
printn(f'{phone} 登录失败')
await asyncio.gather(*tasks) async def close(self):
if self.session and not self.session.closed:
await self.session.close()
# 修改 main 函数,增加日志记录
async def main(timeDiff, isTRUE, hour):
config = Config()
request_util = RequestUtil()
try:
PHONES = os.environ.get('chinaTelecomAccount')
if not PHONES:
logger.error("未找到环境变量 chinaTelecomAccount")
raise ValueError("未找到环境变量 chinaTelecomAccount")
phone_list = PHONES.split('\n')
tasks = []
logger.info(f"开始处理 {len(phone_list)} 个账号")
for phoneV in phone_list:
try:
value = phoneV.split('#')
if len(value) != 2:
logger.warning(f"账号格式错误: {phoneV}")
continue
phone, password = value[0], value[1]
logger.info(f'{get_first_three(phone)} 开始登录')
ticket = userLoginNormal(phone, password)
if not ticket:
logger.error(f'{get_first_three(phone)} 登录失败')
continue
logger.info(f'{get_first_three(phone)} 登录成功')
if hour > 15:
logger.info(f'{get_first_three(phone)} 加入夜间任务队列')
tasks.append(qgNight(phone, ticket, timeDiff, isTRUE))
else:
logger.info(f'{get_first_three(phone)} 加入日间任务队列')
tasks.append(qgDay(phone, ticket, timeDiff, isTRUE))
except Exception as e:
logger.error(f'{get_first_three(phone)} 处理失败: {str(e)}')
continue
logger.info(f"开始执行 {len(tasks)} 个任务")
results = await asyncio.gather(*tasks, return_exceptions=True)
# 记录任务执行结果
for i, result in enumerate(results, start=1): # 从1开始计数
if isinstance(result, Exception):
logger.error(f"任务 {i} 执行失败: {str(result)}")
else:
logger.info(f"任务 {i} 执行成功")
except Exception as e:
logger.error(f'主程序运行失败: {str(e)}', exc_info=True)
finally:
logger.info("正在关闭请求会话")
await request_util.close()
logger.info("程序执行完毕")
if __name__ == "__main__": if __name__ == "__main__":
h = datetime.datetime.now().hour h = datetime.datetime.now().hour

View File

@@ -8,127 +8,177 @@ export Ikuuu_HOST="ikuuu.one"
cron: 33 08 * * * cron: 33 08 * * *
const $ = new Env("ikuuu 机场签到"); const $ = new Env("ikuuu 机场签到");
*/ */
const { sendNotify } = require("./sendNotify"); // 引入 sendNotify.js 模块 const { sendNotify } = require("./sendNotify");
const fs = require('fs');
const path = require('path');
const host = process.env.Ikuuu_HOST || "ikuuu.one"; // 配置类
class Config {
const protocolPrefix = "https://"; static get HOST() {
const logInUrl = `${protocolPrefix}${host}/auth/login`; return process.env.Ikuuu_HOST || "ikuuu.one";
const checkInUrl = `${protocolPrefix}${host}/user/checkin`;
function parseCookie(rawCookie) {
let cookieSets = rawCookie.split("path=/,");
const cookies = {};
cookieSets.forEach((cookie) => {
const match = cookie.match(/^([^=]+)=(.*?);/);
if (match) {
const fieldName = match[1].trim();
let fieldValue = match[2].trim();
fieldValue = decodeURIComponent(fieldValue);
if (!cookies[fieldName]) {
cookies[fieldName] = fieldValue;
}
} }
});
return cookies; static get PROTOCOL_PREFIX() {
return "https://";
}
static get LOGIN_URL() {
return `${Config.PROTOCOL_PREFIX}${Config.HOST}/auth/login`;
}
static get CHECKIN_URL() {
return `${Config.PROTOCOL_PREFIX}${Config.HOST}/user/checkin`;
}
} }
function generateCookieStr(cookieObject) { // 日志配置
return Object.entries(cookieObject) const logStream = fs.createWriteStream(path.join(__dirname, 'ikuuu.log'), { flags: 'a' });
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join("; "); function log(level, message) {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level}] ${message}\n`;
logStream.write(logMessage);
console.log(logMessage);
} }
async function logIn(email, passwd) { // Cookie 工具类
console.log(`Logging in with email: ${email}...`); class CookieUtil {
static parseCookie(rawCookie) {
let cookieSets = rawCookie.split("path=/,");
const cookies = {};
let formData = new FormData(); cookieSets.forEach((cookie) => {
formData.append("host", host); const match = cookie.match(/^([^=]+)=(.*?);/);
formData.append("email", email); if (match) {
formData.append("passwd", passwd); const fieldName = match[1].trim();
formData.append("code", ""); let fieldValue = match[2].trim();
formData.append("remember_me", "off"); fieldValue = decodeURIComponent(fieldValue);
let response = await fetch(logInUrl, { if (!cookies[fieldName]) {
method: "POST", cookies[fieldName] = fieldValue;
body: formData, }
}); }
});
let rawCookie = response.headers.get("set-cookie"); return cookies;
}
let responseJson = await response.json(); static generateCookieStr(cookieObject) {
return Object.entries(cookieObject)
if (responseJson) { .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
console.log(responseJson.msg); .join("; ");
} }
return parseCookie(rawCookie);
} }
function checkIn(cookie) { // Ikuuu 客户端类
return fetch(checkInUrl, { class IkuuuClient {
method: "POST", constructor(email, password) {
headers: { this.email = email;
Cookie: generateCookieStr(cookie), this.password = password;
}, }
})
.then((res) => res.json()) async login() {
.then((resJson) => { log('INFO', `Logging in with email: ${this.email}...`);
if (resJson) {
console.log(resJson.msg); let formData = new FormData();
} formData.append("host", Config.HOST);
}); formData.append("email", this.email);
formData.append("passwd", this.password);
formData.append("code", "");
formData.append("remember_me", "off");
try {
let response = await fetch(Config.LOGIN_URL, {
method: "POST",
body: formData,
});
let rawCookie = response.headers.get("set-cookie");
let responseJson = await response.json();
if (responseJson) {
log('INFO', responseJson.msg);
}
return CookieUtil.parseCookie(rawCookie);
} catch (error) {
log('ERROR', `Login failed for ${this.email}: ${error.message}`);
throw error;
}
}
async checkIn(cookie) {
try {
let response = await fetch(Config.CHECKIN_URL, {
method: "POST",
headers: {
Cookie: CookieUtil.generateCookieStr(cookie),
},
});
let responseJson = await response.json();
if (responseJson) {
log('INFO', responseJson.msg);
}
} catch (error) {
log('ERROR', `Check-in failed for ${this.email}: ${error.message}`);
throw error;
}
}
} }
// 延迟函数,单位为毫秒 // 延迟函数,单位为毫秒
function delay(ms) { function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
async function main() { async function main() {
let emails = process.env.Ikuuu_EMAIL; let emails = process.env.Ikuuu_EMAIL;
let passwords = process.env.Ikuuu_PASSWD; let passwords = process.env.Ikuuu_PASSWD;
if (!emails || !passwords) { if (!emails || !passwords) {
console.log("ENV ERROR: Please set both Ikuuu_EMAIL and Ikuuu_PASSWD."); log('ERROR', "ENV ERROR: Please set both Ikuuu_EMAIL and Ikuuu_PASSWD.");
process.exit(1); process.exit(1);
} }
let emailList = emails.split(";"); let emailList = emails.split(";");
let passwdList = passwords.split(";"); let passwdList = passwords.split(";");
if (emailList.length !== passwdList.length) { if (emailList.length !== passwdList.length) {
console.log("Error: The number of emails does not match the number of passwords."); log('ERROR', "Error: The number of emails does not match the number of passwords.");
process.exit(1); process.exit(1);
} }
// 创建一个通知数组,避免多次发送相同的消息 let notifications = [];
let notifications = [];
// 遍历每个账号执行登录并签到 for (let i = 0; i < emailList.length; i++) {
for (let i = 0; i < emailList.length; i++) { let email = emailList[i];
let email = emailList[i]; let passwd = passwdList[i];
let passwd = passwdList[i]; let client = new IkuuuClient(email, passwd);
let cookie = await logIn(email, passwd);
await checkIn(cookie);
// 每个账号的操作只添加一条通知 try {
notifications.push(`账号 ${email} 登录成功,签到完成`); let cookie = await client.login();
await client.checkIn(cookie);
notifications.push(`账号 ${email} 登录成功,签到完成`);
} catch (error) {
notifications.push(`账号 ${email} 操作失败: ${error.message}`);
}
// 每次登录后延迟 2 秒 await delay(2000); // 延迟 2 秒
await delay(2000); // 延迟 2 秒 }
}
// 合并所有通知为一条消息,避免多次发送 // 过滤掉 undefined 值
const notificationMessage = notifications.join("\n"); const notificationMessage = notifications
.filter(msg => msg !== undefined)
.join("\n");
// 发送合并后的消息通知 // 调试:打印通知数组
sendNotify(`多个账号操作完成:\n${notificationMessage}`); console.log("通知数组内容:", notifications);
sendNotify(`多个账号操作完成:\n${notificationMessage}`);
} }
main(); main().catch(error => {
log('ERROR', `Main function failed: ${error.message}`);
process.exit(1);
});

138
tieba.py
View File

@@ -7,6 +7,7 @@ import os
import time import time
from requests import session from requests import session
from hashlib import md5 from hashlib import md5
from concurrent.futures import ThreadPoolExecutor, as_completed
try: try:
from sendNotify import send from sendNotify import send
@@ -14,6 +15,11 @@ except ImportError:
print("加载通知服务失败") print("加载通知服务失败")
class Tieba(): class Tieba():
# 将配置项提取为类变量
MAX_RETRY = 10 # 最大重试次数
REQUEST_TIMEOUT = 30 # 请求超时时间
SLEEP_TIME = 10 # 退出前等待时间
Tieba_BDUSS = os.getenv("Tieba_BDUSS") Tieba_BDUSS = os.getenv("Tieba_BDUSS")
def __init__(self, STOKEN): def __init__(self, STOKEN):
@@ -40,43 +46,54 @@ class Tieba():
self.session.cookies.update({'BDUSS': self.BDUSS, 'STOKEN': self.STOKEN}) self.session.cookies.update({'BDUSS': self.BDUSS, 'STOKEN': self.STOKEN})
def fetch_tbs(self): def fetch_tbs(self):
r = self.session.get('http://tieba.baidu.com/dc/common/tbs').json() try:
if r['is_login'] == 1: r = self.session.get('http://tieba.baidu.com/dc/common/tbs').json()
self.tbs = r['tbs'] if r['is_login'] == 1:
else: self.tbs = r['tbs']
raise Exception('获取tbs错误以下为返回数据' + str(r)) else:
raise Exception('获取tbs错误以下为返回数据' + str(r))
except Exception as e:
raise Exception(f'获取tbs时发生异常{str(e)}')
def fetch_likes(self): def fetch_likes(self):
self.rest = set() try:
self.already = set() self.rest = set()
r = self.session.get('https://tieba.baidu.com/mo/q/newmoindex?').json() self.already = set()
if r['no'] == 0: r = self.session.get('https://tieba.baidu.com/mo/q/newmoindex?').json()
for forum in r['data']['like_forum']: if r['no'] == 0:
if forum['is_sign'] == 1: for forum in r['data']['like_forum']:
self.already.add(forum['forum_name']) if forum['is_sign'] == 1:
else: self.already.add(forum['forum_name'])
self.rest.add(forum['forum_name']) else:
else: self.rest.add(forum['forum_name'])
raise Exception('获取关注贴吧错误!以下为返回数据:' + str(r)) else:
raise Exception('获取关注贴吧错误!以下为返回数据:' + str(r))
except Exception as e:
raise Exception(f'获取关注贴吧时发生异常:{str(e)}')
def sign(self, forum_name): def sign(self, forum_name):
data = { try:
'kw': forum_name, data = {
'tbs': self.tbs, 'kw': forum_name,
'sign': md5(f'kw={forum_name}tbs={self.tbs}tiebaclient!!!'.encode('utf8')).hexdigest() 'tbs': self.tbs,
} 'sign': md5(f'kw={forum_name}tbs={self.tbs}tiebaclient!!!'.encode('utf8')).hexdigest()
r = self.session.post('http://c.tieba.baidu.com/c/c/forum/sign', data).json() }
if r['error_code'] == '160002': r = self.session.post('http://c.tieba.baidu.com/c/c/forum/sign', data, timeout=self.REQUEST_TIMEOUT).json()
print(f'"{forum_name}"已签到') if r['error_code'] == '160002':
self.sign_list.append(forum_name) print(f'"{forum_name}"已签到')
return True self.sign_list.append(forum_name)
elif r['error_code'] == '0': return True
print(f'"{forum_name}">>>>>>>签到成功,您是第{r["user_info"]["user_sign_rank"]}个签到的用户!') elif r['error_code'] == '0':
self.result[forum_name] = r print(f'"{forum_name}">>>>>>>签到成功,您是第{r["user_info"]["user_sign_rank"]}个签到的用户!')
self.success_list.append(forum_name) self.result[forum_name] = r
return True self.success_list.append(forum_name)
else: return True
print(f'"{forum_name}"签到失败!以下为返回数据:{str(r)}') else:
print(f'"{forum_name}"签到失败!以下为返回数据:{str(r)}')
self.fail_list.append(forum_name)
return False
except Exception as e:
print(f'"{forum_name}"签到时发生异常:{str(e)}')
self.fail_list.append(forum_name) self.fail_list.append(forum_name)
return False return False
@@ -84,12 +101,19 @@ class Tieba():
print(f'* 开始第{n}轮签到 *') print(f'* 开始第{n}轮签到 *')
rest = set() rest = set()
self.fetch_tbs() self.fetch_tbs()
for forum_name in self.rest: # 使用线程池并发签到
flag = self.sign(forum_name) with ThreadPoolExecutor(max_workers=5) as executor:
if not flag: futures = {executor.submit(self.sign, forum_name): forum_name for forum_name in self.rest}
rest.add(forum_name) for future in as_completed(futures):
forum_name = futures[future]
try:
if not future.result():
rest.add(forum_name)
except Exception as e:
print(f'"{forum_name}"签到异常:{str(e)}')
rest.add(forum_name)
self.rest = rest self.rest = rest
if n >= 10: # 最大重试次数 if n >= self.MAX_RETRY: # 使用类变量
self.rest = set() self.rest = set()
def main(self, max): def main(self, max):
@@ -114,28 +138,32 @@ class Tieba():
self.send_notification_message() self.send_notification_message()
def send_notification_message(self): def send_notification_message(self):
msg = "贴吧签到结果:\n" try:
msg = "贴吧签到结果:\n"
if self.success_list: if self.success_list:
msg += "- **签到成功贴吧**\n" msg += "- **签到成功贴吧**\n"
for forum in self.success_list: for forum in self.success_list:
sign_rank = self.result[forum]['user_info']['user_sign_rank'] sign_rank = self.result[forum]['user_info']['user_sign_rank']
msg += f" {forum} (签到成功,第{sign_rank}个签到)\n" msg += f" {forum} (签到成功,第{sign_rank}个签到)\n"
if self.sign_list: if self.sign_list:
msg += "- **已经签到的贴吧**\n" msg += "- **已经签到的贴吧**\n"
msg += " " + "\n ".join(self.sign_list) + "\n" msg += " " + ", ".join(self.sign_list) # 去掉末尾的换行符
msg += f"\n共关注了{len(self.already) + len(self.success_list)}个贴吧," # 直接拼接统计信息,去掉前面的空行
msg += f"本次成功签到{len(self.success_list)}个," msg += f"\n共关注{len(self.already) + len(self.success_list)}贴吧"
msg += f"失败{len(self.fail_list)}个," msg += f"本次成功签到{len(self.success_list)}个,"
msg += f"{len(self.sign_list)}贴吧已经签到。" msg += f"失败了{len(self.fail_list)}"
msg += f"{len(self.sign_list)}个贴吧已经签到。"
# 发送通知 # 发送通知
send('Tieba_Sign', msg) send('Tieba_Sign', msg)
# Add a 10-second delay before exiting # 使用类变量
time.sleep(10) time.sleep(self.SLEEP_TIME)
except Exception as e:
print(f'发送通知时发生异常:{str(e)}')
if __name__ == "__main__": if __name__ == "__main__":
BDUSS_values = os.getenv("Tieba_BDUSS") BDUSS_values = os.getenv("Tieba_BDUSS")