diff --git a/repo/dockerbot/jbot/__init__.py b/repo/dockerbot/jbot/__init__.py new file mode 100644 index 00000000..236784e0 --- /dev/null +++ b/repo/dockerbot/jbot/__init__.py @@ -0,0 +1,56 @@ +from telethon import TelegramClient +import json +import os +import logging + +_JdDir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +_ConfigDir = _JdDir + '/config' +_ScriptsDir = _JdDir + '/scripts' +_OwnDir = _JdDir + '/own' +_JdbotDir = _JdDir + '/jbot' +_DiyScripts = _JdDir + '/diyscripts' +_LogDir = _JdDir + '/log' +_shortcut = _ConfigDir + '/shortcut.list' +_botlog = _LogDir + '/bot/run.log' +_botjson = _ConfigDir + '/bot.json' +img_file = _ConfigDir + 'qr.jpg' +if not os.path.exists(_LogDir + '/bot'): + os.mkdir(_LogDir + '/bot') +logging.basicConfig( + format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO, filename=_botlog, + filemode='w') +logger = logging.getLogger(__name__) + +with open(_botjson, 'r', encoding='utf-8') as f: + bot = json.load(f) +chat_id = int(bot['user_id']) +# 机器人 TOKEN +TOKEN = bot['bot_token'] +# HOSTAPI = bot['apihost'] +# 发消息的TG代理 +# my.telegram.org申请到的api_id,api_hash +api_id = bot['api_id'] +api_hash = bot['api_hash'] +proxystart = bot['proxy'] +StartCMD = bot['StartCMD'] +if 'proxy_user' in bot.keys() and bot['proxy_user'] != "代理的username,有则填写,无则不用动": + proxy = { + 'proxy_type': bot['proxy_type'], + 'addr': bot['proxy_add'], + 'port': bot['proxy_port'], + 'username': bot['proxy_user'], + 'password': bot['proxy_password']} +else: + proxy = (bot['proxy_type'], bot['proxy_add'], bot['proxy_port']) +# 开启tg对话 +if proxystart and 'noretry' in bot.keys() and bot['noretry']: + jdbot = TelegramClient('bot', api_id, api_hash, + proxy=proxy).start(bot_token=TOKEN) +elif proxystart: + jdbot = TelegramClient('bot', api_id, api_hash, + proxy=proxy, connection_retries=None).start(bot_token=TOKEN) +elif 'noretry' in bot.keys() and bot['noretry']: + jdbot = TelegramClient('bot', api_id, api_hash).start(bot_token=TOKEN) +else: + jdbot = TelegramClient('bot', api_id, api_hash, + connection_retries=None).start(bot_token=TOKEN) diff --git a/repo/dockerbot/jbot/__main__.py b/repo/dockerbot/jbot/__main__.py new file mode 100644 index 00000000..27ee4c0b --- /dev/null +++ b/repo/dockerbot/jbot/__main__.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# _*_ coding:utf-8 _*_ +# 0.3 版本开始不再区分ql、V3、V4。运行日志:log/bot/run.log +# author: https://github.com/SuMaiKaDe + +from . import jdbot, chat_id, logger,_JdbotDir, _LogDir +from .utils import load_diy +import os +from .bot.update import version,botlog +_botuplog = _LogDir + '/bot/up.log' +botpath = _JdbotDir + "/bot/" +diypath = _JdbotDir + "/diy/" +logger.info('loading bot module...') +load_diy('bot', botpath) +logger.info('loading diy module...') +load_diy('diy', diypath) + +async def hello(): + if os.path.exists(_botuplog): + isnew = False + with open(_botuplog, 'r', encoding='utf-8') as f: + logs = f.readlines() + for log in logs: + if version in log: + isnew = True + return + if not isnew: + with open(_botuplog, 'a', encoding='utf-8') as f: + f.writelines([version, botlog]) + await jdbot.send_message(chat_id, '[机器人上新了](https://github.com/SuMaiKaDe/jddockerbot/tree/master)\n'+botlog+'\n运行日志为log/bot/run.log', link_preview=False) + else: + with open(_botuplog, 'w+', encoding='utf-8') as f: + f.writelines([version, botlog]) + await jdbot.send_message(chat_id, '[机器人上新了](https://github.com/SuMaiKaDe/jddockerbot/tree/master)\n'+botlog+'\n运行日志为log/bot/run.log', link_preview=False) +if __name__ == "__main__": + with jdbot: + jdbot.loop.create_task(hello()) + jdbot.loop.run_forever() diff --git a/repo/dockerbot/jbot/bot/bean.py b/repo/dockerbot/jbot/bot/bean.py new file mode 100644 index 00000000..012d8c8c --- /dev/null +++ b/repo/dockerbot/jbot/bot/bean.py @@ -0,0 +1,84 @@ +from PIL import Image, ImageFont, ImageDraw +from telethon import events +from .. import jdbot, chat_id, _LogDir, _JdbotDir,logger +from prettytable import PrettyTable +import subprocess +from .beandata import get_bean_data +IN = _LogDir + '/bean_income.csv' +OUT = _LogDir + '/bean_outlay.csv' +TOTAL = _LogDir + '/bean_total.csv' +_botimg = _LogDir + '/bean.jpg' +_font = _JdbotDir + '/font/jet.ttf' + + +@jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/bean')) +async def mybean(event): + try: + await jdbot.send_message(chat_id, '正在查询,请稍后') + if len(event.raw_text.split(' ')) > 1: + text = event.raw_text.replace('/bean ', '') + else: + text = None + if text and text == 'in': + subprocess.check_output( + 'jcsv', shell=True, stderr=subprocess.STDOUT) + creat_bean_counts(IN) + await jdbot.send_message(chat_id, '您的近日收入情况', file=_botimg) + elif text and text == 'out': + subprocess.check_output( + 'jcsv', shell=True, stderr=subprocess.STDOUT) + creat_bean_counts(OUT) + await jdbot.send_message(chat_id, '您的近日支出情况', file=_botimg) + elif text and int(text): + beanin, beanout, beanstotal,date = get_bean_data(int(text)) + if not beanout: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(beanin)) + else: + creat_bean_count(date,beanin, beanout, beanstotal[1:]) + await jdbot.send_message(chat_id, f'您的账号{text}收支情况', file=_botimg) + else: + subprocess.check_output( + 'jcsv', shell=True, stderr=subprocess.STDOUT) + creat_bean_counts(TOTAL) + await jdbot.send_message(chat_id, '您的总京豆情况', file=_botimg) + except Exception as e: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry'+str(e)) + +def creat_bean_count(date,beansin,beansout,beanstotal): + tb = PrettyTable() + tb.add_column('DATE',date) + tb.add_column('BEANIN',beansin) + tb.add_column('BEANOUT',beansout) + tb.add_column('TOTAL',beanstotal) + font = ImageFont.truetype(_font, 18) + im = Image.new("RGB", (500, 260), (244, 244, 244)) + dr = ImageDraw.Draw(im) + dr.text((10, 5), str(tb), font=font, fill="#000000") + im.save(_botimg) + +def creat_bean_counts(csv_file): + with open(csv_file, 'r', encoding='utf-8') as f: + data = f.readlines() + tb = PrettyTable() + num = len(data[-1].split(',')) - 1 + title = ['DATE'] + for i in range(0, num): + title.append('COUNT'+str(i+1)) + tb.field_names = title + data = data[-7:] + for line in data: + row = line.split(',') + if len(row) > len(title): + row = row[:len(title)] + elif len(row) < len(title): + i = len(title) - len(row) + for _ in range(0,i): + row.append(0) + tb.add_row(row) + length = 172 + 100 * num + im = Image.new("RGB", (length, 400), (244, 244, 244)) + dr = ImageDraw.Draw(im) + font = ImageFont.truetype(_font, 18) + dr.text((10, 5), str(tb), font=font, fill="#000000") + im.save(_botimg) diff --git a/repo/dockerbot/jbot/bot/beandata.py b/repo/dockerbot/jbot/bot/beandata.py new file mode 100644 index 00000000..f812f71e --- /dev/null +++ b/repo/dockerbot/jbot/bot/beandata.py @@ -0,0 +1,114 @@ +import requests +import datetime +import time +import json +from datetime import timedelta +from datetime import timezone +from .utils import cookies +SHA_TZ = timezone( + timedelta(hours=8), + name='Asia/Shanghai', +) + + +session = requests.session() + + +url = "https://api.m.jd.com/api" + + +def getbody(page): + body = { + "beginDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), + "endDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), + "pageNo": page, + "pageSize": 20, + } + return body + + +def getparms(page): + body = getbody(page) + parms = { + "functionId": "jposTradeQuery", + "appid": "swat_miniprogram", + "client": "tjj_m", + "sdkName": "orderDetail", + "sdkVersion": "1.0.0", + "clientVersion": "3.1.3", + "timestamp": int(round(time.time() * 1000)), + "body": json.dumps(body) + } + return parms + + +def getbeans(ck): + _7day = True + page = 0 + headers = { + "Host": "api.m.jd.com", + "Connection": "keep-alive", + "charset": "utf-8", + "User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2797 MMWEBSDK/201201 Mobile Safari/537.36 MMWEBID/7986 MicroMessenger/8.0.1840(0x2800003B) Process/appbrand4 WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android", + "Content-Type": "application/x-www-form-urlencoded;", + "Accept-Encoding": "gzip, compress, deflate, br", + "Cookie": ck, + "Referer": "https://servicewechat.com/wxa5bf5ee667d91626/141/page-frame.html", + } + _7days = [] + for i in range(0, 7): + _7days.append( + (datetime.date.today() - datetime.timedelta(days=i)).strftime("%Y-%m-%d")) + beansin = {key: 0 for key in _7days} + beansout = {key: 0 for key in _7days} + while _7day: + page = page + 1 + resp = session.get(url, params=getparms(page), headers=headers).text + res = json.loads(resp) + if res['resultCode'] == 0: + for i in res['data']['list']: + for date in _7days: + if str(date) in i['createDate'] and i['amount'] > 0: + beansin[str(date)] = beansin[str(date)] + i['amount'] + break + elif str(date) in i['createDate'] and i['amount'] < 0: + beansout[str(date)] = beansout[str(date)] + i['amount'] + break + if i['createDate'].split(' ')[0] not in str(_7days): + _7day = False + else: + return 'error' + str(res), None, None + return beansin, beansout, _7days + + +def getTotal(ck): + headers = { + "Host": "wxapp.m.jd.com", + "Connection": "keep-alive", + "charset": "utf-8", + "User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2797 MMWEBSDK/201201 Mobile Safari/537.36 MMWEBID/7986 MicroMessenger/8.0.1840(0x2800003B) Process/appbrand4 WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android", + "Content-Type": "application/x-www-form-urlencoded;", + "Accept-Encoding": "gzip, compress, deflate, br", + "Cookie": ck, + } + jurl = "https://wxapp.m.jd.com/kwxhome/myJd/home.json" + resp = session.get(jurl, headers=headers).text + res = json.loads(resp) + return res['user']['jingBean'] + + +def get_bean_data(i): + ck = cookies[i-1] + beansin, beansout, _7days = getbeans(ck) + beantotal = getTotal(ck) + if not beansout: + return str(beansin), None, None,None + else: + beanin, beanout = [], [] + beanstotal = [int(beantotal), ] + for i in beansin: + beantotal = int(beantotal) - int(beansin[i]) - int(beansout[i]) + beanin.append(beansin[i]) + beanout.append(int(str(beansout[i]).replace('-', ''))) + beanstotal.append(beantotal) + return beanin[::-1], beanout[::-1], beanstotal[::-1], _7days[::-1] diff --git a/repo/dockerbot/jbot/bot/chart.py b/repo/dockerbot/jbot/bot/chart.py new file mode 100644 index 00000000..253570aa --- /dev/null +++ b/repo/dockerbot/jbot/bot/chart.py @@ -0,0 +1,134 @@ +from telethon import events +from .. import jdbot, chat_id, _LogDir, logger +from ..bot.quickchart import QuickChart +from .beandata import get_bean_data +_botimg = _LogDir + '/bot/bean.jpeg' + +@jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/chart')) +async def mybean(event): + try: + await jdbot.send_message(chat_id, '正在查询,请稍后') + if len(event.raw_text.split(' ')) > 1: + text = event.raw_text.replace('/chart ', '') + else: + text = None + if text and int(text): + beanin, beanout, beanstotal, date = get_bean_data(int(text)) + if not beanout: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(beanin)) + else: + creat_chart(date, '账号'+str(text), + beanin, beanout, beanstotal[1:]) + await jdbot.send_message(chat_id, f'您的账号{text}收支情况', file=_botimg) + else: + await jdbot.send_message(chat_id, '请正确使用命令\n/chart n n为第n个账号') + except Exception as e: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry'+str(e)) + + +def creat_chart(xdata, title, bardata, bardata2, linedate): + qc = QuickChart() + qc.background_color = '#fff' + qc.width = "1000" + qc.height = "600" + qc.config = { + "type": "bar", + "data": { + "labels": xdata, + "datasets": [ + { + "label": "IN", + "backgroundColor": [ + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(54, 162, 235)", + "rgb(153, 102, 255)", + "rgb(255, 99, 132)" + ], + "yAxisID": "y1", + "data": bardata + }, + { + "label": "OUT", + "backgroundColor": [ + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(54, 162, 235)", + "rgb(153, 102, 255)", + "rgb(255, 99, 132)" + ], + "yAxisID": "y1", + "data": bardata2 + }, + { + "label": "TOTAL", + "type": "line", + "fill": False, + "backgroundColor": "rgb(201, 203, 207)", + "yAxisID": "y2", + "data": linedate + } + ] + }, + "options": { + "plugins": { + "datalabels": { + "anchor": 'end', + "align": -100, + "color": '#666', + "font": { + "size": 20, + } + }, + }, + "legend": { + "labels": { + "fontSize": 20, + "fontStyle": 'bold', + } + }, + "title": { + "display": True, + "text": title + " 收支情况", + "fontSize": 24, + }, + "scales": { + "xAxes": [{ + "ticks": { + "fontSize": 24, + } + }], + "yAxes": [ + { + "id": "y1", + "type": "linear", + "display": False, + "position": "left", + "ticks": { + "max": int(int(max([max(bardata), max(bardata2)])+100)*2) + }, + "scaleLabel": { + "fontSize": 20, + "fontStyle": 'bold', + } + }, + { + "id": "y2", + "type": "linear", + "display": False, + "ticks": { + "min": int(min(linedate)*2-(max(linedate))-100), + "max": int(int(max(linedate))) + }, + "position": "right" + } + ] + } + } + } + qc.to_file(_botimg) diff --git a/repo/dockerbot/jbot/bot/cmd.py b/repo/dockerbot/jbot/bot/cmd.py new file mode 100644 index 00000000..b50048a6 --- /dev/null +++ b/repo/dockerbot/jbot/bot/cmd.py @@ -0,0 +1,26 @@ +from telethon import events +import re +from .. import jdbot, StartCMD, chat_id, logger +from .utils import cmd + + +@jdbot.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 jlog # 删除旧日志 + /cmd jup # 更新所有脚本 + /cmd jcode # 导出所有互助码 + /cmd jcsv # 记录豆豆变化情况 + 不建议直接使用cmd命令执行脚本,请使用/node或/snode + ''' + await jdbot.send_message(chat_id, msg) + else: + logger.info(text) + await cmd(text[0].replace('/cmd ', '')) + else: + await jdbot.send_message(chat_id, '未开启CMD命令,如需使用请修改配置文件') diff --git a/repo/dockerbot/jbot/bot/editfile.py b/repo/dockerbot/jbot/bot/editfile.py new file mode 100644 index 00000000..88158c1d --- /dev/null +++ b/repo/dockerbot/jbot/bot/editfile.py @@ -0,0 +1,137 @@ +from telethon import events, Button +import os +import shutil +from asyncio import exceptions +from .. import jdbot, chat_id, _JdDir +from .utils import split_list, logger,press_event + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern='/edit')) +async def myfileup(event): + '''定义编辑文件操作''' + SENDER = event.sender_id + path = _JdDir + page = 0 + if len(event.raw_text.split(' ')) > 1: + text = event.raw_text.replace('/edit ','') + else: + text =None + if text and os.path.isfile(text): + try: + with open(text,'r',encoding='utf-8') as f: + lines = f.readlines() + filelist = split_list(lines, 15) + path = text + except Exception as e: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) + elif text and os.path.isdir(text): + path = text + filelist = None + elif text: + await jdbot.send_message(chat_id, 'please marksure it\'s a dir or a file') + filelist = None + else: + filelist = None + async with jdbot.conversation(SENDER, timeout=60) as conv: + msg = await conv.send_message('正在查询,请稍后') + while path: + path, msg, page, filelist = await myedit(conv, SENDER, path, msg, page, filelist) + + +async def myedit(conv, SENDER, path, msg, page, filelist): + mybtn = [Button.inline('上一页', data='up'), Button.inline('下一页', data='next'), Button.inline( + '上级', data='updir'), Button.inline('取消', data='cancel')] + mybtn2 = [[Button.inline('上一页', data='up'), Button.inline( + '下一页', data='next'), Button.inline('取消', data='cancel')], [Button.inline('上十页', data='up10'), Button.inline( + '下十页', data='next10'), Button.inline('编辑', data='edit')]] + try: + if filelist and type(filelist[0][0]) == str: + markup = filelist + newmarkup = markup[page] + msg = await jdbot.edit_message(msg, "".join(newmarkup), buttons=mybtn2) + else: + if filelist: + markup = filelist + newmarkup = markup[page] + if mybtn not in newmarkup: + newmarkup.append(mybtn) + else: + dir = os.listdir(path) + dir.sort() + markup = [Button.inline(file, data=str( + file)) for file in dir] + markup = split_list(markup, 3) + if len(markup) > 30: + markup = split_list(markup, 30) + newmarkup = markup[page] + newmarkup.append(mybtn) + else: + newmarkup = markup + if path == _JdDir: + newmarkup.append([Button.inline('取消', data='cancel')]) + else: + newmarkup.append( + [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) + msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) + convdata = await conv.wait_event(press_event(SENDER)) + res = bytes.decode(convdata.data) + if res == 'cancel': + msg = await jdbot.edit_message(msg, '对话已取消') + conv.cancel() + return None, None, None, None + elif res == 'next': + page = page + 1 + if page > len(markup) - 1: + page = 0 + return path, msg, page, markup + elif res == 'up': + page = page - 1 + if page < 0: + page = len(markup) - 1 + return path, msg, page, markup + elif res == 'next10': + page = page + 10 + if page > len(markup) - 1: + page = 0 + return path, msg, page, markup + elif res == 'up10': + page = page - 10 + if page < 0: + page = len(markup) - 1 + return path, msg, page, markup + elif res == 'updir': + path = '/'.join(path.split('/')[:-1]) + if path == '': + path = _JdDir + return path, msg, page, None + elif res == 'edit': + await jdbot.send_message(chat_id, '请复制并修改以下内容,修改完成后发回机器人,2分钟内有效') + await jdbot.delete_messages(chat_id, msg) + msg = await conv.send_message("".join(newmarkup)) + resp = await conv.get_response() + markup[page] = resp.raw_text.split('\n') + for a in range(len(markup[page])): + markup[page][a] = markup[page][a]+'\n' + shutil.copy(path, path+'.bak') + with open(path, 'w+', encoding='utf-8') as f: + markup = ["".join(a) for a in markup] + f.writelines(markup) + await jdbot.send_message(chat_id, '文件已修改成功,原文件备份为'+path+'.bak') + conv.cancel() + return None, None, None, None + elif os.path.isfile(path+'/'+res): + msg = await jdbot.edit_message(msg, '文件读取中...请稍候') + with open(path+'/'+res, 'r', encoding='utf-8') as f: + lines = f.readlines() + lines = split_list(lines, 15) + page = 0 + return path+'/'+res, msg, page, lines + else: + return path+'/'+res, msg, page, None + except exceptions.TimeoutError: + msg = await jdbot.edit_message(msg, '选择已超时,本次对话已停止') + return None, None, None, None + except Exception as e: + msg = await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry\n'+str(e)) + return None, None, None, None diff --git a/repo/dockerbot/jbot/bot/getcookie.py b/repo/dockerbot/jbot/bot/getcookie.py new file mode 100644 index 00000000..4a201da2 --- /dev/null +++ b/repo/dockerbot/jbot/bot/getcookie.py @@ -0,0 +1,176 @@ +from telethon import events, Button +import requests +import re +import time +import qrcode +from asyncio import exceptions +from .. import jdbot, chat_id, img_file +from .utils import press_event + +cookiemsg = '' +# 扫码获取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' + } + resp = requests.get(url=get_url, headers=get_header) + parseGetRespCookie(resp.headers, resp.json()) + + +def parseGetRespCookie(headers, get_resp): + global s_token + global cookies + s_token = get_resp.get('s_token') + set_cookies = headers.get('set-cookie') + 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}; " + + +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()) + except Exception as error: + print("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] + + +def parseJDCookies(headers): + global jd_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] + 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) + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/getcookie')) +async def mycookie(event): + '''接收/getcookie后执行程序''' + login = True + msg = await jdbot.send_message(chat_id, '正在获取二维码,请稍后') + global cookiemsg + try: + SENDER = event.sender_id + async with jdbot.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 jdbot.delete_messages(chat_id, msg) + cookiemsg = await jdbot.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 jdbot.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 jdbot.delete_messages(chat_id, cookiemsg) + await jdbot.send_message(chat_id, '以下为获取到的cookie') + await jdbot.send_message(chat_id, jd_cookie) + return + if data.get("errcode") == 21: + await jdbot.delete_messages(chat_id, cookiemsg) + await jdbot.send_message(chat_id, '发生了某些错误\n'+data.get("errcode")) + return + if time.time() > expired_time: + await jdbot.delete_messages(chat_id, cookiemsg) + await jdbot.send_message(chat_id, '超过3分钟未扫码,二维码已过期') + return + except Exception as e: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) diff --git a/repo/dockerbot/jbot/bot/getfile.py b/repo/dockerbot/jbot/bot/getfile.py new file mode 100644 index 00000000..b76e549f --- /dev/null +++ b/repo/dockerbot/jbot/bot/getfile.py @@ -0,0 +1,62 @@ +from telethon import events, Button +import subprocess +from asyncio import exceptions +from .. import jdbot, chat_id, _ScriptsDir, _ConfigDir, logger +from .utils import press_event, backfile, _DiyDir, jdcmd, V4 + + +@jdbot.on(events.NewMessage(from_users=chat_id)) +async def myfile(event): + '''定义文件操作''' + try: + v4btn = [[Button.inline('放入config', data=_ConfigDir), Button.inline('放入scripts', data=_ScriptsDir), Button.inline('放入OWN文件夹', data=_DiyDir)], [ + Button.inline('放入scripts并运行', data='node1'), Button.inline('放入OWN并运行', data='node'), Button.inline('取消', data='cancel')]] + btn = [[Button.inline('放入config', data=_ConfigDir), Button.inline('放入scripts', data=_ScriptsDir)], [ + Button.inline('放入scripts并运行', data='node1'), Button.inline('取消', data='cancel')]] + SENDER = event.sender_id + if event.message.file: + markup = [] + filename = event.message.file.name + async with jdbot.conversation(SENDER, timeout=30) as conv: + msg = await conv.send_message('请选择您要放入的文件夹或操作:\n') + if V4: + markup = v4btn + else: + markup = btn + msg = await jdbot.edit_message(msg, '请选择您要放入的文件夹或操作:', buttons=markup) + convdata = await conv.wait_event(press_event(SENDER)) + res = bytes.decode(convdata.data) + if res == 'cancel': + msg = await jdbot.edit_message(msg, '对话已取消') + conv.cancel() + elif res == 'node': + await backfile(_DiyDir+'/'+filename) + await jdbot.download_media(event.message, _DiyDir) + cmdtext = '{} {}/{} now'.format(jdcmd, _DiyDir, filename) + subprocess.Popen( + cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + await jdbot.edit_message(msg, '脚本已保存到DIY文件夹,并成功在后台运行,请稍后自行查看日志') + conv.cancel() + elif res == 'node1': + await backfile(_ScriptsDir+'/'+filename) + await jdbot.download_media(event.message, _ScriptsDir) + cmdtext = '{} {}/{} now'.format(jdcmd, + _ScriptsDir, filename) + subprocess.Popen( + cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + await jdbot.edit_message(msg, '脚本已保存到scripts文件夹,并成功在后台运行,请稍后自行查看日志') + conv.cancel() + else: + await backfile(res+'/'+filename) + await jdbot.download_media(event.message, res) + await jdbot.edit_message(msg, filename+'已保存到'+res+'文件夹') + if filename == 'crontab.list' and V4: + cmdtext = 'crontab '+res+'/'+filename + subprocess.Popen( + cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + await jdbot.edit_message(msg, '定时文件已保存,并更新') + except exceptions.TimeoutError: + msg = await jdbot.send_message(chat_id, '选择已超时,对话已停止') + except Exception as e: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry\n'+str(e)) diff --git a/repo/dockerbot/jbot/bot/help.py b/repo/dockerbot/jbot/bot/help.py new file mode 100644 index 00000000..8c53b685 --- /dev/null +++ b/repo/dockerbot/jbot/bot/help.py @@ -0,0 +1,35 @@ +from telethon import events +from .. import jdbot, chat_id + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern='^/help')) +async def myhelp(event): + '''接收/help命令后执行程序''' + if len(event.raw_text) > 6: + text = event.raw_text.replace('/help ', '') + else: + text = 'mhelp' + mhelp = ''' +a-我的自定义快捷按钮 +bean-获取收支 +edit-编辑文件 +start-开始使用本程序 +node-执行js脚本文件,绝对路径。 +cmd-执行cmd命令 +snode-选择脚本后台运行 +log-选择日志 +getfile-获取jd目录下文件 +setshort-设置自定义按钮 +getcookie-扫码获取cookie''' + bean = '/bean 加数字,获取该账户近期收支情况\n/bean in\out获取所有账户近期收或支情况\n/bean 获取账户总豆数量' + cmd = '/cmd用于执行cmd命令,如果命令持续10分钟仍未结束,将强行终止,以保障机器人响应' + edit = '/edit 进入/jd目录选择文件进行编辑,仅限简易编辑\n/edit /jd/config进入config目录选择文件编辑\n/edit /jd/config/config.sh 直接编辑config.sh文件' + getcookie = '/getcookie 扫码获取jdcookie' + node = '/node 用于执行js脚本 用法:\n/node /jd/own/abc/def.js' + getfile = '/getfile 进入/jd目录选择文件进行获取\n/getfile /jd/config进入config目录选择文件获取\n/getfile /jd/config/config.sh 直接获取config.sh文件' + setshort = '/setshort 用于设置快捷方式,格式如下:\n更新-->jup\nAAA-->BBB这种格式使用/a选择\n/bean 1\n/edit /jd/config/config.sh\n以“/”开头的为机器人命令快捷,使用/b选择' + snode = '/snode 选择脚本并运行' + chart = '' + helpme = {'bean': bean, 'cmd': cmd, 'edit': edit, 'getcookie': getcookie, 'node': node, + 'getfile': getfile, 'setshort': setshort, 'snode': snode, 'chart': chart,'mhelp':mhelp} + await jdbot.send_message(chat_id, helpme[text]) diff --git a/repo/dockerbot/jbot/bot/node.py b/repo/dockerbot/jbot/bot/node.py new file mode 100644 index 00000000..af9ab809 --- /dev/null +++ b/repo/dockerbot/jbot/bot/node.py @@ -0,0 +1,19 @@ +from telethon import events +import re +from .. import jdbot, chat_id +from .utils import cmd, jdcmd + + +@jdbot.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 /own/abc.js 运行own/abc.js脚本 + ''' + await jdbot.send_message(chat_id, res) + else: + await cmd('{} {} now'.format(jdcmd, text[0].replace('/node ', ''))) diff --git a/repo/dockerbot/jbot/bot/quickchart.py b/repo/dockerbot/jbot/bot/quickchart.py new file mode 100644 index 00000000..80a8cef0 --- /dev/null +++ b/repo/dockerbot/jbot/bot/quickchart.py @@ -0,0 +1,110 @@ +"""A python client for quickchart.io, a web service that generates static +charts.""" + +import datetime +import json +import re +try: + from urllib import urlencode +except: + # For Python 3 + from urllib.parse import urlencode + +FUNCTION_DELIMITER_RE = re.compile('\"__BEGINFUNCTION__(.*?)__ENDFUNCTION__\"') + + +class QuickChartFunction: + def __init__(self, script): + self.script = script + + def __repr__(self): + return self.script + + +def serialize(obj): + if isinstance(obj, QuickChartFunction): + return '__BEGINFUNCTION__' + obj.script + '__ENDFUNCTION__' + if isinstance(obj, (datetime.date, datetime.datetime)): + return obj.isoformat() + return obj.__dict__ + + +def dump_json(obj): + ret = json.dumps(obj, default=serialize, separators=(',', ':')) + ret = FUNCTION_DELIMITER_RE.sub( + lambda match: json.loads('"' + match.group(1) + '"'), ret) + return ret + + +class QuickChart: + def __init__(self): + self.config = None + self.width = 500 + self.height = 300 + self.background_color = '#ffffff' + self.device_pixel_ratio = 1.0 + self.format = 'png' + self.key = None + self.scheme = 'https' + self.host = 'quickchart.io' + + def is_valid(self): + return self.config is not None + + def get_url_base(self): + return '%s://%s' % (self.scheme, self.host) + + def get_url(self): + if not self.is_valid(): + raise RuntimeError( + 'You must set the `config` attribute before generating a url') + params = { + 'c': dump_json(self.config) if type(self.config) == dict else self.config, + 'w': self.width, + 'h': self.height, + 'bkg': self.background_color, + 'devicePixelRatio': self.device_pixel_ratio, + 'f': self.format, + } + if self.key: + params['key'] = self.key + return '%s/chart?%s' % (self.get_url_base(), urlencode(params)) + + def _post(self, url): + try: + import requests + except: + raise RuntimeError('Could not find `requests` dependency') + + postdata = { + 'chart': dump_json(self.config) if type(self.config) == dict else self.config, + 'width': self.width, + 'height': self.height, + 'backgroundColor': self.background_color, + 'devicePixelRatio': self.device_pixel_ratio, + 'format': self.format, + } + if self.key: + postdata['key'] = self.key + resp = requests.post(url, json=postdata) + if resp.status_code != 200: + raise RuntimeError( + 'Invalid response code from chart creation endpoint') + return resp + + def get_short_url(self): + resp = self._post('%s/chart/create' % self.get_url_base()) + parsed = json.loads(resp.text) + if not parsed['success']: + raise RuntimeError( + 'Failure response status from chart creation endpoint') + return parsed['url'] + + def get_bytes(self): + resp = self._post('%s/chart' % self.get_url_base()) + return resp.content + + def to_file(self, path): + content = self.get_bytes() + with open(path, 'wb') as f: + f.write(content) diff --git a/repo/dockerbot/jbot/bot/sendfile.py b/repo/dockerbot/jbot/bot/sendfile.py new file mode 100644 index 00000000..e81da875 --- /dev/null +++ b/repo/dockerbot/jbot/bot/sendfile.py @@ -0,0 +1,43 @@ +from telethon import events +from .. import jdbot, chat_id, _LogDir, _JdDir +from .utils import logbtn +import os + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/log')) +async def mylog(event): + '''定义日志文件操作''' + SENDER = event.sender_id + path = _LogDir + page = 0 + filelist = None + async with jdbot.conversation(SENDER, timeout=60) as conv: + msg = await conv.send_message('正在查询,请稍后') + while path: + path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile')) +async def mygetfile(event): + '''定义获取文件命令''' + SENDER = event.sender_id + path = _JdDir + page = 0 + if len(event.raw_text.split(' ')) > 1: + text = event.raw_text.replace('/getfile ','') + else: + text =None + if text and os.path.isfile(text): + await jdbot.send_message(chat_id, '请查收文件',file=text) + return + elif text and os.path.isdir(text): + path = text + filelist = None + elif text: + await jdbot.send_message(chat_id, 'please marksure it\'s a dir or a file') + filelist = None + else: + filelist = None + async with jdbot.conversation(SENDER, timeout=60) as conv: + msg = await conv.send_message('正在查询,请稍后') + while path: + path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) diff --git a/repo/dockerbot/jbot/bot/setshort.py b/repo/dockerbot/jbot/bot/setshort.py new file mode 100644 index 00000000..e81c5de2 --- /dev/null +++ b/repo/dockerbot/jbot/bot/setshort.py @@ -0,0 +1,15 @@ +from telethon import events +from .. import jdbot, chat_id, _shortcut + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/setshort$')) +async def setshortcut(event): + SENDER = event.sender_id + async with jdbot.conversation(SENDER, timeout=60) as conv: + await conv.send_message( + '60s内回复有效\n请按格式输入您的快捷命令。例如:\n京豆通知-->jtask jd_bean_change\n更新脚本-->jup\n获取互助码-->jcode\nnode运行XX脚本-->node /XX/XX.js\nbash运行abc/123.sh脚本-->bash /abc/123.sh\n-->前边为要显示的名字,-->后边为要运行的命令\n 如添加运行脚本立即执行命令记得在后边添加now\n如不等待运行结果请添加nohup,如京豆通知-->nohup jtask jd_bean_change now\n如不添加nohup 会等待程序执行完,期间不能交互\n建议运行时间短命令不添加nohup\n部分功能青龙可能不支持,请自行测试,自行设定 ') + shortcut = await conv.get_response() + with open(_shortcut, 'w+', encoding='utf-8') as f: + f.write(shortcut.raw_text) + await conv.send_message('已设置成功可通过"/a"使用') + conv.cancel() diff --git a/repo/dockerbot/jbot/bot/short.py b/repo/dockerbot/jbot/bot/short.py new file mode 100644 index 00000000..73e24e71 --- /dev/null +++ b/repo/dockerbot/jbot/bot/short.py @@ -0,0 +1,57 @@ +from telethon import events, Button +from .utils import split_list, press_event, cmd +import subprocess +from asyncio import exceptions +from .. import jdbot, chat_id, _shortcut, logger + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/a$')) +async def shortcut(event): + markup = [] + SENDER = event.sender_id + msg = await jdbot.send_message(chat_id, '正在查询您的常用命令,请稍后') + with open(_shortcut, 'r', encoding='utf-8') as f: + shortcuts = f.readlines() + try: + async with jdbot.conversation(SENDER, timeout=60) as conv: + markup = [Button.inline(shortcut.split( + '-->')[0], data=str(shortcut.split('-->')[-1])) for shortcut in shortcuts if '-->' in shortcut] + markup = split_list(markup, 3) + markup.append([Button.inline('取消', data='cancel')]) + msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=markup) + convdata = await conv.wait_event(press_event(SENDER)) + res = bytes.decode(convdata.data) + if res == 'cancel': + msg = await jdbot.edit_message(msg, '对话已取消') + conv.cancel() + elif 'nohup ' in res: + msg = await jdbot.edit_message(msg, '即将执行您的操作'+res) + cmdtext = res.replace('nohup ', '') + subprocess.Popen( + cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + msg = await jdbot.edit_message(msg, '已在后台执行您的操作'+res.replace('nohup ', '')) + conv.cancel() + else: + await jdbot.delete_messages(chat_id, msg) + await cmd(res) + conv.cancel() + except exceptions.TimeoutError: + msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') + except Exception as e: + await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry\n'+str(e)) + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/b$')) +async def shortcut(event): + markup = [] + msg = await jdbot.send_message(chat_id, '正在查询您的常用命令,请稍后') + with open(_shortcut, 'r', encoding='utf-8') as f: + shortcuts = f.readlines() + try: + await jdbot.delete_messages(chat_id,msg) + markup = [Button.text(shortcut,single_use=True) for shortcut in shortcuts if '-->' not in shortcut] + markup = split_list(markup, 3) + await jdbot.send_message(chat_id, '请做出您的选择:', buttons=markup) + except Exception as e: + await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry\n'+str(e)) diff --git a/repo/dockerbot/jbot/bot/snode.py b/repo/dockerbot/jbot/bot/snode.py new file mode 100644 index 00000000..26ead770 --- /dev/null +++ b/repo/dockerbot/jbot/bot/snode.py @@ -0,0 +1,18 @@ +from telethon import events +from .. import jdbot, chat_id, _JdDir +from .utils import cmd, nodebtn + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode')) +async def mysnode(event): + '''定义supernode文件命令''' + SENDER = event.sender_id + path = _JdDir + page = 0 + filelist = None + async with jdbot.conversation(SENDER, timeout=60) as conv: + msg = await conv.send_message('正在查询,请稍后') + while path: + path, msg, page, filelist = await nodebtn(conv, SENDER, path, msg, page, filelist) + if filelist and filelist.startswith('CMD-->'): + await cmd(filelist.replace('CMD-->', '')) diff --git a/repo/dockerbot/jbot/bot/start.py b/repo/dockerbot/jbot/bot/start.py new file mode 100644 index 00000000..b7ebc3c6 --- /dev/null +++ b/repo/dockerbot/jbot/bot/start.py @@ -0,0 +1,21 @@ +from telethon import events +from .. import jdbot, chat_id + + +@jdbot.on(events.NewMessage(from_users=chat_id, pattern='/start')) +async def mystart(event): + '''接收/start命令后执行程序''' + msg = '''使用方法如下: + /help 获取命令,可直接发送至botfather + /a 使用你的自定义快捷按钮 + /start 开始使用本程序 + /node 执行js脚本文件,直接输入/node jd_bean_change 如执行其他自己js,需输入绝对路径。即可进行执行。该命令会等待脚本执行完,期间不能使用机器人,建议使用snode命令。 + /cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 不建议使用机器人使用并发,可能产生不明原因的崩溃 + /snode 命令可以选择脚本执行,只能选择/scripts 和/own目录下的脚本,选择完后直接后台运行,不影响机器人响应其他命令 + /log 选择查看执行日志 + /getfile 获取jd目录下文件 + /setshort 设置自定义按钮,每次设置会覆盖原设置 + /getcookie 扫码获取cookie 增加30s内取消按钮,30s后不能进行其他交互直到2分钟或获取到cookie + /edit 从jd目录下选择文件编辑,需要将编辑好信息全部发给机器人,机器人会根据你发的信息进行替换。建议用来编辑config或crontab.list 其他文件慎用!!! + 此外直接发送文件,会让您选择保存到哪个文件夹,如果选择运行,将保存至own目录下,并立即运行脚本,crontab.list文件会自动更新时间''' + await jdbot.send_message(chat_id, msg) diff --git a/repo/dockerbot/jbot/bot/update.py b/repo/dockerbot/jbot/bot/update.py new file mode 100644 index 00000000..64713022 --- /dev/null +++ b/repo/dockerbot/jbot/bot/update.py @@ -0,0 +1,10 @@ +version = 'version :0.3.5' +botlog = ''' +**2021年5月18日** +本次更新内容如下: +- 修改机器人断网重连 + - 默认断网自动重连 + - 如修改请在bot.json内添加括号内内容【```"noretry":true```】 + - 添加上述内容后,机器人断开连接重连5次,5次不能连接成功将自动结束,如需重新启用需通过终端运行```pm2 start jbot``` +- 修正bean与chart命令 账号不一致问题 +''' \ No newline at end of file diff --git a/repo/dockerbot/jbot/bot/utils.py b/repo/dockerbot/jbot/bot/utils.py new file mode 100644 index 00000000..dc0f2592 --- /dev/null +++ b/repo/dockerbot/jbot/bot/utils.py @@ -0,0 +1,258 @@ +import os +from telethon import events, Button +import re +from .. import jdbot, chat_id, _LogDir, logger, _JdDir, _OwnDir, _ConfigDir +import asyncio +import datetime + +bean_log = _LogDir + '/jd_bean_change/' +_ConfigFile = _ConfigDir+'/config.sh' +V4, QL = False, False +if 'JD_DIR' in os.environ.keys(): + V4 = True + _ConfigFile = _ConfigDir+'/config.sh' + _DiyDir = _OwnDir + jdcmd = 'jtask' +elif 'QL_DIR' in os.environ.keys(): + QL = True + _ConfigFile = _ConfigDir+'/cookie.sh' + _DiyDir = None + jdcmd = 'task' + dirs = os.listdir(_LogDir) + for mydir in dirs: + if 'jd_bean_change' in mydir: + bean_log = _LogDir + '/' + mydir + break +else: + _DiyDir = None + jdcmd = 'node' + +ckreg = re.compile(r'pt_key=\S*;pt_pin=\S*;') +with open(_ConfigFile, 'r', encoding='utf-8') as f: + lines = f.read() +cookies = ckreg.findall(lines) +for ck in cookies: + if ck == 'pt_key=xxxxxxxxxx;pt_pin=xxxx;': + cookies.remove(ck) + break + + +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 backfile(file): + '''如果文件存在,则备份,并更新''' + if os.path.exists(file): + try: + os.rename(file, file+'.bak') + except WindowsError: + os.remove(file+'.bak') + os.rename(file, file+'.bak') + + +def press_event(user_id): + return events.CallbackQuery(func=lambda e: e.sender_id == user_id) + + +async def cmd(cmdtext): + '''定义执行cmd命令''' + try: + msg = await jdbot.send_message(chat_id, '开始执行命令') + p = await asyncio.create_subprocess_shell( + cmdtext, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + res_bytes, res_err = await p.communicate() + res = res_bytes.decode('utf-8') + if len(res) == 0: + await jdbot.edit_message(msg, '已执行,但返回值为空') + elif len(res) <= 4000: + await jdbot.delete_messages(chat_id, msg) + await jdbot.send_message(chat_id, res) + elif len(res) > 4000: + _log = _LogDir + '/bot/'+cmdtext.split('/')[-1].split( + '.js')[0]+datetime.datetime.now().strftime('%H-%M-%S')+'.log' + with open(_log, 'w+', encoding='utf-8') as f: + f.write(res) + await jdbot.delete_messages(chat_id, msg) + await jdbot.send_message(chat_id, '执行结果较长,请查看日志', file=_log) + os.remove(_log) + except Exception as e: + await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry'+str(e)) + + +async def getname(path, dir): + '''获取文件中文名称,如无则返回文件名''' + names = [] + reg = r'new Env\(\'[\S]+?\'\)' + cname = False + for file in dir: + if os.path.isdir(path+'/'+file): + names.append(file) + elif file.endswith('.js') and file != 'jdCookie.js' and file != 'getJDCookie.js' and file != 'JD_extra_cookie.js' and 'ShareCode' not in file: + with open(path+'/'+file, 'r', encoding='utf-8') as f: + resdatas = f.readlines() + for data in resdatas: + if 'new Env' in data: + data = data.replace('\"', '\'') + res = re.findall(reg, data) + if len(res) != 0: + res = res[0].split('\'')[-2] + names.append(res+'--->'+file) + cname = True + break + if not cname: + names.append(file+'--->'+file) + cname = False + else: + continue + return names + + +async def logbtn(conv, SENDER, path, msg, page, filelist): + '''定义log日志按钮''' + mybtn = [Button.inline('上一页', data='up'), Button.inline( + '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] + try: + if filelist: + markup = filelist + newmarkup = markup[page] + if mybtn not in newmarkup: + newmarkup.append(mybtn) + else: + dir = os.listdir(path) + dir.sort() + markup = [Button.inline(file, data=str(file)) + for file in dir] + markup = split_list(markup, 3) + if len(markup) > 30: + markup = split_list(markup, 30) + newmarkup = markup[page] + newmarkup.append(mybtn) + else: + newmarkup = markup + if path == _JdDir: + newmarkup.append([Button.inline('取消', data='cancel')]) + else: + newmarkup.append( + [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) + msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) + convdata = await conv.wait_event(press_event(SENDER)) + res = bytes.decode(convdata.data) + if res == 'cancel': + msg = await jdbot.edit_message(msg, '对话已取消') + conv.cancel() + return None, None, None, None + elif res == 'next': + page = page + 1 + if page > len(markup) - 1: + page = 0 + return path, msg, page, markup + elif res == 'up': + page = page - 1 + if page < 0: + page = len(markup) - 1 + return path, msg, page, markup + elif res == 'updir': + path = '/'.join(path.split('/')[:-1]) + logger.info(path) + if path == '': + path = _JdDir + return path, msg, page, None + elif os.path.isfile(path+'/'+res): + msg = await jdbot.edit_message(msg, '文件发送中,请注意查收') + await conv.send_file(path+'/'+res) + msg = await jdbot.edit_message(msg, res+'发送成功,请查收') + conv.cancel() + return None, None, None, None + else: + return path+'/'+res, msg, page, None + except asyncio.exceptions.TimeoutError: + msg = await jdbot.edit_message(msg, '选择已超时,本次对话已停止') + return None, None, None, None + except Exception as e: + msg = await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry\n'+str(e)) + return None, None, None, None + + +async def nodebtn(conv, SENDER, path, msg, page, filelist): + '''定义scripts脚本按钮''' + mybtn = [Button.inline('上一页', data='up'), Button.inline( + '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] + try: + if filelist: + markup = filelist + newmarkup = markup[page] + if mybtn not in newmarkup: + newmarkup.append(mybtn) + else: + if path == _JdDir and V4: + dir = ['scripts', _OwnDir.split('/')[-1]] + elif path == _JdDir and QL: + dir = ['scripts'] + else: + dir = os.listdir(path) + dir = await getname(path, dir) + dir.sort() + markup = [Button.inline(file.split('--->')[0], data=str(file.split('--->')[-1])) + for file in dir if os.path.isdir(path+'/'+file) or file.endswith('.js')] + markup = split_list(markup, 3) + if len(markup) > 30: + markup = split_list(markup, 30) + newmarkup = markup[page] + newmarkup.append(mybtn) + else: + newmarkup = markup + if path == _JdDir: + newmarkup.append([Button.inline('取消', data='cancel')]) + else: + newmarkup.append( + [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) + msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) + convdata = await conv.wait_event(press_event(SENDER)) + res = bytes.decode(convdata.data) + if res == 'cancel': + msg = await jdbot.edit_message(msg, '对话已取消') + conv.cancel() + return None, None, None, None + elif res == 'next': + page = page + 1 + if page > len(markup) - 1: + page = 0 + return path, msg, page, markup + elif res == 'up': + page = page - 1 + if page < 0: + page = len(markup) - 1 + return path, msg, page, markup + elif res == 'updir': + path = '/'.join(path.split('/')[:-1]) + if path == '': + path = _JdDir + return path, msg, page, None + elif os.path.isfile(path+'/'+res): + conv.cancel() + logger.info(path+'/'+res+'脚本即将在后台运行') + msg = await jdbot.edit_message(msg, res + '在后台运行成功') + cmdtext = '{} {}/{} now'.format(jdcmd, path, res) + return None, None, None, 'CMD-->'+cmdtext + else: + return path+'/'+res, msg, page, None + except asyncio.exceptions.TimeoutError: + msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') + return None, None, None, None + except Exception as e: + msg = await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) + logger.error('something wrong,I\'m sorry\n'+str(e)) + return None, None, None, None diff --git a/repo/dockerbot/jbot/diy/example.py b/repo/dockerbot/jbot/diy/example.py new file mode 100644 index 00000000..e9cafc1a --- /dev/null +++ b/repo/dockerbot/jbot/diy/example.py @@ -0,0 +1,10 @@ +#引入库文件,基于telethon +from telethon import events +#从上级目录引入 jdbot,chat_id变量 +from .. import jdbot,chat_id +#格式基本固定,本例子表示从chat_id处接收到包含hello消息后,要做的事情 +@jdbot.on(events.NewMessage(chats=chat_id,pattern=('hello'))) +#定义自己的函数名称 +async def hi(event): + #do something + await jdbot.send_message(chat_id,'hello') \ No newline at end of file diff --git a/repo/dockerbot/jbot/ecosystem.config.js b/repo/dockerbot/jbot/ecosystem.config.js new file mode 100644 index 00000000..4ca9565a --- /dev/null +++ b/repo/dockerbot/jbot/ecosystem.config.js @@ -0,0 +1,22 @@ +module.exports = { + apps: [{ + name: "jbot", + version: "0.3.5", + cwd: "..", + script: "python", + args: "-m jbot", + autorestart: true, + watch: ["jbot"], + ignore_watch: [ + "jbot/__pycache__/*", + "jbot/bot/__pycache__/*", + "jbot/diy/__pycache__/*", + "jbot/*.log", + "jbot/*/*.log", + "jbot/requirements.txt", + "jbot/ecosystem.config.js" + ], + watch_delay: 15000, + interpreter: "" + }] +} \ No newline at end of file diff --git a/repo/dockerbot/jbot/font/jet.ttf b/repo/dockerbot/jbot/font/jet.ttf new file mode 100644 index 00000000..0cd1cb66 Binary files /dev/null and b/repo/dockerbot/jbot/font/jet.ttf differ diff --git a/repo/dockerbot/jbot/requirements.txt b/repo/dockerbot/jbot/requirements.txt new file mode 100644 index 00000000..ba13f3d1 --- /dev/null +++ b/repo/dockerbot/jbot/requirements.txt @@ -0,0 +1,7 @@ +qrcode==6.1 +Telethon==1.21.1 +requests==2.25.1 +Pillow==8.1.2 +python-socks==1.2.4 +async_timeout==3.0.1 +prettytable>=2.1.0 \ No newline at end of file diff --git a/repo/dockerbot/jbot/utils.py b/repo/dockerbot/jbot/utils.py new file mode 100644 index 00000000..ed2d2843 --- /dev/null +++ b/repo/dockerbot/jbot/utils.py @@ -0,0 +1,22 @@ +import sys +import importlib +import os +from . import logger + + +def load_diy(module, path): + files = os.listdir(path) + for file in files: + try: + if file.endswith('.py'): + filename = file.replace('.py', '') + name = "jbot.{}.{}".format(module, filename) + spec = importlib.util.spec_from_file_location(name, path+file) + load = importlib.util.module_from_spec(spec) + spec.loader.exec_module(load) + sys.modules[f"jbot.{module}.{filename}"] = load + logger.info("JBot加载 " + filename+" 完成") + except Exception as e: + logger.info("JBot加载失败"+str(e)) + continue +