Files
js_tool_clone/bot/bot.py
2021-07-08 01:40:37 -04:00

470 lines
20 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# _*_ coding:utf-8 _*_
# author: https://github.com/SuMaiKaDe
from telethon import TelegramClient, events, Button
import requests
import re
import json
import time
import os
import qrcode
import logging
from asyncio import exceptions
logging.basicConfig(
format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO)
logger = logging.getLogger(__name__)
_JdDir = '/root/jd'
_ConfigDir = _JdDir + '/config'
_ScriptsDir = _JdDir + '/scripts'
_LogDir = _JdDir + '/log'
_ThirdpardDir = _JdDir +'/thirdpard'
# 频道id/用户id
with open('/root/jd/config/bot.json') as f:
bot = json.load(f)
chat_id = bot['user_id']
# 机器人 TOKEN
TOKEN = bot['bot_token']
# 发消息的TG代理
# my.telegram.org申请到的api_id,api_hash
api_id = bot['api_id']
api_hash = bot['api_hash']
proxystart = bot['proxy']
proxy = (bot['proxy_type'], bot['proxy_add'], bot['proxy_port'])
# 开启tg对话
if proxystart:
client = TelegramClient('bot', api_id, api_hash,proxy=proxy).start(bot_token=TOKEN)
else:
client = TelegramClient('bot', api_id, api_hash).start(bot_token=TOKEN)
cookiemsg =''
img_file = '/jd/config/qr.jpg'
StartCMD = bot['StartCMD']
def press_event(user_id):
return events.CallbackQuery(func=lambda e: e.sender_id == user_id)
# 扫码获取cookie 直接采用LOF大佬代码
# getSToken请求获取s_token用于发送post请求是的必须参数
s_token = ""
# getSToken请求获取guid,lsid,lstoken用于组装cookies
guid, lsid, lstoken = "", "", ""
# 由上面参数组装生成getOKLToken函数发送请求需要使用
cookies = ""
# getOKLToken请求获取token用户生成二维码使用、okl_token用户检查扫码登录结果使用
token, okl_token = "", ""
# 最终获取到的可用的cookie
jd_cookie = ""
def getSToken():
time_stamp = int(time.time() * 1000)
get_url = 'https://plogin.m.jd.com/cgi-bin/mm/new_login_entrance?lang=chs&appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=%s&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % time_stamp
get_header = {
'Connection': 'Keep-Alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-cn',
'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=%s&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % time_stamp,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'Host': 'plogin.m.jd.com'
}
try:
resp = requests.get(url=get_url, headers=get_header)
parseGetRespCookie(resp.headers, resp.json())
logger.info(resp.headers)
logger.info(resp.json())
except Exception as error:
logger.exception("Get网络请求异常", error)
def parseGetRespCookie(headers, get_resp):
global s_token
global cookies
s_token = get_resp.get('s_token')
set_cookies = headers.get('set-cookie')
logger.info(set_cookies)
guid = re.findall(r"guid=(.+?);", set_cookies)[0]
lsid = re.findall(r"lsid=(.+?);", set_cookies)[0]
lstoken = re.findall(r"lstoken=(.+?);", set_cookies)[0]
cookies = f"guid={guid}; lang=chs; lsid={lsid}; lstoken={lstoken}; "
logger.info(cookies)
def getOKLToken():
post_time_stamp = int(time.time() * 1000)
post_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=%s&v=%s&remember=true' % (
s_token, post_time_stamp)
post_data = {
'lang': 'chs',
'appid': 300,
'returnurl': 'https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action' % post_time_stamp,
'source': 'wq_passport'
}
post_header = {
'Connection': 'Keep-Alive',
'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8',
'Accept': 'application/json, text/plain, */*',
'Cookie': cookies,
'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % post_time_stamp,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'Host': 'plogin.m.jd.com',
}
try:
global okl_token
resp = requests.post(
url=post_url, headers=post_header, data=post_data, timeout=20)
parsePostRespCookie(resp.headers, resp.json())
logger.info(resp.headers)
except Exception as error:
logger.exception("Post网络请求错误", error)
def parsePostRespCookie(headers, data):
global token
global okl_token
token = data.get('token')
okl_token = re.findall(r"okl_token=(.+?);", headers.get('set-cookie'))[0]
logger.info("token:" + token)
logger.info("okl_token:" + okl_token)
def chekLogin():
global login
def parseJDCookies(headers):
global jd_cookie
logger.info("扫码登录成功下面为获取到的用户Cookie。")
set_cookie = headers.get('Set-Cookie')
pt_key = re.findall(r"pt_key=(.+?);", set_cookie)[0]
pt_pin = re.findall(r"pt_pin=(.+?);", set_cookie)[0]
logger.info(pt_key)
logger.info(pt_pin)
jd_cookie = f'pt_key={pt_key};pt_pin={pt_pin};'
def creatqr(text):
'''实例化QRCode生成qr对象'''
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4
)
qr.clear()
# 传入数据
qr.add_data(text)
qr.make(fit=True)
# 生成二维码
img = qr.make_image()
# 保存二维码
img.save(img_file)
def split_list(datas, n, row: bool = True):
"""一维列表转二维列表根据N不同生成不同级别的列表"""
length = len(datas)
size = length / n + 1 if length % n else length/n
_datas = []
if not row:
size, n = n, size
for i in range(int(size)):
start = int(i * n)
end = int((i + 1) * n)
_datas.append(datas[start:end])
return _datas
async def logbtn(conv, SENDER, path: str, content: str, msg):
'''定义log日志按钮'''
try:
dir = os.listdir(path)
dir.sort()
markup = [Button.inline(file, data=str(path+'/'+file))
for file in dir]
markup.append(Button.inline('取消', data='cancle'))
markup = split_list(markup, 3)
msg = await client.edit_message(msg, '请做出你的选择:', buttons=markup)
convdata = await conv.wait_event(press_event(SENDER))
res = bytes.decode(convdata.data)
if res == 'cancle':
msg = await client.edit_message(msg, '对话已取消')
conv.cancel()
return None, None
elif os.path.isfile(res):
msg = await client.edit_message(msg, content + '中,请注意查收')
await conv.send_file(res)
msg = await client.edit_message(msg, res+ content + '成功,请查收')
conv.cancel()
return None, None
else:
return res, msg
except exceptions.TimeoutError:
msg = await client.edit_message(msg, '选择已超时,对话已停止')
return None, None
except Exception as e:
msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e))
logger.error('something wrong,I\'m sorry\n'+str(e))
return None, None
async def nodebtn(conv, SENDER, path: str, msg):
'''定义scripts脚本按钮'''
try:
if path == '/jd':
dir = ['scripts', 'thirdpard']
else:
dir = os.listdir(path)
dir.sort()
markup = [Button.inline(file, data=str(path+'/'+file))
for file in dir if os.path.isdir(path+'/'+file) or re.search(r'.js$', file)]
markup.append(Button.inline('取消', data='cancel'))
markup = split_list(markup, 3)
msg = await client.edit_message(msg, '请做出你的选择:', buttons=markup)
convdata = await conv.wait_event(press_event(SENDER))
res = bytes.decode(convdata.data)
if res == 'cancel':
msg = await client.edit_message(msg, '对话已取消')
conv.cancel()
return None, None
elif os.path.isfile(res):
msg = await client.edit_message(msg, '脚本即将在后台运行')
logger.info(res+'脚本即将在后台运行')
os.popen('/jd/jd.sh {} now >/jd/log/bot.log &'.format(res))
msg = await client.edit_message(msg, res + '在后台运行成功,请自行在程序结束后查看日志')
conv.cancel()
return None, None
else:
return res, msg
except exceptions.TimeoutError:
msg = await client.edit_message(msg, '选择已超时,对话已停止')
return None, None
except Exception as e:
msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e))
logger.error('something wrong,I\'m sorry\n'+str(e))
return None, None
@client.on(events.NewMessage(from_users=chat_id, pattern=r'^/log'))
async def mylog(event):
'''定义日志文件操作'''
SENDER = event.sender_id
path = _LogDir
async with client.conversation(SENDER, timeout=60) as conv:
msg = await conv.send_message('正在查询,请稍后')
while path:
path, msg = await logbtn(conv, SENDER, path, '查询日志', msg)
@client.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode'))
async def mysnode(event):
'''定义supernode文件命令'''
SENDER = event.sender_id
path = _JdDir
async with client.conversation(SENDER, timeout=60) as conv:
msg = await conv.send_message('正在查询,请稍后')
while path:
path, msg = await nodebtn(conv, SENDER, path, msg)
@client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile'))
async def mygetfile(event):
'''定义获取文件命令'''
SENDER = event.sender_id
path = _JdDir
async with client.conversation(SENDER, timeout=60) as conv:
msg = await conv.send_message('正在查询,请稍后')
while path:
path, msg = await logbtn(conv, SENDER, path, '文件发送', msg)
async def backfile(file):
if os.path.exists(file):
try:
os.rename(file, file+'.bak')
except WindowsError:
os.remove(file+'.bak')
os.rename(file, file+'.bak')
@client.on(events.NewMessage(from_users=chat_id))
async def myfile(event):
'''定义文件操作'''
try:
SENDER = event.sender_id
if event.message.file:
markup = []
filename = event.message.file.name
async with client.conversation(SENDER, timeout=30) as conv:
msg = await conv.send_message('请选择您要放入的文件夹或操作:\n')
markup.append(Button.inline('放入config', data=_ConfigDir))
markup.append(Button.inline('放入scripts', data=_ScriptsDir))
markup.append(Button.inline('放入thirdpard', data=_ThirdpardDir))
markup.append(Button.inline('放入thirdpard并运行', data='node'))
msg = await client.edit_message(msg, '请做出你的选择:', buttons=markup)
convdata = await conv.wait_event(press_event(SENDER))
res = bytes.decode(convdata.data)
if res == 'node':
await backfile(_ThirdpardDir+'/'+filename)
await client.download_media(event.message, _ThirdpardDir)
os.popen('jd {}/{} now >/jd/log/bot.log &'.format(_ThirdpardDir,filename))
await client.edit_message(msg,'脚本已保存到thirdpard文件夹并成功在后台运行请稍后自行查看日志')
conv.cancel()
else:
await backfile(res+'/'+filename)
await client.download_media(event.message, res)
await client.edit_message(msg,filename+'已保存到'+res+'文件夹')
if filename == 'crontab.list':
os.popen('crontab '+res+'/'+filename)
await client.edit_message(msg, '定时文件已保存,并更新')
conv.cancel()
except Exception as e:
await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e))
logger.error('something wrong,I\'m sorry\n'+str(e))
@client.on(events.NewMessage(from_users=chat_id, pattern='/node'))
async def mynode(event):
'''接收/node命令后执行程序'''
nodereg = re.compile(r'^/node [\S]+')
text = re.findall(nodereg, event.raw_text)
if len(text) == 0:
res = '''请正确使用/node命令
/node /abc/123.js 运行abc/123.js脚本
/node /thirdpard/abc.js 运行thirdpard/abc.js脚本
'''
await client.send_message(chat_id, res)
else:
await cmd('jd '+text[0].replace('/node ', '')+' now')
@client.on(events.NewMessage(from_users=chat_id, pattern='/cmd'))
async def mycmd(event):
'''接收/cmd命令后执行程序'''
if StartCMD:
cmdreg = re.compile(r'^/cmd [\s\S]+')
text = re.findall(cmdreg, event.raw_text)
if len(text) == 0:
msg = '''请正确使用/cmd命令
/cmd jd clean # 删除旧日志
/cmd jd update # 更新所有脚本
/cmd jd myhelp # 导出所有互助码
不建议直接使用cmd命令执行脚本请使用/node或/snode
'''
await client.send_message(chat_id, msg)
else:
print(text)
await cmd(text[0].replace('/cmd ', ''))
else:
await client.send_message(chat_id, '未开启CMD命令如需使用请修改配置文件')
async def cmd(cmdtext):
'''定义执行cmd命令'''
try:
await client.send_message(chat_id, '开始执行程序,如程序复杂,建议稍等')
res = os.popen(cmdtext).read()
if len(res) == 0:
await client.send_message(chat_id, '已执行,但返回值为空')
elif len(res) <= 4000:
await client.send_message(chat_id, res)
else:
with open(_LogDir+'/botres.log','w+') as f:
f.write(res)
await client.send_message(chat_id, '执行结果较长,请查看日志',file=_LogDir+'/botres.log')
except Exception as e:
await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e))
logger.error('something wrong,I\'m sorry'+str(e))
@client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getcookie'))
async def mycookie(event):
'''接收/getcookie后执行程序'''
login = True
msg = await client.send_message(chat_id,'正在获取二维码,请稍后')
global cookiemsg
try:
SENDER = event.sender_id
async with client.conversation(SENDER, timeout=30) as conv:
getSToken()
getOKLToken()
url = 'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token='+token
creatqr(url)
markup = [Button.inline("已扫码", data='confirm'),Button.inline("取消", data='cancel')]
await client.delete_messages(chat_id,msg)
cookiemsg = await client.send_message(chat_id, '30s内点击取消将取消本次操作\n如不取消扫码结果将于30s后显示\n扫码后不想等待点击已扫码', file=img_file,buttons=markup)
convdata = await conv.wait_event(press_event(SENDER))
res = bytes.decode(convdata.data)
if res == 'cancel':
login = False
await client.delete_messages(chat_id,cookiemsg)
msg = await conv.send_message('对话已取消')
conv.cancel()
else:
raise exceptions.TimeoutError()
except exceptions.TimeoutError:
expired_time = time.time() + 60 * 2
while login:
check_time_stamp = int(time.time() * 1000)
check_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=%s&ou_state=0&okl_token=%s' % (
token, okl_token)
check_data = {
'lang': 'chs',
'appid': 300,
'returnurl': 'https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action' % check_time_stamp,
'source': 'wq_passport'
}
check_header = {
'Referer': f'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % check_time_stamp,
'Cookie': cookies,
'Connection': 'Keep-Alive',
'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8',
'Accept': 'application/json, text/plain, */*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
}
resp = requests.post(
url=check_url, headers=check_header, data=check_data, timeout=30)
data = resp.json()
if data.get("errcode") == 0:
parseJDCookies(resp.headers)
await client.delete_messages(chat_id,cookiemsg)
await client.send_message(chat_id, '以下为获取到的cookie')
await client.send_message(chat_id, jd_cookie)
return
if data.get("errcode") == 21:
await client.delete_messages(chat_id,cookiemsg)
await client.send_message(chat_id, '发生了某些错误\n'+data.get("errcode"))
return
if time.time() > expired_time:
await client.delete_messages(chat_id,cookiemsg)
await client.send_message(chat_id, '超过3分钟未扫码二维码已过期')
return
except Exception as e:
await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e))
logger.error('something wrong,I\'m sorry\n'+str(e))
@client.on(events.NewMessage(from_users=chat_id, pattern='/help'))
@client.on(events.NewMessage(from_users=chat_id, pattern='/start'))
async def mystart(event):
'''接收/help /start命令后执行程序'''
msg = '''使用方法如下:
/start 开始使用本程序
/node 执行js脚本文件直接输入/node jd_bean_change 如执行其他自己js需输入绝对路径。即可进行执行。该命令会等待脚本执行完期间不能使用机器人建议使用snode命令。
/cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 不建议使用机器人使用并发,可能产生不明原因的崩溃
/snode 命令可以选择脚本执行,只能选择/scripts 和/thirdpard目录下的脚本选择完后直接后台运行不影响机器人响应其他命令
/log 选择查看执行日志
/getfile 获取jd目录下文件
/getcookie 扫码获取cookie 增加30s内取消按钮30s后不能进行其他交互直到2分钟或获取到cookie
此外直接发送文件会让你选择保存到哪个文件夹如果选择运行将保存至thirdpard目录下并立即运行脚本crontab.list文件会自动更新时间'''
await client.send_message(chat_id, msg)
with client:
client.loop.run_forever()