├── README.md ├── bot.json ├── botV3.py ├── botV4.py ├── botql.py ├── config └── bot.json ├── jbot ├── __init__.py ├── __main__.py ├── bot │ ├── bean.py │ ├── beandata.py │ ├── chart.py │ ├── cmd.py │ ├── editfile.py │ ├── getcookie.py │ ├── getfile.py │ ├── help.py │ ├── node.py │ ├── quickchart.py │ ├── sendfile.py │ ├── setshort.py │ ├── short.py │ ├── snode.py │ ├── start.py │ ├── update.py │ └── utils.py ├── diy │ └── example.py ├── ecosystem.config.js ├── font │ └── jet.ttf ├── requirements.txt └── utils.py ├── rebotV3.sh ├── rebotV4.sh ├── rebotql.sh └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # 去my.telegram.org获取api_id api_hash千万不要点错成delete账户!!!! 2 | - 刚开始学习使用GITHUB,我是一个菜鸟 3 | - 同样的也是刚开始学习PYTHON 4 | - ~~尝试使用python写一个基于E大的dockerV3的机器人交互~~ 5 | - 最新版本为jbot文件夹,以后只更新此文件,欢迎大佬pr 6 | *** 7 | - BUG漫天飞 8 | - MAIKA永相随 9 | *** 10 | ## 使用方法: 11 | - 使用方法 12 | - ~~将bot.py、bot.json、rebot.sh放入/jd/config文件夹下(旧版本使用方法)~~ 13 | - 在docker内执行`apk add python3` 14 | - 如需扫码获取cookie及获取图片 需执行`apk add zlib-dev gcc jpeg-dev python3-dev musl-dev freetype-dev` 15 | - 由于需要安装多个依赖包,建议将清华源设置为默认源`pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple` 16 | - 执行`pip3 install telethon python-socks[asyncio] pillow qrcode requests prettytable` 17 | - 或者下载requirements.txt `pip3 install -r requirements.txt` 18 | - ~~rebot.sh 用于杀死原bot进程,后台启动新进程,建议直接环境搭建好后直接 `bash /jd/config/rebot.sh`~~ 19 | - 下载jbot文件夹 放在、/jd或/ql目录下,下载config/bot.json放在config下,在jd或ql目录下运行 `nohup python3 -m jbot >/dev/null 2>&1 &` 20 | - 如果需要更换机器人token,需要将bot.session删除后,重新运行 ~~`bash /jd/config/rebot.sh`~~ 21 | *** 22 | ## 主要实现功能: 23 | - 主要功能 24 | - /a 使用你的自定义快捷按钮 25 | - /start 开始使用本程序 26 | - /help 获取命令,可直接发送至botfather 27 | - /bash 执行bash程序,如git_pull、diy及可执行自定义.sh,例如/bash /jd/config/abcd.sh 28 | - /node 执行js脚本文件,目前仅支持/scirpts、/config目录下js,直接输入/node jd_bean_change 即可进行执行。该命令会等待脚本执行完,期间不能使用机器人,建议使用snode命令。 29 | - /cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 30 | - /snode 命令可以选择脚本执行,只能选择/jd/scripts目录下的脚本,选择完后直接后台运行,不影响机器人响应其他命令 31 | - /log 选择查看执行日志 32 | - /getfile 获取/jd目录下文件 33 | - /setshort 设置自定义按钮,每次设置会覆盖原设置 34 | - /getcookie 扫码获取cookie 35 | - 此外直接发送文件,会让你选择保存到哪个文件夹,如果选择运行,将保存至scripts或者own目录下,并立即运行脚本crontab.list文件会自动更新时间 36 | ## todo 37 | - todo: 38 | - ~~snode忽略非js文件,由于tg最大支持100个按钮,需要进行排除非js文件~~ 已完成 39 | - ~~V4更新了,还没来得及看,后期新增~~ V4版本已更新 40 | - ~~扫码获取cookie~~ 采用lof大佬方案 41 | - ~~上一页下一页功能~~ 已完成 42 | - 有错误请留言,有需要增加功能的,我可以尝试写 43 | - 初次新增青龙bot,仅支持基础设置,青龙特性尚未研究,后续可能会更新 44 | -------------------------------------------------------------------------------- /bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "//开头的的都是注释,不要动,剩下的都按要求改自己的", 3 | "//user_id": "↓↓↓ 你的USERID,去除双引号 ↓↓↓", 4 | "user_id": 123456789, 5 | "//bot_token": "↓↓↓ 你的机器人TOKEN ↓↓↓", 6 | "bot_token": "123456789:ABCDEFGSHSFDASDFAD", 7 | "//api_id": "↓↓↓ https://my.telegram.org 在该网站申请到的id ↓↓↓", 8 | "api_id": "456423156", 9 | "//api_hash": "↓↓↓ https://my.telegram.org 在该网站申请到的hash ↓↓↓", 10 | "api_hash": "ASDFAWEFADSFAWEFDSFASFD", 11 | "//proxy": "↓↓↓ 使用代理改成true,不使用下方带proxy的不用动 ↓↓↓", 12 | "proxy": false, 13 | "//proxy_type": "↓↓↓ socks5 或者 http 或者 MTProxy ↓↓↓", 14 | "proxy_type": "socks5", 15 | "//proxy_add": "↓↓↓ 代理IP地址例如:192.168.99.100 ↓↓↓", 16 | "proxy_add": "192.168.99.100", 17 | "//proxy_port": "↓↓↓ 代理端口,不需要双引号例如 5890 ↓↓↓", 18 | "proxy_port": 5890, 19 | "//proxy_secret": "↓↓↓ MTProxy代理秘钥 ↓↓↓", 20 | "proxy_secret": "", 21 | "//proxy_user": "↓↓↓ 代理的username,有就改,没有就不要动 ↓↓↓", 22 | "proxy_user": "代理的username,有则填写,无则不用动", 23 | "//proxy_password": "↓↓↓ 代理的密码,有则填写,无则不用动 ↓↓↓", 24 | "proxy_password": "代理的密码,有则填写,无则不用动", 25 | "//StartCMD": "↓↓↓ 是否开启CMD命令,开启改成true ↓↓↓", 26 | "StartCMD": false, 27 | "//noretry": "↓↓↓ 是否 关闭 bot掉线重连,默认开启,关闭改成true ↓↓↓", 28 | "noretry": false 29 | } 30 | -------------------------------------------------------------------------------- /botV3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | # time: 2021 4 | # newtime: 2021-04-25 5 | # version: 0.1.7 6 | # log: 新增:连接成功,机器人发送通知,修复:没有中文名称脚本不能识别 7 | # author: https://github.com/SuMaiKaDe 8 | 9 | from telethon import TelegramClient, events, Button, connection 10 | import requests 11 | import re 12 | import json 13 | import time 14 | import os 15 | import qrcode 16 | import logging 17 | import subprocess 18 | import shutil 19 | from asyncio import exceptions 20 | logging.basicConfig( 21 | format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO) 22 | logger = logging.getLogger(__name__) 23 | # 定义目录,以bot.py所在目录上级目录为/jd或/ql文件夹 24 | _JdDir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 25 | _ConfigDir = _JdDir + '/config' 26 | _ScriptsDir = _JdDir + '/scripts' 27 | _LogDir = _JdDir + '/log' 28 | _shortcut = _ConfigDir + '/shortcut.list' 29 | _bot = _ConfigDir + '/bot.json' 30 | _qr = _ConfigDir + 'qr.jpg' 31 | # 频道id/用户id 32 | with open(_bot, 'r', encoding='utf-8') as f: 33 | bot = json.load(f) 34 | chat_id = int(bot['user_id']) 35 | # 机器人 TOKEN 36 | TOKEN = bot['bot_token'] 37 | # 发消息的TG代理 38 | # my.telegram.org申请到的api_id,api_hash 39 | api_id = bot['api_id'] 40 | api_hash = bot['api_hash'] 41 | proxystart = bot['proxy'] 42 | proxyType = bot['proxy_type'] 43 | connectionType = connection.ConnectionTcpMTProxyRandomizedIntermediate if proxyType == "MTProxy" else connection.ConnectionTcpFull 44 | if 'proxy_user' in bot.keys() and bot['proxy_user'] != "代理的username,有则填写,无则不用动": 45 | proxy = { 46 | 'proxy_type': bot['proxy_type'], 47 | 'addr': bot['proxy_add'], 48 | 'port': bot['proxy_port'], 49 | 'username': bot['proxy_user'], 50 | 'password': bot['proxy_password']} 51 | elif proxyType == "MTProxy": 52 | proxy = (bot['proxy_add'], bot['proxy_port'], bot['proxy_secret']) 53 | else: 54 | proxy = (bot['proxy_type'], bot['proxy_add'], bot['proxy_port']) 55 | # 开启tg对话 56 | if proxystart: 57 | client = TelegramClient('bot', api_id, api_hash,connection=connectionType, 58 | proxy=proxy, connection_retries=None).start(bot_token=TOKEN) 59 | else: 60 | client = TelegramClient('bot', api_id, api_hash, 61 | connection_retries=None).start(bot_token=TOKEN) 62 | cookiemsg = '' 63 | img_file = _qr 64 | StartCMD = bot['StartCMD'] 65 | 66 | 67 | async def hello(): 68 | await client.send_message(chat_id, 'duang duang duang \n您的机器人已成功激活\n/start试试吧') 69 | 70 | 71 | def press_event(user_id): 72 | return events.CallbackQuery(func=lambda e: e.sender_id == user_id) 73 | 74 | 75 | # 扫码获取cookie 直接采用LOF大佬代码 76 | # getSToken请求获取,s_token用于发送post请求是的必须参数 77 | s_token = "" 78 | # getSToken请求获取,guid,lsid,lstoken用于组装cookies 79 | guid, lsid, lstoken = "", "", "" 80 | # 由上面参数组装生成,getOKLToken函数发送请求需要使用 81 | cookies = "" 82 | # getOKLToken请求获取,token用户生成二维码使用、okl_token用户检查扫码登录结果使用 83 | token, okl_token = "", "" 84 | # 最终获取到的可用的cookie 85 | jd_cookie = "" 86 | 87 | 88 | def getSToken(): 89 | time_stamp = int(time.time() * 1000) 90 | 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 91 | get_header = { 92 | 'Connection': 'Keep-Alive', 93 | 'Content-Type': 'application/x-www-form-urlencoded', 94 | 'Accept': 'application/json, text/plain, */*', 95 | 'Accept-Language': 'zh-cn', 96 | '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, 97 | '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', 98 | 'Host': 'plogin.m.jd.com' 99 | } 100 | try: 101 | resp = requests.get(url=get_url, headers=get_header) 102 | parseGetRespCookie(resp.headers, resp.json()) 103 | logger.info(resp.headers) 104 | logger.info(resp.json()) 105 | except Exception as error: 106 | logger.exception("Get网络请求异常", error) 107 | 108 | 109 | def parseGetRespCookie(headers, get_resp): 110 | global s_token 111 | global cookies 112 | s_token = get_resp.get('s_token') 113 | set_cookies = headers.get('set-cookie') 114 | logger.info(set_cookies) 115 | guid = re.findall(r"guid=(.+?);", set_cookies)[0] 116 | lsid = re.findall(r"lsid=(.+?);", set_cookies)[0] 117 | lstoken = re.findall(r"lstoken=(.+?);", set_cookies)[0] 118 | cookies = f"guid={guid}; lang=chs; lsid={lsid}; lstoken={lstoken}; " 119 | logger.info(cookies) 120 | 121 | 122 | def getOKLToken(): 123 | post_time_stamp = int(time.time() * 1000) 124 | post_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=%s&v=%s&remember=true' % ( 125 | s_token, post_time_stamp) 126 | post_data = { 127 | 'lang': 'chs', 128 | 'appid': 300, 129 | '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, 130 | 'source': 'wq_passport' 131 | } 132 | post_header = { 133 | 'Connection': 'Keep-Alive', 134 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 135 | 'Accept': 'application/json, text/plain, */*', 136 | 'Cookie': cookies, 137 | '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, 138 | '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', 139 | 'Host': 'plogin.m.jd.com', 140 | } 141 | try: 142 | global okl_token 143 | resp = requests.post( 144 | url=post_url, headers=post_header, data=post_data, timeout=20) 145 | parsePostRespCookie(resp.headers, resp.json()) 146 | logger.info(resp.headers) 147 | except Exception as error: 148 | logger.exception("Post网络请求错误", error) 149 | 150 | 151 | def parsePostRespCookie(headers, data): 152 | global token 153 | global okl_token 154 | token = data.get('token') 155 | okl_token = re.findall(r"okl_token=(.+?);", headers.get('set-cookie'))[0] 156 | logger.info("token:" + token) 157 | logger.info("okl_token:" + okl_token) 158 | 159 | 160 | def parseJDCookies(headers): 161 | global jd_cookie 162 | logger.info("扫码登录成功,下面为获取到的用户Cookie。") 163 | set_cookie = headers.get('Set-Cookie') 164 | pt_key = re.findall(r"pt_key=(.+?);", set_cookie)[0] 165 | pt_pin = re.findall(r"pt_pin=(.+?);", set_cookie)[0] 166 | logger.info(pt_key) 167 | logger.info(pt_pin) 168 | jd_cookie = f'pt_key={pt_key};pt_pin={pt_pin};' 169 | 170 | 171 | def creatqr(text): 172 | '''实例化QRCode生成qr对象''' 173 | qr = qrcode.QRCode( 174 | version=1, 175 | error_correction=qrcode.constants.ERROR_CORRECT_H, 176 | box_size=10, 177 | border=4 178 | ) 179 | qr.clear() 180 | # 传入数据 181 | qr.add_data(text) 182 | qr.make(fit=True) 183 | # 生成二维码 184 | img = qr.make_image() 185 | # 保存二维码 186 | img.save(img_file) 187 | 188 | 189 | def split_list(datas, n, row: bool = True): 190 | """一维列表转二维列表,根据N不同,生成不同级别的列表""" 191 | length = len(datas) 192 | size = length / n + 1 if length % n else length/n 193 | _datas = [] 194 | if not row: 195 | size, n = n, size 196 | for i in range(int(size)): 197 | start = int(i * n) 198 | end = int((i + 1) * n) 199 | _datas.append(datas[start:end]) 200 | return _datas 201 | 202 | 203 | async def backfile(file): 204 | if os.path.exists(file): 205 | try: 206 | os.rename(file, file+'.bak') 207 | except WindowsError: 208 | os.remove(file+'.bak') 209 | os.rename(file, file+'.bak') 210 | 211 | 212 | async def cmd(cmdtext): 213 | '''定义执行cmd命令''' 214 | try: 215 | msg = await client.send_message(chat_id, '开始执行程序,如程序复杂,建议稍等') 216 | res_bytes = subprocess.check_output( 217 | cmdtext, shell=True, stderr=subprocess.STDOUT) 218 | res = res_bytes.decode('utf-8') 219 | if len(res) == 0: 220 | await client.edit_message(msg, '已执行,但返回值为空') 221 | elif len(res) <= 4000: 222 | await client.edit_message(msg, res) 223 | elif len(res) > 4000: 224 | with open(_LogDir+'/botres.log', 'w+', encoding='utf-8') as f: 225 | f.write(res) 226 | await client.send_message(chat_id, '执行结果较长,请查看日志', file=_LogDir+'/botres.log') 227 | except Exception as e: 228 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 229 | logger.error('something wrong,I\'m sorry'+str(e)) 230 | 231 | 232 | async def logbtn(conv, SENDER, path, msg, page, filelist): 233 | '''定义log日志按钮''' 234 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 235 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 236 | try: 237 | if filelist: 238 | markup = filelist 239 | newmarkup = markup[page] 240 | if mybtn not in newmarkup: 241 | newmarkup.append(mybtn) 242 | else: 243 | dir = os.listdir(path) 244 | dir.sort() 245 | markup = [Button.inline(file, data=str(file)) 246 | for file in dir] 247 | markup = split_list(markup, 3) 248 | if len(markup) > 30: 249 | markup = split_list(markup, 30) 250 | newmarkup = markup[page] 251 | newmarkup.append(mybtn) 252 | else: 253 | newmarkup = markup 254 | if path == _JdDir: 255 | newmarkup.append([Button.inline('取消', data='cancel')]) 256 | else: 257 | newmarkup.append( 258 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 259 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 260 | convdata = await conv.wait_event(press_event(SENDER)) 261 | res = bytes.decode(convdata.data) 262 | if res == 'cancel': 263 | msg = await client.edit_message(msg, '对话已取消') 264 | conv.cancel() 265 | return None, None, None, None 266 | elif res == 'next': 267 | page = page + 1 268 | if page > len(markup) - 1: 269 | page = 0 270 | return path, msg, page, markup 271 | elif res == 'up': 272 | page = page - 1 273 | if page < 0: 274 | page = len(markup) - 1 275 | return path, msg, page, markup 276 | elif res == 'updir': 277 | path = '/'.join(path.split('/')[:-1]) 278 | logger.info(path) 279 | if path == '': 280 | path = _JdDir 281 | return path, msg, page, None 282 | elif os.path.isfile(path+'/'+res): 283 | msg = await client.edit_message(msg, '文件发送中,请注意查收') 284 | await conv.send_file(path+'/'+res) 285 | msg = await client.edit_message(msg, res+'发送成功,请查收') 286 | conv.cancel() 287 | return None, None, None, None 288 | else: 289 | return path+'/'+res, msg, page, None 290 | except exceptions.TimeoutError: 291 | msg = await client.edit_message(msg, '选择已超时,本次对话已停止') 292 | return None, None, None, None 293 | except Exception as e: 294 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 295 | logger.error('something wrong,I\'m sorry\n'+str(e)) 296 | return None, None, None, None 297 | 298 | 299 | async def getname(path, dir): 300 | names = [] 301 | reg = r'new Env\(\'[\S]+?\'\)' 302 | cname = False 303 | for file in dir: 304 | if os.path.isdir(path+'/'+file): 305 | names.append(file) 306 | elif file.endswith('.js') and file != 'jdCookie.js' and file != 'getJDCookie.js' and file != 'JD_extra_cookie.js': 307 | with open(path+'/'+file, 'r', encoding='utf-8') as f: 308 | resdatas = f.readlines() 309 | for data in resdatas: 310 | if 'new Env' in data: 311 | data = data.replace('\"', '\'') 312 | res = re.findall(reg, data) 313 | if len(res) != 0: 314 | res = res[0].split('\'')[-2] 315 | names.append(res+'--->'+file) 316 | cname = True 317 | break 318 | if not cname: 319 | names.append(file+'--->'+file) 320 | cname = False 321 | else: 322 | continue 323 | return names 324 | 325 | 326 | async def nodebtn(conv, SENDER, path, msg, page, filelist): 327 | '''定义scripts脚本按钮''' 328 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 329 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 330 | try: 331 | if filelist: 332 | markup = filelist 333 | newmarkup = markup[page] 334 | if mybtn not in newmarkup: 335 | newmarkup.append(mybtn) 336 | else: 337 | if path == _JdDir: 338 | dir = ['scripts', 'own'] 339 | else: 340 | dir = os.listdir(path) 341 | dir = await getname(path, dir) 342 | dir.sort() 343 | markup = [Button.inline(file.split('--->')[0], data=str(file.split('--->')[-1])) 344 | for file in dir if os.path.isdir(path+'/'+file) or re.search(r'.js$', file.split('--->')[-1])] 345 | markup = split_list(markup, 3) 346 | if len(markup) > 30: 347 | markup = split_list(markup, 30) 348 | newmarkup = markup[page] 349 | newmarkup.append(mybtn) 350 | else: 351 | newmarkup = markup 352 | if path == _JdDir: 353 | newmarkup.append([Button.inline('取消', data='cancel')]) 354 | else: 355 | newmarkup.append( 356 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 357 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 358 | convdata = await conv.wait_event(press_event(SENDER)) 359 | res = bytes.decode(convdata.data) 360 | if res == 'cancel': 361 | msg = await client.edit_message(msg, '对话已取消') 362 | conv.cancel() 363 | return None, None, None 364 | elif res == 'next': 365 | page = page + 1 366 | if page > len(markup) - 1: 367 | page = 0 368 | return path, msg, page, markup 369 | elif res == 'up': 370 | page = page - 1 371 | if page < 0: 372 | page = len(markup) - 1 373 | return path, msg, page, markup 374 | elif res == 'updir': 375 | path = '/'.join(path.split('/')[:-1]) 376 | if path == '': 377 | path = _JdDir 378 | return path, msg, page, None 379 | elif os.path.isfile(path+'/'+res): 380 | msg = await client.edit_message(msg, '脚本即将在后台运行') 381 | logger.info(path+'/'+res+'脚本即将在后台运行') 382 | cmdtext = 'jd {}/{} now'.format(path, res) 383 | subprocess.Popen(cmdtext, shell=True, 384 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 385 | msg = await client.edit_message(msg, res + '在后台运行成功,请自行在程序结束后查看日志') 386 | conv.cancel() 387 | return None, None, None, None 388 | else: 389 | return path+'/'+res, msg, page, None 390 | except exceptions.TimeoutError: 391 | msg = await client.edit_message(msg, '选择已超时,对话已停止') 392 | return None, None, None, None 393 | except Exception as e: 394 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 395 | logger.error('something wrong,I\'m sorry\n'+str(e)) 396 | return None, None, None, None 397 | 398 | 399 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/log')) 400 | async def mylog(event): 401 | '''定义日志文件操作''' 402 | SENDER = event.sender_id 403 | path = _LogDir 404 | page = 0 405 | filelist = None 406 | async with client.conversation(SENDER, timeout=60) as conv: 407 | msg = await conv.send_message('正在查询,请稍后') 408 | while path: 409 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 410 | 411 | 412 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode')) 413 | async def mysnode(event): 414 | '''定义supernode文件命令''' 415 | SENDER = event.sender_id 416 | path = _ScriptsDir 417 | page = 0 418 | filelist = None 419 | async with client.conversation(SENDER, timeout=60) as conv: 420 | msg = await conv.send_message('正在查询,请稍后') 421 | while path: 422 | path, msg, page, filelist = await nodebtn(conv, SENDER, path, msg, page, filelist) 423 | 424 | 425 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile')) 426 | async def mygetfile(event): 427 | '''定义获取文件命令''' 428 | SENDER = event.sender_id 429 | path = _JdDir 430 | page = 0 431 | filelist = None 432 | async with client.conversation(SENDER, timeout=60) as conv: 433 | msg = await conv.send_message('正在查询,请稍后') 434 | while path: 435 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 436 | 437 | 438 | @client.on(events.NewMessage(from_users=chat_id)) 439 | async def myfile(event): 440 | '''定义文件操作''' 441 | try: 442 | SENDER = event.sender_id 443 | if event.message.file: 444 | markup = [] 445 | filename = event.message.file.name 446 | async with client.conversation(SENDER, timeout=30) as conv: 447 | msg = await conv.send_message('请选择您要放入的文件夹或操作:\n') 448 | markup.append([Button.inline('放入config', data=_ConfigDir), Button.inline( 449 | '放入scripts', data=_ScriptsDir)]) 450 | markup.append( 451 | [Button.inline('放入scripts并运行', data='node'), Button.inline('取消', data='cancel')]) 452 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=markup) 453 | convdata = await conv.wait_event(press_event(SENDER)) 454 | res = bytes.decode(convdata.data) 455 | if res == 'cancel': 456 | msg = await client.edit_message(msg, '对话已取消') 457 | conv.cancel() 458 | elif res == 'node': 459 | await backfile(_ScriptsDir+'/'+filename) 460 | await client.download_media(event.message, _ScriptsDir) 461 | cmdtext = 'jd {} now'.format(filename) 462 | subprocess.Popen( 463 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 464 | await client.edit_message(msg, '脚本已保存到Scripts文件夹,并成功在后台运行,请稍后自行查看日志') 465 | conv.cancel() 466 | else: 467 | await backfile(res+'/'+filename) 468 | await client.download_media(event.message, res) 469 | await client.edit_message(msg, filename+'已保存到'+res+'文件夹') 470 | if filename == 'crontab.list': 471 | cmdtext = 'crontab '+res+'/'+filename 472 | subprocess.Popen( 473 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 474 | await client.edit_message(msg, '定时文件已保存,并更新') 475 | conv.cancel() 476 | except exceptions.TimeoutError: 477 | msg = await client.send_message(chat_id, '选择已超时,对话已停止') 478 | except Exception as e: 479 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 480 | logger.error('something wrong,I\'m sorry\n'+str(e)) 481 | 482 | 483 | @client.on(events.NewMessage(from_users=chat_id, pattern='/edit')) 484 | async def myfileup(event): 485 | '''定义编辑文件操作''' 486 | SENDER = event.sender_id 487 | path = _JdDir 488 | page = 0 489 | filelist = None 490 | async with client.conversation(SENDER, timeout=60) as conv: 491 | msg = await conv.send_message('正在查询,请稍后') 492 | while path: 493 | path, msg, page, filelist = await myedit(conv, SENDER, path, msg, page, filelist) 494 | 495 | 496 | async def myedit(conv, SENDER, path, msg, page, filelist): 497 | mybtn = [Button.inline('上一页', data='up'), Button.inline('下一页', data='next'), Button.inline( 498 | '上级', data='updir'), Button.inline('取消', data='cancel')] 499 | mybtn2 = [[Button.inline('上一页', data='up'), Button.inline( 500 | '下一页', data='next'), Button.inline('取消', data='cancel')], [Button.inline('上十页', data='up10'), Button.inline( 501 | '下十页', data='next10'), Button.inline('编辑', data='edit')]] 502 | try: 503 | if filelist and type(filelist[0][0]) == str: 504 | markup = filelist 505 | newmarkup = markup[page] 506 | msg = await client.edit_message(msg, "".join(newmarkup), buttons=mybtn2) 507 | else: 508 | if filelist: 509 | markup = filelist 510 | newmarkup = markup[page] 511 | if mybtn not in newmarkup: 512 | newmarkup.append(mybtn) 513 | else: 514 | dir = os.listdir(path) 515 | dir.sort() 516 | markup = [Button.inline(file, data=str( 517 | file)) for file in dir] 518 | markup = split_list(markup, 3) 519 | if len(markup) > 30: 520 | markup = split_list(markup, 30) 521 | newmarkup = markup[page] 522 | newmarkup.append(mybtn) 523 | else: 524 | newmarkup = markup 525 | if path == _JdDir: 526 | newmarkup.append([Button.inline('取消', data='cancel')]) 527 | else: 528 | newmarkup.append( 529 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 530 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 531 | convdata = await conv.wait_event(press_event(SENDER)) 532 | res = bytes.decode(convdata.data) 533 | if res == 'cancel': 534 | msg = await client.edit_message(msg, '对话已取消') 535 | conv.cancel() 536 | return None, None, None, None, None 537 | elif res == 'next': 538 | page = page + 1 539 | if page > len(markup) - 1: 540 | page = 0 541 | return path, msg, page, markup 542 | elif res == 'up': 543 | page = page - 1 544 | if page < 0: 545 | page = len(markup) - 1 546 | return path, msg, page, markup 547 | elif res == 'next10': 548 | page = page + 10 549 | if page > len(markup) - 1: 550 | page = 0 551 | return path, msg, page, markup 552 | elif res == 'up10': 553 | page = page - 10 554 | if page < 0: 555 | page = len(markup) - 1 556 | return path, msg, page, markup 557 | elif res == 'updir': 558 | path = '/'.join(path.split('/')[:-1]) 559 | if path == '': 560 | path = _JdDir 561 | return path, msg, page, None 562 | elif res == 'edit': 563 | await client.send_message(chat_id, '请复制并修改以下内容,修改完成后发回机器人,2分钟内有效') 564 | await client.delete_messages(chat_id, msg) 565 | msg = await conv.send_message("".join(newmarkup)) 566 | resp = await conv.get_response() 567 | markup[page] = resp.raw_text.split('\n') 568 | for a in range(len(markup[page])): 569 | markup[page][a] = markup[page][a]+'\n' 570 | shutil.copy(path, path+'.bak') 571 | with open(path, 'w+', encoding='utf-8') as f: 572 | markup = ["".join(a) for a in markup] 573 | f.writelines(markup) 574 | await client.send_message(chat_id, '文件已修改成功,原文件备份为'+path+'.bak') 575 | conv.cancel() 576 | return None, None, None, None 577 | elif os.path.isfile(path+'/'+res): 578 | msg = await client.edit_message(msg, '文件读取中...请稍候') 579 | with open(path+'/'+res, 'r', encoding='utf-8') as f: 580 | lines = f.readlines() 581 | lines = split_list(lines, 15) 582 | page = 0 583 | return path+'/'+res, msg, page, lines 584 | else: 585 | return path+'/'+res, msg, page, None 586 | except exceptions.TimeoutError: 587 | msg = await client.edit_message(msg, '选择已超时,本次对话已停止') 588 | return None, None, None, None 589 | except Exception as e: 590 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 591 | logger.error('something wrong,I\'m sorry\n'+str(e)) 592 | return None, None, None, None 593 | 594 | 595 | @client.on(events.NewMessage(from_users=chat_id, pattern='/node')) 596 | async def mynode(event): 597 | '''接收/node命令后执行程序''' 598 | nodereg = re.compile(r'^/node [\S]+') 599 | text = re.findall(nodereg, event.raw_text) 600 | if len(text) == 0: 601 | res = '''请正确使用/node命令,如 602 | /node /abc/123.js 运行abc/123.js脚本 603 | /node /own/abc.js 运行own/abc.js脚本 604 | ''' 605 | await client.send_message(chat_id, res) 606 | else: 607 | await cmd('node '+text[0].replace('/node ', '')+' now') 608 | 609 | 610 | @client.on(events.NewMessage(from_users=chat_id, pattern='/cmd')) 611 | async def mycmd(event): 612 | '''接收/cmd命令后执行程序''' 613 | if StartCMD: 614 | cmdreg = re.compile(r'^/cmd [\s\S]+') 615 | text = re.findall(cmdreg, event.raw_text) 616 | if len(text) == 0: 617 | msg = '''请正确使用/cmd命令,如 618 | /cmd python3 /python/bot.py 运行/python目录下的bot文件 619 | /cmd ps 获取当前docker内进行 620 | ''' 621 | await client.send_message(chat_id, msg) 622 | else: 623 | logger.info(text) 624 | await cmd(text[0].replace('/cmd ', '')) 625 | else: 626 | await client.send_message(chat_id, '未开启CMD命令,如需使用请修改配置文件') 627 | 628 | 629 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getcookie')) 630 | async def mycookie(event): 631 | '''接收/getcookie后执行程序''' 632 | login = True 633 | msg = await client.send_message(chat_id, '正在获取二维码,请稍后') 634 | global cookiemsg 635 | try: 636 | SENDER = event.sender_id 637 | async with client.conversation(SENDER, timeout=30) as conv: 638 | getSToken() 639 | getOKLToken() 640 | url = 'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token='+token 641 | creatqr(url) 642 | markup = [Button.inline("已扫码", data='confirm'), 643 | Button.inline("取消", data='cancel')] 644 | await client.delete_messages(chat_id, msg) 645 | cookiemsg = await client.send_message(chat_id, '30s内点击取消将取消本次操作\n如不取消,扫码结果将于30s后显示\n扫码后不想等待点击已扫码', file=img_file, buttons=markup) 646 | convdata = await conv.wait_event(press_event(SENDER)) 647 | res = bytes.decode(convdata.data) 648 | if res == 'cancel': 649 | login = False 650 | await client.delete_messages(chat_id, cookiemsg) 651 | msg = await conv.send_message('对话已取消') 652 | conv.cancel() 653 | else: 654 | raise exceptions.TimeoutError() 655 | except exceptions.TimeoutError: 656 | expired_time = time.time() + 60 * 2 657 | while login: 658 | check_time_stamp = int(time.time() * 1000) 659 | check_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=%s&ou_state=0&okl_token=%s' % ( 660 | token, okl_token) 661 | check_data = { 662 | 'lang': 'chs', 663 | 'appid': 300, 664 | '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, 665 | 'source': 'wq_passport' 666 | } 667 | check_header = { 668 | '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, 669 | 'Cookie': cookies, 670 | 'Connection': 'Keep-Alive', 671 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 672 | 'Accept': 'application/json, text/plain, */*', 673 | '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', 674 | } 675 | resp = requests.post( 676 | url=check_url, headers=check_header, data=check_data, timeout=30) 677 | data = resp.json() 678 | if data.get("errcode") == 0: 679 | parseJDCookies(resp.headers) 680 | await client.delete_messages(chat_id, cookiemsg) 681 | await client.send_message(chat_id, '以下为获取到的cookie') 682 | await client.send_message(chat_id, jd_cookie) 683 | return 684 | if data.get("errcode") == 21: 685 | await client.delete_messages(chat_id, cookiemsg) 686 | await client.send_message(chat_id, '发生了某些错误\n'+data.get("errcode")) 687 | return 688 | if time.time() > expired_time: 689 | await client.delete_messages(chat_id, cookiemsg) 690 | await client.send_message(chat_id, '超过3分钟未扫码,二维码已过期') 691 | return 692 | except Exception as e: 693 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 694 | logger.error('something wrong,I\'m sorry\n'+str(e)) 695 | 696 | 697 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/setshort$')) 698 | async def setshortcut(event): 699 | SENDER = event.sender_id 700 | async with client.conversation(SENDER, timeout=60) as conv: 701 | await conv.send_message( 702 | '60s内回复有效\n请按格式输入您的快捷命令。例如:\n京豆通知-->jd 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 jd jd_bean_change now\n如不添加nohup 会等待程序执行完,期间不能交互\n建议运行时间短命令不添加nohup ') 703 | shortcut = await conv.get_response() 704 | with open(_shortcut, 'w+', encoding='utf-8') as f: 705 | f.write(shortcut.raw_text) 706 | await conv.send_message('已设置成功可通过"/a"使用') 707 | conv.cancel() 708 | 709 | 710 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/a$')) 711 | async def shortcut(event): 712 | markup = [] 713 | SENDER = event.sender_id 714 | msg = await client.send_message(chat_id, '正在查询您的常用命令,请稍后') 715 | with open(_shortcut, 'r', encoding='utf-8') as f: 716 | shortcuts = f.readlines() 717 | try: 718 | async with client.conversation(SENDER, timeout=60) as conv: 719 | markup = [Button.inline(shortcut.split( 720 | '-->')[0], data=str(shortcut.split('-->')[-1])) for shortcut in shortcuts] 721 | markup = split_list(markup, 3) 722 | markup.append([Button.inline('取消', data='cancel')]) 723 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=markup) 724 | convdata = await conv.wait_event(press_event(SENDER)) 725 | res = bytes.decode(convdata.data) 726 | if res == 'cancel': 727 | msg = await client.edit_message(msg, '对话已取消') 728 | conv.cancel() 729 | elif 'nohup ' in res: 730 | msg = await client.edit_message(msg, '即将执行您的操作'+res) 731 | cmdtext = res.replace('nohup ', '') 732 | subprocess.Popen( 733 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 734 | msg = await client.edit_message(msg, '已在后台执行您的操作'+res.replace('nohup ', '')) 735 | conv.cancel() 736 | else: 737 | await client.delete_messages(chat_id, msg) 738 | await cmd(res) 739 | conv.cancel() 740 | except exceptions.TimeoutError: 741 | msg = await client.edit_message(msg, '选择已超时,对话已停止') 742 | except Exception as e: 743 | await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 744 | logger.error('something wrong,I\'m sorry\n'+str(e)) 745 | 746 | 747 | @client.on(events.NewMessage(from_users=chat_id, pattern='/help')) 748 | async def myhelp(event): 749 | '''接收/help命令后执行程序''' 750 | msg = ''' 751 | a-我的自定义快捷按钮 752 | edit-编辑文件 753 | start-开始使用本程序 754 | node-执行js脚本文件,绝对路径。 755 | cmd-执行cmd命令 756 | snode-选择脚本后台运行 757 | log-选择日志 758 | getfile-获取jd目录下文件 759 | setshort-设置自定义按钮 760 | getcookie-扫码获取cookie''' 761 | await client.send_message(chat_id, msg) 762 | 763 | 764 | @client.on(events.NewMessage(from_users=chat_id, pattern='/start')) 765 | async def mystart(event): 766 | '''接收/start命令后执行程序''' 767 | msg = '''使用方法如下: 768 | /help 获取命令,可直接发送至botfather 769 | /a 使用你的自定义快捷按钮 770 | /start 开始使用本程序 771 | /node 执行js脚本文件,直接输入/node jd_bean_change 如执行其他自己js,需输入绝对路径。即可进行执行。该命令会等待脚本执行完,期间不能使用机器人,建议使用snode命令。 772 | /cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 不建议使用机器人使用并发,可能产生不明原因的崩溃 773 | /snode 命令可以选择脚本执行,只能选择/scripts 和/own目录下的脚本,选择完后直接后台运行,不影响机器人响应其他命令 774 | /log 选择查看执行日志 775 | /getfile 获取jd目录下文件 776 | /setshort 设置自定义按钮,每次设置会覆盖原设置 777 | /getcookie 扫码获取cookie 增加30s内取消按钮,30s后不能进行其他交互直到2分钟或获取到cookie 778 | /edit 从jd目录下选择文件编辑,需要将编辑好信息全部发给机器人,机器人会根据你发的信息进行替换。建议用来编辑config或crontab.list 其他文件慎用!!! 779 | 此外直接发送文件,会让您选择保存到哪个文件夹,如果选择运行`,将保存至own目录下,并立即运行脚本,crontab.list文件会自动更新时间''' 780 | await client.send_message(chat_id, msg) 781 | 782 | with client: 783 | task = client.loop.create_task(hello()) 784 | client.loop.run_forever() 785 | -------------------------------------------------------------------------------- /botV4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | # time: 2021 4 | # newtime: 2021-04-25 5 | # version: 0.1.7 6 | # log: 新增:连接成功,机器人发送通知,修复:没有中文名称脚本不能识别 7 | # author: https://github.com/SuMaiKaDe 8 | 9 | from telethon import TelegramClient, events, Button, connection 10 | import requests 11 | import re 12 | import json 13 | import time 14 | import os 15 | import qrcode 16 | import logging 17 | import subprocess 18 | import shutil 19 | from asyncio import exceptions 20 | logging.basicConfig( 21 | format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO) 22 | logger = logging.getLogger(__name__) 23 | # 定义目录,以bot.py所在目录上级目录为/jd或/ql文件夹 24 | _JdDir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 25 | _ConfigDir = _JdDir + '/config' 26 | _ScriptsDir = _JdDir + '/scripts' 27 | _OwnDir = _JdDir + '/own' 28 | _LogDir = _JdDir + '/log' 29 | _shortcut = _ConfigDir + '/shortcut.list' 30 | _bot = _ConfigDir + '/bot.json' 31 | _qr = _ConfigDir + 'qr.jpg' 32 | # 频道id/用户id 33 | with open(_bot, 'r', encoding='utf-8') as f: 34 | bot = json.load(f) 35 | chat_id = int(bot['user_id']) 36 | # 机器人 TOKEN 37 | TOKEN = bot['bot_token'] 38 | # 发消息的TG代理 39 | # my.telegram.org申请到的api_id,api_hash 40 | api_id = bot['api_id'] 41 | api_hash = bot['api_hash'] 42 | proxystart = bot['proxy'] 43 | proxyType = bot['proxy_type'] 44 | connectionType = connection.ConnectionTcpMTProxyRandomizedIntermediate if proxyType == "MTProxy" else connection.ConnectionTcpFull 45 | if 'proxy_user' in bot.keys() and bot['proxy_user'] != "代理的username,有则填写,无则不用动": 46 | proxy = { 47 | 'proxy_type': bot['proxy_type'], 48 | 'addr': bot['proxy_add'], 49 | 'port': bot['proxy_port'], 50 | 'username': bot['proxy_user'], 51 | 'password': bot['proxy_password']} 52 | elif proxyType == "MTProxy": 53 | proxy = (bot['proxy_add'], bot['proxy_port'], bot['proxy_secret']) 54 | else: 55 | proxy = (bot['proxy_type'], bot['proxy_add'], bot['proxy_port']) 56 | # 开启tg对话 57 | if proxystart: 58 | client = TelegramClient('bot', api_id, api_hash, connection=connectionType, 59 | proxy=proxy, connection_retries=None).start(bot_token=TOKEN) 60 | else: 61 | client = TelegramClient('bot', api_id, api_hash, 62 | connection_retries=None).start(bot_token=TOKEN) 63 | cookiemsg = '' 64 | img_file = _qr 65 | StartCMD = bot['StartCMD'] 66 | 67 | 68 | async def hello(): 69 | await client.send_message(chat_id, 'duang duang duang \n您的机器人已成功激活\n发个 /start 试试吧') 70 | 71 | 72 | def press_event(user_id): 73 | return events.CallbackQuery(func=lambda e: e.sender_id == user_id) 74 | 75 | 76 | # 扫码获取cookie 直接采用LOF大佬代码 77 | # getSToken请求获取,s_token用于发送post请求是的必须参数 78 | s_token = "" 79 | # getSToken请求获取,guid,lsid,lstoken用于组装cookies 80 | guid, lsid, lstoken = "", "", "" 81 | # 由上面参数组装生成,getOKLToken函数发送请求需要使用 82 | cookies = "" 83 | # getOKLToken请求获取,token用户生成二维码使用、okl_token用户检查扫码登录结果使用 84 | token, okl_token = "", "" 85 | # 最终获取到的可用的cookie 86 | jd_cookie = "" 87 | 88 | 89 | def getSToken(): 90 | time_stamp = int(time.time() * 1000) 91 | 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 92 | get_header = { 93 | 'Connection': 'Keep-Alive', 94 | 'Content-Type': 'application/x-www-form-urlencoded', 95 | 'Accept': 'application/json, text/plain, */*', 96 | 'Accept-Language': 'zh-cn', 97 | '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, 98 | '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', 99 | 'Host': 'plogin.m.jd.com' 100 | } 101 | try: 102 | resp = requests.get(url=get_url, headers=get_header) 103 | parseGetRespCookie(resp.headers, resp.json()) 104 | logger.info(resp.headers) 105 | logger.info(resp.json()) 106 | except Exception as error: 107 | logger.exception("Get网络请求异常", error) 108 | 109 | 110 | def parseGetRespCookie(headers, get_resp): 111 | global s_token 112 | global cookies 113 | s_token = get_resp.get('s_token') 114 | set_cookies = headers.get('set-cookie') 115 | logger.info(set_cookies) 116 | guid = re.findall(r"guid=(.+?);", set_cookies)[0] 117 | lsid = re.findall(r"lsid=(.+?);", set_cookies)[0] 118 | lstoken = re.findall(r"lstoken=(.+?);", set_cookies)[0] 119 | cookies = f"guid={guid}; lang=chs; lsid={lsid}; lstoken={lstoken}; " 120 | logger.info(cookies) 121 | 122 | 123 | def getOKLToken(): 124 | post_time_stamp = int(time.time() * 1000) 125 | post_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=%s&v=%s&remember=true' % ( 126 | s_token, post_time_stamp) 127 | post_data = { 128 | 'lang': 'chs', 129 | 'appid': 300, 130 | '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, 131 | 'source': 'wq_passport' 132 | } 133 | post_header = { 134 | 'Connection': 'Keep-Alive', 135 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 136 | 'Accept': 'application/json, text/plain, */*', 137 | 'Cookie': cookies, 138 | '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, 139 | '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', 140 | 'Host': 'plogin.m.jd.com', 141 | } 142 | try: 143 | global okl_token 144 | resp = requests.post( 145 | url=post_url, headers=post_header, data=post_data, timeout=20) 146 | parsePostRespCookie(resp.headers, resp.json()) 147 | logger.info(resp.headers) 148 | except Exception as error: 149 | logger.exception("Post网络请求错误", error) 150 | 151 | 152 | def parsePostRespCookie(headers, data): 153 | global token 154 | global okl_token 155 | token = data.get('token') 156 | okl_token = re.findall(r"okl_token=(.+?);", headers.get('set-cookie'))[0] 157 | logger.info("token:" + token) 158 | logger.info("okl_token:" + okl_token) 159 | 160 | 161 | def parseJDCookies(headers): 162 | global jd_cookie 163 | logger.info("扫码登录成功,下面为获取到的用户Cookie。") 164 | set_cookie = headers.get('Set-Cookie') 165 | pt_key = re.findall(r"pt_key=(.+?);", set_cookie)[0] 166 | pt_pin = re.findall(r"pt_pin=(.+?);", set_cookie)[0] 167 | logger.info(pt_key) 168 | logger.info(pt_pin) 169 | jd_cookie = f'pt_key={pt_key};pt_pin={pt_pin};' 170 | 171 | 172 | def creatqr(text): 173 | '''实例化QRCode生成qr对象''' 174 | qr = qrcode.QRCode( 175 | version=1, 176 | error_correction=qrcode.constants.ERROR_CORRECT_H, 177 | box_size=10, 178 | border=4 179 | ) 180 | qr.clear() 181 | # 传入数据 182 | qr.add_data(text) 183 | qr.make(fit=True) 184 | # 生成二维码 185 | img = qr.make_image() 186 | # 保存二维码 187 | img.save(img_file) 188 | 189 | 190 | def split_list(datas, n, row: bool = True): 191 | """一维列表转二维列表,根据N不同,生成不同级别的列表""" 192 | length = len(datas) 193 | size = length / n + 1 if length % n else length/n 194 | _datas = [] 195 | if not row: 196 | size, n = n, size 197 | for i in range(int(size)): 198 | start = int(i * n) 199 | end = int((i + 1) * n) 200 | _datas.append(datas[start:end]) 201 | return _datas 202 | 203 | 204 | async def backfile(file): 205 | if os.path.exists(file): 206 | try: 207 | os.rename(file, file+'.bak') 208 | except WindowsError: 209 | os.remove(file+'.bak') 210 | os.rename(file, file+'.bak') 211 | 212 | 213 | async def cmd(cmdtext): 214 | '''定义执行cmd命令''' 215 | try: 216 | msg = await client.send_message(chat_id, '开始执行程序,如程序复杂,建议稍等') 217 | res_bytes = subprocess.check_output( 218 | cmdtext, shell=True, stderr=subprocess.STDOUT) 219 | res = res_bytes.decode('utf-8') 220 | if len(res) == 0: 221 | await client.edit_message(msg, '已执行,但返回值为空') 222 | elif len(res) <= 4000: 223 | await client.edit_message(msg, res) 224 | elif len(res) > 4000: 225 | with open(_LogDir+'/botres.log', 'w+', encoding='utf-8') as f: 226 | f.write(res) 227 | await client.send_message(chat_id, '执行结果较长,请查看日志', file=_LogDir+'/botres.log') 228 | except Exception as e: 229 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 230 | logger.error('something wrong,I\'m sorry'+str(e)) 231 | 232 | 233 | async def logbtn(conv, SENDER, path, msg, page, filelist): 234 | '''定义log日志按钮''' 235 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 236 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 237 | try: 238 | if filelist: 239 | markup = filelist 240 | newmarkup = markup[page] 241 | if mybtn not in newmarkup: 242 | newmarkup.append(mybtn) 243 | else: 244 | dir = os.listdir(path) 245 | dir.sort() 246 | markup = [Button.inline(file, data=str(file)) 247 | for file in dir] 248 | markup = split_list(markup, 3) 249 | if len(markup) > 30: 250 | markup = split_list(markup, 30) 251 | newmarkup = markup[page] 252 | newmarkup.append(mybtn) 253 | else: 254 | newmarkup = markup 255 | if path == _JdDir: 256 | newmarkup.append([Button.inline('取消', data='cancel')]) 257 | else: 258 | newmarkup.append( 259 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 260 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 261 | convdata = await conv.wait_event(press_event(SENDER)) 262 | res = bytes.decode(convdata.data) 263 | if res == 'cancel': 264 | msg = await client.edit_message(msg, '对话已取消') 265 | conv.cancel() 266 | return None, None, None, None 267 | elif res == 'next': 268 | page = page + 1 269 | if page > len(markup) - 1: 270 | page = 0 271 | return path, msg, page, markup 272 | elif res == 'up': 273 | page = page - 1 274 | if page < 0: 275 | page = len(markup) - 1 276 | return path, msg, page, markup 277 | elif res == 'updir': 278 | path = '/'.join(path.split('/')[:-1]) 279 | logger.info(path) 280 | if path == '': 281 | path = _JdDir 282 | return path, msg, page, None 283 | elif os.path.isfile(path+'/'+res): 284 | msg = await client.edit_message(msg, '文件发送中,请注意查收') 285 | await conv.send_file(path+'/'+res) 286 | msg = await client.edit_message(msg, res+'发送成功,请查收') 287 | conv.cancel() 288 | return None, None, None, None 289 | else: 290 | return path+'/'+res, msg, page, None 291 | except exceptions.TimeoutError: 292 | msg = await client.edit_message(msg, '选择已超时,本次对话已停止') 293 | return None, None, None, None 294 | except Exception as e: 295 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 296 | logger.error('something wrong,I\'m sorry\n'+str(e)) 297 | return None, None, None, None 298 | 299 | 300 | async def getname(path, dir): 301 | names = [] 302 | reg = r'new Env\(\'[\S]+?\'\)' 303 | cname = False 304 | for file in dir: 305 | if os.path.isdir(path+'/'+file): 306 | names.append(file) 307 | elif file.endswith('.js') and file != 'jdCookie.js' and file != 'getJDCookie.js' and file != 'JD_extra_cookie.js': 308 | with open(path+'/'+file, 'r', encoding='utf-8') as f: 309 | resdatas = f.readlines() 310 | for data in resdatas: 311 | if 'new Env' in data: 312 | data = data.replace('\"', '\'') 313 | res = re.findall(reg, data) 314 | if len(res) != 0: 315 | res = res[0].split('\'')[-2] 316 | names.append(res+'--->'+file) 317 | cname = True 318 | break 319 | if not cname: 320 | names.append(file+'--->'+file) 321 | cname = False 322 | else: 323 | continue 324 | return names 325 | 326 | 327 | async def nodebtn(conv, SENDER, path, msg, page, filelist): 328 | '''定义scripts脚本按钮''' 329 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 330 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 331 | try: 332 | if filelist: 333 | markup = filelist 334 | newmarkup = markup[page] 335 | if mybtn not in newmarkup: 336 | newmarkup.append(mybtn) 337 | else: 338 | if path == _JdDir: 339 | dir = ['scripts', 'own'] 340 | else: 341 | dir = os.listdir(path) 342 | dir = await getname(path, dir) 343 | dir.sort() 344 | markup = [Button.inline(file.split('--->')[0], data=str(file.split('--->')[-1])) 345 | for file in dir if os.path.isdir(path+'/'+file) or file.endswith('.js')] 346 | markup = split_list(markup, 3) 347 | if len(markup) > 30: 348 | markup = split_list(markup, 30) 349 | newmarkup = markup[page] 350 | newmarkup.append(mybtn) 351 | else: 352 | newmarkup = markup 353 | if path == _JdDir: 354 | newmarkup.append([Button.inline('取消', data='cancel')]) 355 | else: 356 | newmarkup.append( 357 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 358 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 359 | convdata = await conv.wait_event(press_event(SENDER)) 360 | res = bytes.decode(convdata.data) 361 | if res == 'cancel': 362 | msg = await client.edit_message(msg, '对话已取消') 363 | conv.cancel() 364 | return None, None, None 365 | elif res == 'next': 366 | page = page + 1 367 | if page > len(markup) - 1: 368 | page = 0 369 | return path, msg, page, markup 370 | elif res == 'up': 371 | page = page - 1 372 | if page < 0: 373 | page = len(markup) - 1 374 | return path, msg, page, markup 375 | elif res == 'updir': 376 | path = '/'.join(path.split('/')[:-1]) 377 | if path == '': 378 | path = _JdDir 379 | return path, msg, page, None 380 | elif os.path.isfile(path+'/'+res): 381 | msg = await client.edit_message(msg, '脚本即将在后台运行') 382 | logger.info(path+'/'+res+'脚本即将在后台运行') 383 | cmdtext = 'jtask {}/{} now'.format(path, res) 384 | subprocess.Popen(cmdtext, shell=True, 385 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 386 | msg = await client.edit_message(msg, res + '在后台运行成功,请自行在程序结束后查看日志') 387 | conv.cancel() 388 | return None, None, None, None 389 | else: 390 | return path+'/'+res, msg, page, None 391 | except exceptions.TimeoutError: 392 | msg = await client.edit_message(msg, '选择已超时,对话已停止') 393 | return None, None, None, None 394 | except Exception as e: 395 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 396 | logger.error('something wrong,I\'m sorry\n'+str(e)) 397 | return None, None, None, None 398 | 399 | 400 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/log')) 401 | async def mylog(event): 402 | '''定义日志文件操作''' 403 | SENDER = event.sender_id 404 | path = _LogDir 405 | page = 0 406 | filelist = None 407 | async with client.conversation(SENDER, timeout=60) as conv: 408 | msg = await conv.send_message('正在查询,请稍后') 409 | while path: 410 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 411 | 412 | 413 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode')) 414 | async def mysnode(event): 415 | '''定义supernode文件命令''' 416 | SENDER = event.sender_id 417 | path = _JdDir 418 | page = 0 419 | filelist = None 420 | async with client.conversation(SENDER, timeout=60) as conv: 421 | msg = await conv.send_message('正在查询,请稍后') 422 | while path: 423 | path, msg, page, filelist = await nodebtn(conv, SENDER, path, msg, page, filelist) 424 | 425 | 426 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile')) 427 | async def mygetfile(event): 428 | '''定义获取文件命令''' 429 | SENDER = event.sender_id 430 | path = _JdDir 431 | page = 0 432 | filelist = None 433 | async with client.conversation(SENDER, timeout=60) as conv: 434 | msg = await conv.send_message('正在查询,请稍后') 435 | while path: 436 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 437 | 438 | 439 | @client.on(events.NewMessage(from_users=chat_id)) 440 | async def myfile(event): 441 | '''定义文件操作''' 442 | try: 443 | SENDER = event.sender_id 444 | if event.message.file: 445 | markup = [] 446 | filename = event.message.file.name 447 | async with client.conversation(SENDER, timeout=30) as conv: 448 | msg = await conv.send_message('请选择您要放入的文件夹或操作:\n') 449 | markup.append([Button.inline('放入config', data=_ConfigDir), Button.inline( 450 | '放入scripts', data=_ScriptsDir), Button.inline('放入own', data=_OwnDir)]) 451 | markup.append( 452 | [Button.inline('放入own并运行', data='node'), Button.inline('取消', data='cancel')]) 453 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=markup) 454 | convdata = await conv.wait_event(press_event(SENDER)) 455 | res = bytes.decode(convdata.data) 456 | if res == 'cancel': 457 | msg = await client.edit_message(msg, '对话已取消') 458 | conv.cancel() 459 | elif res == 'node': 460 | await backfile(_OwnDir+'/'+filename) 461 | await client.download_media(event.message, _OwnDir) 462 | cmdtext = 'jtask {}/{} now'.format(_OwnDir, filename) 463 | subprocess.Popen( 464 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 465 | await client.edit_message(msg, '脚本已保存到own文件夹,并成功在后台运行,请稍后自行查看日志') 466 | conv.cancel() 467 | else: 468 | await backfile(res+'/'+filename) 469 | await client.download_media(event.message, res) 470 | await client.edit_message(msg, filename+'已保存到'+res+'文件夹') 471 | if filename == 'crontab.list': 472 | cmdtext = 'crontab '+res+'/'+filename 473 | subprocess.Popen( 474 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 475 | await client.edit_message(msg, '定时文件已保存,并更新') 476 | conv.cancel() 477 | except exceptions.TimeoutError: 478 | msg = await client.send_message(chat_id, '选择已超时,对话已停止') 479 | except Exception as e: 480 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 481 | logger.error('something wrong,I\'m sorry\n'+str(e)) 482 | 483 | 484 | @client.on(events.NewMessage(from_users=chat_id, pattern='/edit')) 485 | async def myfileup(event): 486 | '''定义编辑文件操作''' 487 | SENDER = event.sender_id 488 | path = _JdDir 489 | page = 0 490 | filelist = None 491 | async with client.conversation(SENDER, timeout=60) as conv: 492 | msg = await conv.send_message('正在查询,请稍后') 493 | while path: 494 | path, msg, page, filelist = await myedit(conv, SENDER, path, msg, page, filelist) 495 | 496 | 497 | async def myedit(conv, SENDER, path, msg, page, filelist): 498 | mybtn = [Button.inline('上一页', data='up'), Button.inline('下一页', data='next'), Button.inline( 499 | '上级', data='updir'), Button.inline('取消', data='cancel')] 500 | mybtn2 = [[Button.inline('上一页', data='up'), Button.inline( 501 | '下一页', data='next'), Button.inline('取消', data='cancel')], [Button.inline('上十页', data='up10'), Button.inline( 502 | '下十页', data='next10'), Button.inline('编辑', data='edit')]] 503 | try: 504 | if filelist and type(filelist[0][0]) == str: 505 | markup = filelist 506 | newmarkup = markup[page] 507 | msg = await client.edit_message(msg, "".join(newmarkup), buttons=mybtn2) 508 | else: 509 | if filelist: 510 | markup = filelist 511 | newmarkup = markup[page] 512 | if mybtn not in newmarkup: 513 | newmarkup.append(mybtn) 514 | else: 515 | dir = os.listdir(path) 516 | dir.sort() 517 | markup = [Button.inline(file, data=str( 518 | file)) for file in dir] 519 | markup = split_list(markup, 3) 520 | if len(markup) > 30: 521 | markup = split_list(markup, 30) 522 | newmarkup = markup[page] 523 | newmarkup.append(mybtn) 524 | else: 525 | newmarkup = markup 526 | if path == _JdDir: 527 | newmarkup.append([Button.inline('取消', data='cancel')]) 528 | else: 529 | newmarkup.append( 530 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 531 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 532 | convdata = await conv.wait_event(press_event(SENDER)) 533 | res = bytes.decode(convdata.data) 534 | if res == 'cancel': 535 | msg = await client.edit_message(msg, '对话已取消') 536 | conv.cancel() 537 | return None, None, None, None, None 538 | elif res == 'next': 539 | page = page + 1 540 | if page > len(markup) - 1: 541 | page = 0 542 | return path, msg, page, markup 543 | elif res == 'up': 544 | page = page - 1 545 | if page < 0: 546 | page = len(markup) - 1 547 | return path, msg, page, markup 548 | elif res == 'next10': 549 | page = page + 10 550 | if page > len(markup) - 1: 551 | page = 0 552 | return path, msg, page, markup 553 | elif res == 'up10': 554 | page = page - 10 555 | if page < 0: 556 | page = len(markup) - 1 557 | return path, msg, page, markup 558 | elif res == 'updir': 559 | path = '/'.join(path.split('/')[:-1]) 560 | if path == '': 561 | path = _JdDir 562 | return path, msg, page, None 563 | elif res == 'edit': 564 | await client.send_message(chat_id, '请复制并修改以下内容,修改完成后发回机器人,2分钟内有效') 565 | await client.delete_messages(chat_id, msg) 566 | msg = await conv.send_message("".join(newmarkup)) 567 | resp = await conv.get_response() 568 | markup[page] = resp.raw_text.split('\n') 569 | for a in range(len(markup[page])): 570 | markup[page][a] = markup[page][a]+'\n' 571 | shutil.copy(path, path+'.bak') 572 | with open(path, 'w+', encoding='utf-8') as f: 573 | markup = ["".join(a) for a in markup] 574 | f.writelines(markup) 575 | await client.send_message(chat_id, '文件已修改成功,原文件备份为'+path+'.bak') 576 | conv.cancel() 577 | return None, None, None, None 578 | elif os.path.isfile(path+'/'+res): 579 | msg = await client.edit_message(msg, '文件读取中...请稍候') 580 | with open(path+'/'+res, 'r', encoding='utf-8') as f: 581 | lines = f.readlines() 582 | lines = split_list(lines, 15) 583 | page = 0 584 | return path+'/'+res, msg, page, lines 585 | else: 586 | return path+'/'+res, msg, page, None 587 | except exceptions.TimeoutError: 588 | msg = await client.edit_message(msg, '选择已超时,本次对话已停止') 589 | return None, None, None, None 590 | except Exception as e: 591 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 592 | logger.error('something wrong,I\'m sorry\n'+str(e)) 593 | return None, None, None, None 594 | 595 | 596 | @client.on(events.NewMessage(from_users=chat_id, pattern='/node')) 597 | async def mynode(event): 598 | '''接收/node命令后执行程序''' 599 | nodereg = re.compile(r'^/node [\S]+') 600 | text = re.findall(nodereg, event.raw_text) 601 | if len(text) == 0: 602 | res = '''请正确使用/node命令,如 603 | /node /abc/123.js 运行abc/123.js脚本 604 | /node /own/abc.js 运行own/abc.js脚本 605 | ''' 606 | await client.send_message(chat_id, res) 607 | else: 608 | await cmd('jtask '+text[0].replace('/node ', '')+' now') 609 | 610 | 611 | @client.on(events.NewMessage(from_users=chat_id, pattern='/cmd')) 612 | async def mycmd(event): 613 | '''接收/cmd命令后执行程序''' 614 | if StartCMD: 615 | cmdreg = re.compile(r'^/cmd [\s\S]+') 616 | text = re.findall(cmdreg, event.raw_text) 617 | if len(text) == 0: 618 | msg = '''请正确使用/cmd命令,如 619 | /cmd jlog # 删除旧日志 620 | /cmd jup # 更新所有脚本 621 | /cmd jcode # 导出所有互助码 622 | /cmd jcsv # 记录豆豆变化情况 623 | 不建议直接使用cmd命令执行脚本,请使用/node或/snode 624 | ''' 625 | await client.send_message(chat_id, msg) 626 | else: 627 | logger.info(text) 628 | await cmd(text[0].replace('/cmd ', '')) 629 | else: 630 | await client.send_message(chat_id, '未开启CMD命令,如需使用请修改配置文件') 631 | 632 | 633 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getcookie')) 634 | async def mycookie(event): 635 | '''接收/getcookie后执行程序''' 636 | login = True 637 | msg = await client.send_message(chat_id, '正在获取二维码,请稍后') 638 | global cookiemsg 639 | try: 640 | SENDER = event.sender_id 641 | async with client.conversation(SENDER, timeout=30) as conv: 642 | getSToken() 643 | getOKLToken() 644 | url = 'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token='+token 645 | creatqr(url) 646 | markup = [Button.inline("已扫码", data='confirm'), 647 | Button.inline("取消", data='cancel')] 648 | await client.delete_messages(chat_id, msg) 649 | cookiemsg = await client.send_message(chat_id, '30s内点击取消将取消本次操作\n如不取消,扫码结果将于30s后显示\n扫码后不想等待点击已扫码', file=img_file, buttons=markup) 650 | convdata = await conv.wait_event(press_event(SENDER)) 651 | res = bytes.decode(convdata.data) 652 | if res == 'cancel': 653 | login = False 654 | await client.delete_messages(chat_id, cookiemsg) 655 | msg = await conv.send_message('对话已取消') 656 | conv.cancel() 657 | else: 658 | raise exceptions.TimeoutError() 659 | except exceptions.TimeoutError: 660 | expired_time = time.time() + 60 * 2 661 | while login: 662 | check_time_stamp = int(time.time() * 1000) 663 | check_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=%s&ou_state=0&okl_token=%s' % ( 664 | token, okl_token) 665 | check_data = { 666 | 'lang': 'chs', 667 | 'appid': 300, 668 | '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, 669 | 'source': 'wq_passport' 670 | } 671 | check_header = { 672 | '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, 673 | 'Cookie': cookies, 674 | 'Connection': 'Keep-Alive', 675 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 676 | 'Accept': 'application/json, text/plain, */*', 677 | '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', 678 | } 679 | resp = requests.post( 680 | url=check_url, headers=check_header, data=check_data, timeout=30) 681 | data = resp.json() 682 | if data.get("errcode") == 0: 683 | parseJDCookies(resp.headers) 684 | await client.delete_messages(chat_id, cookiemsg) 685 | await client.send_message(chat_id, '以下为获取到的cookie') 686 | await client.send_message(chat_id, jd_cookie) 687 | return 688 | if data.get("errcode") == 21: 689 | await client.delete_messages(chat_id, cookiemsg) 690 | await client.send_message(chat_id, '发生了某些错误\n'+data.get("errcode")) 691 | return 692 | if time.time() > expired_time: 693 | await client.delete_messages(chat_id, cookiemsg) 694 | await client.send_message(chat_id, '超过3分钟未扫码,二维码已过期') 695 | return 696 | except Exception as e: 697 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 698 | logger.error('something wrong,I\'m sorry\n'+str(e)) 699 | 700 | 701 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/setshort$')) 702 | async def setshortcut(event): 703 | SENDER = event.sender_id 704 | async with client.conversation(SENDER, timeout=60) as conv: 705 | await conv.send_message( 706 | '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 ') 707 | shortcut = await conv.get_response() 708 | with open(_shortcut, 'w+', encoding='utf-8') as f: 709 | f.write(shortcut.raw_text) 710 | await conv.send_message('已设置成功可通过"/a"使用') 711 | conv.cancel() 712 | 713 | 714 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/a$')) 715 | async def shortcut(event): 716 | markup = [] 717 | SENDER = event.sender_id 718 | msg = await client.send_message(chat_id, '正在查询您的常用命令,请稍后') 719 | with open(_shortcut, 'r', encoding='utf-8') as f: 720 | shortcuts = f.readlines() 721 | try: 722 | async with client.conversation(SENDER, timeout=60) as conv: 723 | markup = [Button.inline(shortcut.split( 724 | '-->')[0], data=str(shortcut.split('-->')[-1])) for shortcut in shortcuts] 725 | markup = split_list(markup, 3) 726 | markup.append([Button.inline('取消', data='cancel')]) 727 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=markup) 728 | convdata = await conv.wait_event(press_event(SENDER)) 729 | res = bytes.decode(convdata.data) 730 | if res == 'cancel': 731 | msg = await client.edit_message(msg, '对话已取消') 732 | conv.cancel() 733 | elif 'nohup ' in res: 734 | msg = await client.edit_message(msg, '即将执行您的操作'+res) 735 | cmdtext = res.replace('nohup ', '') 736 | subprocess.Popen( 737 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 738 | msg = await client.edit_message(msg, '已在后台执行您的操作'+res.replace('nohup ', '')) 739 | conv.cancel() 740 | else: 741 | await client.delete_messages(chat_id, msg) 742 | await cmd(res) 743 | conv.cancel() 744 | except exceptions.TimeoutError: 745 | msg = await client.edit_message(msg, '选择已超时,对话已停止') 746 | except Exception as e: 747 | await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 748 | logger.error('something wrong,I\'m sorry\n'+str(e)) 749 | 750 | 751 | @client.on(events.NewMessage(from_users=chat_id, pattern='/help')) 752 | async def myhelp(event): 753 | '''接收/help命令后执行程序''' 754 | msg = ''' 755 | a-我的自定义快捷按钮 756 | edit-编辑文件 757 | start-开始使用本程序 758 | node-执行js脚本文件,绝对路径。 759 | cmd-执行cmd命令 760 | snode-选择脚本后台运行 761 | log-选择日志 762 | getfile-获取jd目录下文件 763 | setshort-设置自定义按钮 764 | getcookie-扫码获取cookie''' 765 | await client.send_message(chat_id, msg) 766 | 767 | 768 | @client.on(events.NewMessage(from_users=chat_id, pattern='/start')) 769 | async def mystart(event): 770 | '''接收/start命令后执行程序''' 771 | msg = '''使用方法如下: 772 | /help 获取命令,可直接发送至botfather 773 | /a 使用你的自定义快捷按钮 774 | /start 开始使用本程序 775 | /node 执行js脚本文件,直接输入/node jd_bean_change 如执行其他自己js,需输入绝对路径。即可进行执行。该命令会等待脚本执行完,期间不能使用机器人,建议使用snode命令。 776 | /cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 不建议使用机器人使用并发,可能产生不明原因的崩溃 777 | /snode 命令可以选择脚本执行,只能选择/scripts 和/own目录下的脚本,选择完后直接后台运行,不影响机器人响应其他命令 778 | /log 选择查看执行日志 779 | /getfile 获取jd目录下文件 780 | /setshort 设置自定义按钮,每次设置会覆盖原设置 781 | /getcookie 扫码获取cookie 增加30s内取消按钮,30s后不能进行其他交互直到2分钟或获取到cookie 782 | /edit 从jd目录下选择文件编辑,需要将编辑好信息全部发给机器人,机器人会根据你发的信息进行替换。建议用来编辑config或crontab.list 其他文件慎用!!! 783 | 此外直接发送文件,会让您选择保存到哪个文件夹,如果选择运行,将保存至own目录下,并立即运行脚本,crontab.list文件会自动更新时间''' 784 | await client.send_message(chat_id, msg) 785 | 786 | with client: 787 | task = client.loop.create_task(hello()) 788 | client.loop.run_forever() 789 | -------------------------------------------------------------------------------- /botql.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | # time: 2021 4 | # newtime: 2021-04-25 5 | # version: 0.1.7 6 | # log: 新增:连接成功,机器人发送通知,修复:没有中文名称脚本不能识别 7 | # author: https://github.com/SuMaiKaDe 8 | 9 | from telethon import TelegramClient, events, Button, connection 10 | import requests 11 | import re 12 | import json 13 | import time 14 | import os 15 | import qrcode 16 | import logging 17 | import subprocess 18 | import shutil 19 | from asyncio import exceptions 20 | logging.basicConfig( 21 | format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO) 22 | logger = logging.getLogger(__name__) 23 | # 定义目录,以bot.py所在目录上级目录为/jd或/ql文件夹 24 | _JdDir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 25 | _ConfigDir = _JdDir + '/config' 26 | _ScriptsDir = _JdDir + '/scripts' 27 | _DiyScripts = _JdDir + '/diyscripts' 28 | _LogDir = _JdDir + '/log' 29 | _shortcut = _ConfigDir + '/shortcut.list' 30 | _bot = _ConfigDir + '/bot.json' 31 | _qr = _ConfigDir + 'qr.jpg' 32 | # 频道id/用户id 33 | with open(_bot, 'r', encoding='utf-8') as f: 34 | bot = json.load(f) 35 | chat_id = int(bot['user_id']) 36 | # 机器人 TOKEN 37 | TOKEN = bot['bot_token'] 38 | # 发消息的TG代理 39 | # my.telegram.org申请到的api_id,api_hash 40 | api_id = bot['api_id'] 41 | api_hash = bot['api_hash'] 42 | proxystart = bot['proxy'] 43 | proxyType = bot['proxy_type'] 44 | connectionType = connection.ConnectionTcpMTProxyRandomizedIntermediate if proxyType == "MTProxy" else connection.ConnectionTcpFull 45 | if 'proxy_user' in bot.keys() and bot['proxy_user'] != "代理的username,有则填写,无则不用动": 46 | proxy = { 47 | 'proxy_type': bot['proxy_type'], 48 | 'addr': bot['proxy_add'], 49 | 'port': bot['proxy_port'], 50 | 'username': bot['proxy_user'], 51 | 'password': bot['proxy_password']} 52 | 53 | elif proxyType == "MTProxy": 54 | proxy = (bot['proxy_add'], bot['proxy_port'], bot['proxy_secret']) 55 | else: 56 | proxy = (bot['proxy_type'], bot['proxy_add'], bot['proxy_port']) 57 | # 开启tg对话 58 | if proxystart: 59 | client = TelegramClient('bot', api_id, api_hash, connection=connectionType, 60 | proxy=proxy, connection_retries=None).start(bot_token=TOKEN) 61 | else: 62 | client = TelegramClient('bot', api_id, api_hash, 63 | connection_retries=None).start(bot_token=TOKEN) 64 | cookiemsg = '' 65 | img_file = _qr 66 | StartCMD = bot['StartCMD'] 67 | 68 | 69 | async def hello(): 70 | await client.send_message(chat_id, 'duang duang duang \n您的机器人已成功激活\n发个 /start 试试吧') 71 | 72 | 73 | def press_event(user_id): 74 | return events.CallbackQuery(func=lambda e: e.sender_id == user_id) 75 | 76 | 77 | # 扫码获取cookie 直接采用LOF大佬代码 78 | # getSToken请求获取,s_token用于发送post请求是的必须参数 79 | s_token = "" 80 | # getSToken请求获取,guid,lsid,lstoken用于组装cookies 81 | guid, lsid, lstoken = "", "", "" 82 | # 由上面参数组装生成,getOKLToken函数发送请求需要使用 83 | cookies = "" 84 | # getOKLToken请求获取,token用户生成二维码使用、okl_token用户检查扫码登录结果使用 85 | token, okl_token = "", "" 86 | # 最终获取到的可用的cookie 87 | jd_cookie = "" 88 | 89 | 90 | def getSToken(): 91 | time_stamp = int(time.time() * 1000) 92 | 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 93 | get_header = { 94 | 'Connection': 'Keep-Alive', 95 | 'Content-Type': 'application/x-www-form-urlencoded', 96 | 'Accept': 'application/json, text/plain, */*', 97 | 'Accept-Language': 'zh-cn', 98 | '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, 99 | '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', 100 | 'Host': 'plogin.m.jd.com' 101 | } 102 | try: 103 | resp = requests.get(url=get_url, headers=get_header) 104 | parseGetRespCookie(resp.headers, resp.json()) 105 | logger.info(resp.headers) 106 | logger.info(resp.json()) 107 | except Exception as error: 108 | logger.exception("Get网络请求异常", error) 109 | 110 | 111 | def parseGetRespCookie(headers, get_resp): 112 | global s_token 113 | global cookies 114 | s_token = get_resp.get('s_token') 115 | set_cookies = headers.get('set-cookie') 116 | logger.info(set_cookies) 117 | guid = re.findall(r"guid=(.+?);", set_cookies)[0] 118 | lsid = re.findall(r"lsid=(.+?);", set_cookies)[0] 119 | lstoken = re.findall(r"lstoken=(.+?);", set_cookies)[0] 120 | cookies = f"guid={guid}; lang=chs; lsid={lsid}; lstoken={lstoken}; " 121 | logger.info(cookies) 122 | 123 | 124 | def getOKLToken(): 125 | post_time_stamp = int(time.time() * 1000) 126 | post_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=%s&v=%s&remember=true' % ( 127 | s_token, post_time_stamp) 128 | post_data = { 129 | 'lang': 'chs', 130 | 'appid': 300, 131 | '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, 132 | 'source': 'wq_passport' 133 | } 134 | post_header = { 135 | 'Connection': 'Keep-Alive', 136 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 137 | 'Accept': 'application/json, text/plain, */*', 138 | 'Cookie': cookies, 139 | '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, 140 | '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', 141 | 'Host': 'plogin.m.jd.com', 142 | } 143 | try: 144 | global okl_token 145 | resp = requests.post( 146 | url=post_url, headers=post_header, data=post_data, timeout=20) 147 | parsePostRespCookie(resp.headers, resp.json()) 148 | logger.info(resp.headers) 149 | except Exception as error: 150 | logger.exception("Post网络请求错误", error) 151 | 152 | 153 | def parsePostRespCookie(headers, data): 154 | global token 155 | global okl_token 156 | token = data.get('token') 157 | okl_token = re.findall(r"okl_token=(.+?);", headers.get('set-cookie'))[0] 158 | logger.info("token:" + token) 159 | logger.info("okl_token:" + okl_token) 160 | 161 | 162 | def parseJDCookies(headers): 163 | global jd_cookie 164 | logger.info("扫码登录成功,下面为获取到的用户Cookie。") 165 | set_cookie = headers.get('Set-Cookie') 166 | pt_key = re.findall(r"pt_key=(.+?);", set_cookie)[0] 167 | pt_pin = re.findall(r"pt_pin=(.+?);", set_cookie)[0] 168 | logger.info(pt_key) 169 | logger.info(pt_pin) 170 | jd_cookie = f'pt_key={pt_key};pt_pin={pt_pin};' 171 | 172 | 173 | def creatqr(text): 174 | '''实例化QRCode生成qr对象''' 175 | qr = qrcode.QRCode( 176 | version=1, 177 | error_correction=qrcode.constants.ERROR_CORRECT_H, 178 | box_size=10, 179 | border=4 180 | ) 181 | qr.clear() 182 | # 传入数据 183 | qr.add_data(text) 184 | qr.make(fit=True) 185 | # 生成二维码 186 | img = qr.make_image() 187 | # 保存二维码 188 | img.save(img_file) 189 | 190 | 191 | def split_list(datas, n, row: bool = True): 192 | """一维列表转二维列表,根据N不同,生成不同级别的列表""" 193 | length = len(datas) 194 | size = length / n + 1 if length % n else length/n 195 | _datas = [] 196 | if not row: 197 | size, n = n, size 198 | for i in range(int(size)): 199 | start = int(i * n) 200 | end = int((i + 1) * n) 201 | _datas.append(datas[start:end]) 202 | return _datas 203 | 204 | 205 | async def backfile(file): 206 | if os.path.exists(file): 207 | try: 208 | os.rename(file, file+'.bak') 209 | except WindowsError: 210 | os.remove(file+'.bak') 211 | os.rename(file, file+'.bak') 212 | 213 | 214 | async def cmd(cmdtext): 215 | '''定义执行cmd命令''' 216 | try: 217 | msg = await client.send_message(chat_id, '开始执行程序,如程序复杂,建议稍等') 218 | res_bytes = subprocess.check_output( 219 | cmdtext, shell=True, stderr=subprocess.STDOUT) 220 | res = res_bytes.decode('utf-8') 221 | if len(res) == 0: 222 | await client.edit_message(msg, '已执行,但返回值为空') 223 | elif len(res) <= 4000: 224 | await client.edit_message(msg, res) 225 | elif len(res) > 4000: 226 | with open(_LogDir+'/botres.log', 'w+', encoding='utf-8') as f: 227 | f.write(res) 228 | await client.send_message(chat_id, '执行结果较长,请查看日志', file=_LogDir+'/botres.log') 229 | except Exception as e: 230 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 231 | logger.error('something wrong,I\'m sorry'+str(e)) 232 | 233 | 234 | async def logbtn(conv, SENDER, path, msg, page, filelist): 235 | '''定义log日志按钮''' 236 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 237 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 238 | try: 239 | if filelist: 240 | markup = filelist 241 | newmarkup = markup[page] 242 | if mybtn not in newmarkup: 243 | newmarkup.append(mybtn) 244 | else: 245 | dir = os.listdir(path) 246 | dir.sort() 247 | markup = [Button.inline(file, data=str(file)) 248 | for file in dir] 249 | markup = split_list(markup, 3) 250 | if len(markup) > 30: 251 | markup = split_list(markup, 30) 252 | newmarkup = markup[page] 253 | newmarkup.append(mybtn) 254 | else: 255 | newmarkup = markup 256 | if path == _JdDir: 257 | newmarkup.append([Button.inline('取消', data='cancel')]) 258 | else: 259 | newmarkup.append( 260 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 261 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 262 | convdata = await conv.wait_event(press_event(SENDER)) 263 | res = bytes.decode(convdata.data) 264 | if res == 'cancel': 265 | msg = await client.edit_message(msg, '对话已取消') 266 | conv.cancel() 267 | return None, None, None, None 268 | elif res == 'next': 269 | page = page + 1 270 | if page > len(markup) - 1: 271 | page = 0 272 | return path, msg, page, markup 273 | elif res == 'up': 274 | page = page - 1 275 | if page < 0: 276 | page = len(markup) - 1 277 | return path, msg, page, markup 278 | elif res == 'updir': 279 | path = '/'.join(path.split('/')[:-1]) 280 | logger.info(path) 281 | if path == '': 282 | path = _JdDir 283 | return path, msg, page, None 284 | elif os.path.isfile(path+'/'+res): 285 | msg = await client.edit_message(msg, '文件发送中,请注意查收') 286 | await conv.send_file(path+'/'+res) 287 | msg = await client.edit_message(msg, res+'发送成功,请查收') 288 | conv.cancel() 289 | return None, None, None, None 290 | else: 291 | return path+'/'+res, msg, page, None 292 | except exceptions.TimeoutError: 293 | msg = await client.edit_message(msg, '选择已超时,本次对话已停止') 294 | return None, None, None, None 295 | except Exception as e: 296 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 297 | logger.error('something wrong,I\'m sorry\n'+str(e)) 298 | return None, None, None, None 299 | 300 | 301 | async def getname(path, dir): 302 | names = [] 303 | reg = r'new Env\(\'[\S]+?\'\)' 304 | cname = False 305 | for file in dir: 306 | if os.path.isdir(path+'/'+file): 307 | names.append(file) 308 | elif file.endswith('.js') and file != 'jdCookie.js' and file != 'getJDCookie.js' and file != 'JD_extra_cookie.js': 309 | with open(path+'/'+file, 'r', encoding='utf-8') as f: 310 | resdatas = f.readlines() 311 | for data in resdatas: 312 | if 'new Env' in data: 313 | data = data.replace('\"', '\'') 314 | res = re.findall(reg, data) 315 | if len(res) != 0: 316 | res = res[0].split('\'')[-2] 317 | names.append(res+'--->'+file) 318 | cname = True 319 | break 320 | if not cname: 321 | names.append(file+'--->'+file) 322 | cname = False 323 | else: 324 | continue 325 | return names 326 | 327 | 328 | async def nodebtn(conv, SENDER, path, msg, page, filelist): 329 | '''定义scripts脚本按钮''' 330 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 331 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 332 | try: 333 | if filelist: 334 | markup = filelist 335 | newmarkup = markup[page] 336 | if mybtn not in newmarkup: 337 | newmarkup.append(mybtn) 338 | else: 339 | if path == _JdDir: 340 | dir = ['scripts', 'diyscripts'] 341 | else: 342 | dir = os.listdir(path) 343 | dir = await getname(path, dir) 344 | dir.sort() 345 | markup = [Button.inline(file.split('--->')[0], data=str(file.split('--->')[-1])) 346 | for file in dir if os.path.isdir(path+'/'+file) or re.search(r'.js$', file.split('--->')[-1])] 347 | markup = split_list(markup, 3) 348 | if len(markup) > 30: 349 | markup = split_list(markup, 30) 350 | newmarkup = markup[page] 351 | newmarkup.append(mybtn) 352 | else: 353 | newmarkup = markup 354 | if path == _JdDir: 355 | newmarkup.append([Button.inline('取消', data='cancel')]) 356 | else: 357 | newmarkup.append( 358 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 359 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 360 | convdata = await conv.wait_event(press_event(SENDER)) 361 | res = bytes.decode(convdata.data) 362 | if res == 'cancel': 363 | msg = await client.edit_message(msg, '对话已取消') 364 | conv.cancel() 365 | return None, None, None 366 | elif res == 'next': 367 | page = page + 1 368 | if page > len(markup) - 1: 369 | page = 0 370 | return path, msg, page, markup 371 | elif res == 'up': 372 | page = page - 1 373 | if page < 0: 374 | page = len(markup) - 1 375 | return path, msg, page, markup 376 | elif res == 'updir': 377 | path = '/'.join(path.split('/')[:-1]) 378 | if path == '': 379 | path = _JdDir 380 | return path, msg, page, None 381 | elif os.path.isfile(path+'/'+res): 382 | msg = await client.edit_message(msg, '脚本即将在后台运行') 383 | logger.info(path+'/'+res+'脚本即将在后台运行') 384 | cmdtext = 'js {} now'.format(res) 385 | subprocess.Popen(cmdtext, shell=True, 386 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 387 | msg = await client.edit_message(msg, res + '在后台运行成功,请自行在程序结束后查看日志') 388 | conv.cancel() 389 | return None, None, None, None 390 | else: 391 | return path+'/'+res, msg, page, None 392 | except exceptions.TimeoutError: 393 | msg = await client.edit_message(msg, '选择已超时,对话已停止') 394 | return None, None, None, None 395 | except Exception as e: 396 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 397 | logger.error('something wrong,I\'m sorry\n'+str(e)) 398 | return None, None, None, None 399 | 400 | 401 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/log')) 402 | async def mylog(event): 403 | '''定义日志文件操作''' 404 | SENDER = event.sender_id 405 | path = _LogDir 406 | page = 0 407 | filelist = None 408 | async with client.conversation(SENDER, timeout=60) as conv: 409 | msg = await conv.send_message('正在查询,请稍后') 410 | while path: 411 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 412 | 413 | 414 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode')) 415 | async def mysnode(event): 416 | '''定义supernode文件命令''' 417 | SENDER = event.sender_id 418 | path = _JdDir 419 | page = 0 420 | filelist = None 421 | async with client.conversation(SENDER, timeout=60) as conv: 422 | msg = await conv.send_message('正在查询,请稍后') 423 | while path: 424 | path, msg, page, filelist = await nodebtn(conv, SENDER, path, msg, page, filelist) 425 | 426 | 427 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile')) 428 | async def mygetfile(event): 429 | '''定义获取文件命令''' 430 | SENDER = event.sender_id 431 | path = _JdDir 432 | page = 0 433 | filelist = None 434 | async with client.conversation(SENDER, timeout=60) as conv: 435 | msg = await conv.send_message('正在查询,请稍后') 436 | while path: 437 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 438 | 439 | 440 | @client.on(events.NewMessage(from_users=chat_id)) 441 | async def myfile(event): 442 | '''定义文件操作''' 443 | try: 444 | SENDER = event.sender_id 445 | if event.message.file: 446 | markup = [] 447 | filename = event.message.file.name 448 | async with client.conversation(SENDER, timeout=30) as conv: 449 | msg = await conv.send_message('请选择您要放入的文件夹或操作:\n') 450 | markup.append([Button.inline('放入config', data=_ConfigDir), Button.inline( 451 | '放入scripts', data=_ScriptsDir), Button.inline('放入diyscripts', data=_DiyScripts)]) 452 | markup.append( 453 | [Button.inline('放入diyscripts并运行', data='node'), Button.inline('取消', data='cancel')]) 454 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=markup) 455 | convdata = await conv.wait_event(press_event(SENDER)) 456 | res = bytes.decode(convdata.data) 457 | if res == 'cancel': 458 | msg = await client.edit_message(msg, '对话已取消') 459 | conv.cancel() 460 | elif res == 'node': 461 | await backfile(_DiyScripts+'/'+filename) 462 | await client.download_media(event.message, _DiyScripts) 463 | cmdtext = 'js {}/{} now'.format(_DiyScripts, filename) 464 | subprocess.Popen( 465 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 466 | await client.edit_message(msg, '脚本已保存到DIYScripts文件夹,并成功在后台运行,请稍后自行查看日志') 467 | conv.cancel() 468 | else: 469 | await backfile(res+'/'+filename) 470 | await client.download_media(event.message, res) 471 | await client.edit_message(msg, filename+'已保存到'+res+'文件夹') 472 | if filename == 'crontab.list': 473 | cmdtext = 'crontab '+res+'/'+filename 474 | subprocess.Popen( 475 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 476 | await client.edit_message(msg, '定时文件已保存,并更新') 477 | conv.cancel() 478 | except exceptions.TimeoutError: 479 | msg = await client.send_message(chat_id, '选择已超时,对话已停止') 480 | except Exception as e: 481 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 482 | logger.error('something wrong,I\'m sorry\n'+str(e)) 483 | 484 | 485 | @client.on(events.NewMessage(from_users=chat_id, pattern='/edit')) 486 | async def myfileup(event): 487 | '''定义编辑文件操作''' 488 | SENDER = event.sender_id 489 | path = _JdDir 490 | page = 0 491 | filelist = None 492 | async with client.conversation(SENDER, timeout=60) as conv: 493 | msg = await conv.send_message('正在查询,请稍后') 494 | while path: 495 | path, msg, page, filelist = await myedit(conv, SENDER, path, msg, page, filelist) 496 | 497 | 498 | async def myedit(conv, SENDER, path, msg, page, filelist): 499 | mybtn = [Button.inline('上一页', data='up'), Button.inline('下一页', data='next'), Button.inline( 500 | '上级', data='updir'), Button.inline('取消', data='cancel')] 501 | mybtn2 = [[Button.inline('上一页', data='up'), Button.inline( 502 | '下一页', data='next'), Button.inline('取消', data='cancel')], [Button.inline('上十页', data='up10'), Button.inline( 503 | '下十页', data='next10'), Button.inline('编辑', data='edit')]] 504 | try: 505 | if filelist and type(filelist[0][0]) == str: 506 | markup = filelist 507 | newmarkup = markup[page] 508 | msg = await client.edit_message(msg, "".join(newmarkup), buttons=mybtn2) 509 | else: 510 | if filelist: 511 | markup = filelist 512 | newmarkup = markup[page] 513 | if mybtn not in newmarkup: 514 | newmarkup.append(mybtn) 515 | else: 516 | dir = os.listdir(path) 517 | dir.sort() 518 | markup = [Button.inline(file, data=str( 519 | file)) for file in dir] 520 | markup = split_list(markup, 3) 521 | if len(markup) > 30: 522 | markup = split_list(markup, 30) 523 | newmarkup = markup[page] 524 | newmarkup.append(mybtn) 525 | else: 526 | newmarkup = markup 527 | if path == _JdDir: 528 | newmarkup.append([Button.inline('取消', data='cancel')]) 529 | else: 530 | newmarkup.append( 531 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 532 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 533 | convdata = await conv.wait_event(press_event(SENDER)) 534 | res = bytes.decode(convdata.data) 535 | if res == 'cancel': 536 | msg = await client.edit_message(msg, '对话已取消') 537 | conv.cancel() 538 | return None, None, None, None, None 539 | elif res == 'next': 540 | page = page + 1 541 | if page > len(markup) - 1: 542 | page = 0 543 | return path, msg, page, markup 544 | elif res == 'up': 545 | page = page - 1 546 | if page < 0: 547 | page = len(markup) - 1 548 | return path, msg, page, markup 549 | elif res == 'next10': 550 | page = page + 10 551 | if page > len(markup) - 1: 552 | page = 0 553 | return path, msg, page, markup 554 | elif res == 'up10': 555 | page = page - 10 556 | if page < 0: 557 | page = len(markup) - 1 558 | return path, msg, page, markup 559 | elif res == 'updir': 560 | path = '/'.join(path.split('/')[:-1]) 561 | if path == '': 562 | path = _JdDir 563 | return path, msg, page, None 564 | elif res == 'edit': 565 | await client.send_message(chat_id, '请复制并修改以下内容,修改完成后发回机器人,2分钟内有效') 566 | await client.delete_messages(chat_id, msg) 567 | msg = await conv.send_message("".join(newmarkup)) 568 | resp = await conv.get_response() 569 | markup[page] = resp.raw_text.split('\n') 570 | for a in range(len(markup[page])): 571 | markup[page][a] = markup[page][a]+'\n' 572 | shutil.copy(path, path+'.bak') 573 | with open(path, 'w+', encoding='utf-8') as f: 574 | markup = ["".join(a) for a in markup] 575 | f.writelines(markup) 576 | await client.send_message(chat_id, '文件已修改成功,原文件备份为'+path+'.bak') 577 | conv.cancel() 578 | return None, None, None, None 579 | elif os.path.isfile(path+'/'+res): 580 | msg = await client.edit_message(msg, '文件读取中...请稍候') 581 | with open(path+'/'+res, 'r', encoding='utf-8') as f: 582 | lines = f.readlines() 583 | lines = split_list(lines, 15) 584 | page = 0 585 | return path+'/'+res, msg, page, lines 586 | else: 587 | return path+'/'+res, msg, page, None 588 | except exceptions.TimeoutError: 589 | msg = await client.edit_message(msg, '选择已超时,本次对话已停止') 590 | return None, None, None, None 591 | except Exception as e: 592 | msg = await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 593 | logger.error('something wrong,I\'m sorry\n'+str(e)) 594 | return None, None, None, None 595 | 596 | 597 | @client.on(events.NewMessage(from_users=chat_id, pattern='/node')) 598 | async def mynode(event): 599 | '''接收/node命令后执行程序''' 600 | nodereg = re.compile(r'^/node [\S]+') 601 | text = re.findall(nodereg, event.raw_text) 602 | if len(text) == 0: 603 | res = '''请正确使用/node命令,如 604 | /node /abc/123.js 运行abc/123.js脚本 605 | /node /own/abc.js 运行own/abc.js脚本 606 | ''' 607 | await client.send_message(chat_id, res) 608 | else: 609 | await cmd('js '+text[0].replace('/node ', '')+' now') 610 | 611 | 612 | @client.on(events.NewMessage(from_users=chat_id, pattern='/cmd')) 613 | async def mycmd(event): 614 | '''接收/cmd命令后执行程序''' 615 | if StartCMD: 616 | cmdreg = re.compile(r'^/cmd [\s\S]+') 617 | text = re.findall(cmdreg, event.raw_text) 618 | if len(text) == 0: 619 | msg = '''请正确使用/cmd命令,如 620 | /cmd python3 /python/bot.py 运行/python目录下的bot文件 621 | /cmd diy monk-coder dust i-chenzhe 拉取自定义仓库自定义目录 622 | /cmd export_sharecodes 获取互助码 623 | /cmd ps 获取当前docker内进行 624 | ''' 625 | await client.send_message(chat_id, msg) 626 | else: 627 | logger.info(text) 628 | await cmd(text[0].replace('/cmd ', '')) 629 | else: 630 | await client.send_message(chat_id, '未开启CMD命令,如需使用请修改配置文件') 631 | 632 | 633 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/getcookie')) 634 | async def mycookie(event): 635 | '''接收/getcookie后执行程序''' 636 | login = True 637 | msg = await client.send_message(chat_id, '正在获取二维码,请稍后') 638 | global cookiemsg 639 | try: 640 | SENDER = event.sender_id 641 | async with client.conversation(SENDER, timeout=30) as conv: 642 | getSToken() 643 | getOKLToken() 644 | url = 'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token='+token 645 | creatqr(url) 646 | markup = [Button.inline("已扫码", data='confirm'), 647 | Button.inline("取消", data='cancel')] 648 | await client.delete_messages(chat_id, msg) 649 | cookiemsg = await client.send_message(chat_id, '30s内点击取消将取消本次操作\n如不取消,扫码结果将于30s后显示\n扫码后不想等待点击已扫码', file=img_file, buttons=markup) 650 | convdata = await conv.wait_event(press_event(SENDER)) 651 | res = bytes.decode(convdata.data) 652 | if res == 'cancel': 653 | login = False 654 | await client.delete_messages(chat_id, cookiemsg) 655 | msg = await conv.send_message('对话已取消') 656 | conv.cancel() 657 | else: 658 | raise exceptions.TimeoutError() 659 | except exceptions.TimeoutError: 660 | expired_time = time.time() + 60 * 2 661 | while login: 662 | check_time_stamp = int(time.time() * 1000) 663 | check_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=%s&ou_state=0&okl_token=%s' % ( 664 | token, okl_token) 665 | check_data = { 666 | 'lang': 'chs', 667 | 'appid': 300, 668 | '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, 669 | 'source': 'wq_passport' 670 | } 671 | check_header = { 672 | '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, 673 | 'Cookie': cookies, 674 | 'Connection': 'Keep-Alive', 675 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 676 | 'Accept': 'application/json, text/plain, */*', 677 | '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', 678 | } 679 | resp = requests.post( 680 | url=check_url, headers=check_header, data=check_data, timeout=30) 681 | data = resp.json() 682 | if data.get("errcode") == 0: 683 | parseJDCookies(resp.headers) 684 | await client.delete_messages(chat_id, cookiemsg) 685 | await client.send_message(chat_id, '以下为获取到的cookie') 686 | await client.send_message(chat_id, jd_cookie) 687 | return 688 | if data.get("errcode") == 21: 689 | await client.delete_messages(chat_id, cookiemsg) 690 | await client.send_message(chat_id, '发生了某些错误\n'+data.get("errcode")) 691 | return 692 | if time.time() > expired_time: 693 | await client.delete_messages(chat_id, cookiemsg) 694 | await client.send_message(chat_id, '超过3分钟未扫码,二维码已过期') 695 | return 696 | except Exception as e: 697 | await client.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 698 | logger.error('something wrong,I\'m sorry\n'+str(e)) 699 | 700 | 701 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/setshort$')) 702 | async def setshortcut(event): 703 | SENDER = event.sender_id 704 | async with client.conversation(SENDER, timeout=60) as conv: 705 | await conv.send_message( 706 | '60s内回复有效\n请按格式输入您的快捷命令。例如:\n京豆通知-->js 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 js jd_bean_change now\n如不添加nohup 会等待程序执行完,期间不能交互\n建议运行时间短命令不添加nohup ') 707 | shortcut = await conv.get_response() 708 | with open(_shortcut, 'w+', encoding='utf-8') as f: 709 | f.write(shortcut.raw_text) 710 | await conv.send_message('已设置成功可通过"/a"使用') 711 | conv.cancel() 712 | 713 | 714 | @client.on(events.NewMessage(from_users=chat_id, pattern=r'^/a$')) 715 | async def shortcut(event): 716 | markup = [] 717 | SENDER = event.sender_id 718 | msg = await client.send_message(chat_id, '正在查询您的常用命令,请稍后') 719 | with open(_shortcut, 'r', encoding='utf-8') as f: 720 | shortcuts = f.readlines() 721 | try: 722 | async with client.conversation(SENDER, timeout=60) as conv: 723 | markup = [Button.inline(shortcut.split( 724 | '-->')[0], data=str(shortcut.split('-->')[-1])) for shortcut in shortcuts] 725 | markup = split_list(markup, 3) 726 | markup.append([Button.inline('取消', data='cancel')]) 727 | msg = await client.edit_message(msg, '请做出您的选择:', buttons=markup) 728 | convdata = await conv.wait_event(press_event(SENDER)) 729 | res = bytes.decode(convdata.data) 730 | if res == 'cancel': 731 | msg = await client.edit_message(msg, '对话已取消') 732 | conv.cancel() 733 | elif 'nohup ' in res: 734 | msg = await client.edit_message(msg, '即将执行您的操作'+res) 735 | cmdtext = res.replace('nohup ', '') 736 | subprocess.Popen( 737 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 738 | msg = await client.edit_message(msg, '已在后台执行您的操作'+res.replace('nohup ', '')) 739 | conv.cancel() 740 | else: 741 | await client.delete_messages(chat_id, msg) 742 | await cmd(res) 743 | conv.cancel() 744 | except exceptions.TimeoutError: 745 | msg = await client.edit_message(msg, '选择已超时,对话已停止') 746 | except Exception as e: 747 | await client.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 748 | logger.error('something wrong,I\'m sorry\n'+str(e)) 749 | 750 | 751 | @client.on(events.NewMessage(from_users=chat_id, pattern='/help')) 752 | async def myhelp(event): 753 | '''接收/help命令后执行程序''' 754 | msg = ''' 755 | a-我的自定义快捷按钮 756 | edit-编辑文件 757 | start-开始使用本程序 758 | node-执行js脚本文件,绝对路径。 759 | cmd-执行cmd命令 760 | snode-选择脚本后台运行 761 | log-选择日志 762 | getfile-获取jd目录下文件 763 | setshort-设置自定义按钮 764 | getcookie-扫码获取cookie''' 765 | await client.send_message(chat_id, msg) 766 | 767 | 768 | @client.on(events.NewMessage(from_users=chat_id, pattern='/start')) 769 | async def mystart(event): 770 | '''接收/start命令后执行程序''' 771 | msg = '''使用方法如下: 772 | /help 获取命令,可直接发送至botfather 773 | /a 使用你的自定义快捷按钮 774 | /start 开始使用本程序 775 | /node 执行js脚本文件,直接输入/node jd_bean_change 如执行其他自己js,需输入绝对路径。即可进行执行。该命令会等待脚本执行完,期间不能使用机器人,建议使用snode命令。 776 | /cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 不建议使用机器人使用并发,可能产生不明原因的崩溃 777 | /snode 命令可以选择脚本执行,只能选择/scripts 和/own目录下的脚本,选择完后直接后台运行,不影响机器人响应其他命令 778 | /log 选择查看执行日志 779 | /getfile 获取jd目录下文件 780 | /setshort 设置自定义按钮,每次设置会覆盖原设置 781 | /getcookie 扫码获取cookie 增加30s内取消按钮,30s后不能进行其他交互直到2分钟或获取到cookie 782 | /edit 从jd目录下选择文件编辑,需要将编辑好信息全部发给机器人,机器人会根据你发的信息进行替换。建议用来编辑config或crontab.list 其他文件慎用!!! 783 | 此外直接发送文件,会让您选择保存到哪个文件夹,如果选择运行,将保存至own目录下,并立即运行脚本,crontab.list文件会自动更新时间''' 784 | await client.send_message(chat_id, msg) 785 | 786 | with client: 787 | task = client.loop.create_task(hello()) 788 | client.loop.run_forever() 789 | -------------------------------------------------------------------------------- /config/bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "//开头的的都是注释,不要动,剩下的都按要求改自己的", 3 | "//user_id": "↓↓↓ 你的USERID,去除双引号 ↓↓↓", 4 | "user_id": 123456789, 5 | "//bot_token": "↓↓↓ 你的机器人TOKEN ↓↓↓", 6 | "bot_token": "123456789:ABCDEFGSHSFDASDFAD", 7 | "//api_id": "↓↓↓ https://my.telegram.org 在该网站申请到的id ↓↓↓", 8 | "api_id": "456423156", 9 | "//api_hash": "↓↓↓ https://my.telegram.org 在该网站申请到的hash ↓↓↓", 10 | "api_hash": "ASDFAWEFADSFAWEFDSFASFD", 11 | "//proxy": "↓↓↓ 使用代理改成true,不使用下方带proxy的不用动 ↓↓↓", 12 | "proxy": false, 13 | "//proxy_type": "↓↓↓ socks5 或者 http 或者 MTProxy ↓↓↓", 14 | "proxy_type": "socks5", 15 | "//proxy_add": "↓↓↓ 代理IP地址例如:192.168.99.100 ↓↓↓", 16 | "proxy_add": "192.168.99.100", 17 | "//proxy_port": "↓↓↓ 代理端口,不需要双引号例如 5890 ↓↓↓", 18 | "proxy_port": 5890, 19 | "//proxy_secret": "↓↓↓ MTProxy代理秘钥 ↓↓↓", 20 | "proxy_secret": "", 21 | "//proxy_user": "↓↓↓ 代理的username,有就改,没有就不要动 ↓↓↓", 22 | "proxy_user": "代理的username,有则填写,无则不用动", 23 | "//proxy_password": "↓↓↓ 代理的密码,有则填写,无则不用动 ↓↓↓", 24 | "proxy_password": "代理的密码,有则填写,无则不用动", 25 | "//StartCMD": "↓↓↓ 是否开启CMD命令,开启改成true ↓↓↓", 26 | "StartCMD": false, 27 | "//noretry": "↓↓↓ 是否 关闭 bot掉线重连,默认开启,关闭改成true ↓↓↓", 28 | "noretry": false 29 | } 30 | -------------------------------------------------------------------------------- /jbot/__init__.py: -------------------------------------------------------------------------------- 1 | from telethon import TelegramClient, connection 2 | import json 3 | import os 4 | import logging 5 | 6 | _JdDir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 7 | _ConfigDir = _JdDir + '/config' 8 | _ScriptsDir = _JdDir + '/scripts' 9 | _OwnDir = _JdDir + '/own' 10 | _JdbotDir = _JdDir + '/jbot' 11 | _DiyScripts = _JdDir + '/diyscripts' 12 | _LogDir = _JdDir + '/log' 13 | _shortcut = _ConfigDir + '/shortcut.list' 14 | _botlog = _LogDir + '/bot/run.log' 15 | _botjson = _ConfigDir + '/bot.json' 16 | img_file = _ConfigDir + 'qr.jpg' 17 | if not os.path.exists(_LogDir + '/bot'): 18 | os.mkdir(_LogDir + '/bot') 19 | logging.basicConfig( 20 | format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO, filename=_botlog, 21 | filemode='w') 22 | logger = logging.getLogger(__name__) 23 | 24 | with open(_botjson, 'r', encoding='utf-8') as f: 25 | bot = json.load(f) 26 | chat_id = int(bot['user_id']) 27 | # 机器人 TOKEN 28 | TOKEN = bot['bot_token'] 29 | # HOSTAPI = bot['apihost'] 30 | # 发消息的TG代理 31 | # my.telegram.org申请到的api_id,api_hash 32 | api_id = bot['api_id'] 33 | api_hash = bot['api_hash'] 34 | proxystart = bot['proxy'] 35 | StartCMD = bot['StartCMD'] 36 | proxyType = bot['proxy_type'] 37 | connectionType = connection.ConnectionTcpMTProxyRandomizedIntermediate if proxyType == "MTProxy" else connection.ConnectionTcpFull 38 | if 'proxy_user' in bot.keys() and bot['proxy_user'] != "代理的username,有则填写,无则不用动": 39 | proxy = { 40 | 'proxy_type': bot['proxy_type'], 41 | 'addr': bot['proxy_add'], 42 | 'port': bot['proxy_port'], 43 | 'username': bot['proxy_user'], 44 | 'password': bot['proxy_password']} 45 | 46 | elif proxyType == "MTProxy": 47 | proxy = (bot['proxy_add'], bot['proxy_port'], bot['proxy_secret']) 48 | else: 49 | proxy = (bot['proxy_type'], bot['proxy_add'], bot['proxy_port']) 50 | # 开启tg对话 51 | if proxystart and 'noretry' in bot.keys() and bot['noretry']: 52 | jdbot = TelegramClient('bot', api_id, api_hash, connection=connectionType, 53 | proxy=proxy).start(bot_token=TOKEN) 54 | elif proxystart: 55 | jdbot = TelegramClient('bot', api_id, api_hash, connection=connectionType, 56 | proxy=proxy, connection_retries=None).start(bot_token=TOKEN) 57 | elif 'noretry' in bot.keys() and bot['noretry']: 58 | jdbot = TelegramClient('bot', api_id, api_hash).start(bot_token=TOKEN) 59 | else: 60 | jdbot = TelegramClient('bot', api_id, api_hash, 61 | connection_retries=None).start(bot_token=TOKEN) 62 | -------------------------------------------------------------------------------- /jbot/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | # 0.3 版本开始不再区分ql、V3、V4。运行日志:log/bot/run.log 4 | # author: https://github.com/SuMaiKaDe 5 | 6 | from . import jdbot, chat_id, logger,_JdbotDir, _LogDir 7 | from .utils import load_diy 8 | import os 9 | from .bot.update import version,botlog 10 | _botuplog = _LogDir + '/bot/up.log' 11 | botpath = _JdbotDir + "/bot/" 12 | diypath = _JdbotDir + "/diy/" 13 | logger.info('loading bot module...') 14 | load_diy('bot', botpath) 15 | logger.info('loading diy module...') 16 | load_diy('diy', diypath) 17 | 18 | async def hello(): 19 | if os.path.exists(_botuplog): 20 | isnew = False 21 | with open(_botuplog, 'r', encoding='utf-8') as f: 22 | logs = f.readlines() 23 | for log in logs: 24 | if version in log: 25 | isnew = True 26 | return 27 | if not isnew: 28 | with open(_botuplog, 'a', encoding='utf-8') as f: 29 | f.writelines([version, botlog]) 30 | await jdbot.send_message(chat_id, '[机器人上新了](https://github.com/SuMaiKaDe/jddockerbot/tree/master)\n'+botlog+'\n运行日志为log/bot/run.log', link_preview=False) 31 | else: 32 | with open(_botuplog, 'w+', encoding='utf-8') as f: 33 | f.writelines([version, botlog]) 34 | await jdbot.send_message(chat_id, '[机器人上新了](https://github.com/SuMaiKaDe/jddockerbot/tree/master)\n'+botlog+'\n运行日志为log/bot/run.log', link_preview=False) 35 | if __name__ == "__main__": 36 | with jdbot: 37 | jdbot.loop.create_task(hello()) 38 | jdbot.loop.run_forever() 39 | -------------------------------------------------------------------------------- /jbot/bot/bean.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageFont, ImageDraw 2 | from telethon import events 3 | from .. import jdbot, chat_id, _LogDir, _JdbotDir,logger 4 | from prettytable import PrettyTable 5 | import subprocess 6 | from .beandata import get_bean_data 7 | IN = _LogDir + '/bean_income.csv' 8 | OUT = _LogDir + '/bean_outlay.csv' 9 | TOTAL = _LogDir + '/bean_total.csv' 10 | _botimg = _LogDir + '/bean.jpg' 11 | _font = _JdbotDir + '/font/jet.ttf' 12 | 13 | 14 | @jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/bean')) 15 | async def mybean(event): 16 | try: 17 | await jdbot.send_message(chat_id, '正在查询,请稍后') 18 | if len(event.raw_text.split(' ')) > 1: 19 | text = event.raw_text.replace('/bean ', '') 20 | else: 21 | text = None 22 | if text and text == 'in': 23 | subprocess.check_output( 24 | 'jcsv', shell=True, stderr=subprocess.STDOUT) 25 | creat_bean_counts(IN) 26 | await jdbot.send_message(chat_id, '您的近日收入情况', file=_botimg) 27 | elif text and text == 'out': 28 | subprocess.check_output( 29 | 'jcsv', shell=True, stderr=subprocess.STDOUT) 30 | creat_bean_counts(OUT) 31 | await jdbot.send_message(chat_id, '您的近日支出情况', file=_botimg) 32 | elif text and int(text): 33 | beanin, beanout, beanstotal,date = get_bean_data(int(text)) 34 | if not beanout: 35 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(beanin)) 36 | else: 37 | creat_bean_count(date,beanin, beanout, beanstotal[1:]) 38 | await jdbot.send_message(chat_id, f'您的账号{text}收支情况', file=_botimg) 39 | else: 40 | subprocess.check_output( 41 | 'jcsv', shell=True, stderr=subprocess.STDOUT) 42 | creat_bean_counts(TOTAL) 43 | await jdbot.send_message(chat_id, '您的总京豆情况', file=_botimg) 44 | except Exception as e: 45 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 46 | logger.error('something wrong,I\'m sorry'+str(e)) 47 | 48 | def creat_bean_count(date,beansin,beansout,beanstotal): 49 | tb = PrettyTable() 50 | tb.add_column('DATE',date) 51 | tb.add_column('BEANIN',beansin) 52 | tb.add_column('BEANOUT',beansout) 53 | tb.add_column('TOTAL',beanstotal) 54 | font = ImageFont.truetype(_font, 18) 55 | im = Image.new("RGB", (500, 260), (244, 244, 244)) 56 | dr = ImageDraw.Draw(im) 57 | dr.text((10, 5), str(tb), font=font, fill="#000000") 58 | im.save(_botimg) 59 | 60 | def creat_bean_counts(csv_file): 61 | with open(csv_file, 'r', encoding='utf-8') as f: 62 | data = f.readlines() 63 | tb = PrettyTable() 64 | num = len(data[-1].split(',')) - 1 65 | title = ['DATE'] 66 | for i in range(0, num): 67 | title.append('COUNT'+str(i+1)) 68 | tb.field_names = title 69 | data = data[-7:] 70 | for line in data: 71 | row = line.split(',') 72 | if len(row) > len(title): 73 | row = row[:len(title)] 74 | elif len(row) < len(title): 75 | i = len(title) - len(row) 76 | for _ in range(0,i): 77 | row.append(0) 78 | tb.add_row(row) 79 | length = 172 + 100 * num 80 | im = Image.new("RGB", (length, 400), (244, 244, 244)) 81 | dr = ImageDraw.Draw(im) 82 | font = ImageFont.truetype(_font, 18) 83 | dr.text((10, 5), str(tb), font=font, fill="#000000") 84 | im.save(_botimg) 85 | -------------------------------------------------------------------------------- /jbot/bot/beandata.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import datetime 3 | import time 4 | import json 5 | from datetime import timedelta 6 | from datetime import timezone 7 | from .utils import cookies 8 | SHA_TZ = timezone( 9 | timedelta(hours=8), 10 | name='Asia/Shanghai', 11 | ) 12 | 13 | 14 | session = requests.session() 15 | 16 | 17 | url = "https://api.m.jd.com/api" 18 | 19 | 20 | def getbody(page): 21 | body = { 22 | "beginDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), 23 | "endDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), 24 | "pageNo": page, 25 | "pageSize": 20, 26 | } 27 | return body 28 | 29 | 30 | def getparms(page): 31 | body = getbody(page) 32 | parms = { 33 | "functionId": "jposTradeQuery", 34 | "appid": "swat_miniprogram", 35 | "client": "tjj_m", 36 | "sdkName": "orderDetail", 37 | "sdkVersion": "1.0.0", 38 | "clientVersion": "3.1.3", 39 | "timestamp": int(round(time.time() * 1000)), 40 | "body": json.dumps(body) 41 | } 42 | return parms 43 | 44 | 45 | def getbeans(ck): 46 | _7day = True 47 | page = 0 48 | headers = { 49 | "Host": "api.m.jd.com", 50 | "Connection": "keep-alive", 51 | "charset": "utf-8", 52 | "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", 53 | "Content-Type": "application/x-www-form-urlencoded;", 54 | "Accept-Encoding": "gzip, compress, deflate, br", 55 | "Cookie": ck, 56 | "Referer": "https://servicewechat.com/wxa5bf5ee667d91626/141/page-frame.html", 57 | } 58 | _7days = [] 59 | for i in range(0, 7): 60 | _7days.append( 61 | (datetime.date.today() - datetime.timedelta(days=i)).strftime("%Y-%m-%d")) 62 | beansin = {key: 0 for key in _7days} 63 | beansout = {key: 0 for key in _7days} 64 | while _7day: 65 | page = page + 1 66 | resp = session.get(url, params=getparms(page), headers=headers).text 67 | res = json.loads(resp) 68 | if res['resultCode'] == 0: 69 | for i in res['data']['list']: 70 | for date in _7days: 71 | if str(date) in i['createDate'] and i['amount'] > 0: 72 | beansin[str(date)] = beansin[str(date)] + i['amount'] 73 | break 74 | elif str(date) in i['createDate'] and i['amount'] < 0: 75 | beansout[str(date)] = beansout[str(date)] + i['amount'] 76 | break 77 | if i['createDate'].split(' ')[0] not in str(_7days): 78 | _7day = False 79 | else: 80 | return 'error' + str(res), None, None 81 | return beansin, beansout, _7days 82 | 83 | 84 | def getTotal(ck): 85 | headers = { 86 | "Host": "wxapp.m.jd.com", 87 | "Connection": "keep-alive", 88 | "charset": "utf-8", 89 | "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", 90 | "Content-Type": "application/x-www-form-urlencoded;", 91 | "Accept-Encoding": "gzip, compress, deflate, br", 92 | "Cookie": ck, 93 | } 94 | jurl = "https://wxapp.m.jd.com/kwxhome/myJd/home.json" 95 | resp = session.get(jurl, headers=headers).text 96 | res = json.loads(resp) 97 | return res['user']['jingBean'] 98 | 99 | 100 | def get_bean_data(i): 101 | ck = cookies[i-1] 102 | beansin, beansout, _7days = getbeans(ck) 103 | beantotal = getTotal(ck) 104 | if not beansout: 105 | return str(beansin), None, None,None 106 | else: 107 | beanin, beanout = [], [] 108 | beanstotal = [int(beantotal), ] 109 | for i in beansin: 110 | beantotal = int(beantotal) - int(beansin[i]) - int(beansout[i]) 111 | beanin.append(beansin[i]) 112 | beanout.append(int(str(beansout[i]).replace('-', ''))) 113 | beanstotal.append(beantotal) 114 | return beanin[::-1], beanout[::-1], beanstotal[::-1], _7days[::-1] 115 | -------------------------------------------------------------------------------- /jbot/bot/chart.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, _LogDir, logger 3 | from ..bot.quickchart import QuickChart 4 | from .beandata import get_bean_data 5 | _botimg = _LogDir + '/bot/bean.jpeg' 6 | 7 | @jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/chart')) 8 | async def mybean(event): 9 | try: 10 | await jdbot.send_message(chat_id, '正在查询,请稍后') 11 | if len(event.raw_text.split(' ')) > 1: 12 | text = event.raw_text.replace('/chart ', '') 13 | else: 14 | text = None 15 | if text and int(text): 16 | beanin, beanout, beanstotal, date = get_bean_data(int(text)) 17 | if not beanout: 18 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(beanin)) 19 | else: 20 | creat_chart(date, '账号'+str(text), 21 | beanin, beanout, beanstotal[1:]) 22 | await jdbot.send_message(chat_id, f'您的账号{text}收支情况', file=_botimg) 23 | else: 24 | await jdbot.send_message(chat_id, '请正确使用命令\n/chart n n为第n个账号') 25 | except Exception as e: 26 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 27 | logger.error('something wrong,I\'m sorry'+str(e)) 28 | 29 | 30 | def creat_chart(xdata, title, bardata, bardata2, linedate): 31 | qc = QuickChart() 32 | qc.background_color = '#fff' 33 | qc.width = "1000" 34 | qc.height = "600" 35 | qc.config = { 36 | "type": "bar", 37 | "data": { 38 | "labels": xdata, 39 | "datasets": [ 40 | { 41 | "label": "IN", 42 | "backgroundColor": [ 43 | "rgb(255, 99, 132)", 44 | "rgb(255, 159, 64)", 45 | "rgb(255, 205, 86)", 46 | "rgb(75, 192, 192)", 47 | "rgb(54, 162, 235)", 48 | "rgb(153, 102, 255)", 49 | "rgb(255, 99, 132)" 50 | ], 51 | "yAxisID": "y1", 52 | "data": bardata 53 | }, 54 | { 55 | "label": "OUT", 56 | "backgroundColor": [ 57 | "rgb(255, 99, 132)", 58 | "rgb(255, 159, 64)", 59 | "rgb(255, 205, 86)", 60 | "rgb(75, 192, 192)", 61 | "rgb(54, 162, 235)", 62 | "rgb(153, 102, 255)", 63 | "rgb(255, 99, 132)" 64 | ], 65 | "yAxisID": "y1", 66 | "data": bardata2 67 | }, 68 | { 69 | "label": "TOTAL", 70 | "type": "line", 71 | "fill": False, 72 | "backgroundColor": "rgb(201, 203, 207)", 73 | "yAxisID": "y2", 74 | "data": linedate 75 | } 76 | ] 77 | }, 78 | "options": { 79 | "plugins": { 80 | "datalabels": { 81 | "anchor": 'end', 82 | "align": -100, 83 | "color": '#666', 84 | "font": { 85 | "size": 20, 86 | } 87 | }, 88 | }, 89 | "legend": { 90 | "labels": { 91 | "fontSize": 20, 92 | "fontStyle": 'bold', 93 | } 94 | }, 95 | "title": { 96 | "display": True, 97 | "text": title + " 收支情况", 98 | "fontSize": 24, 99 | }, 100 | "scales": { 101 | "xAxes": [{ 102 | "ticks": { 103 | "fontSize": 24, 104 | } 105 | }], 106 | "yAxes": [ 107 | { 108 | "id": "y1", 109 | "type": "linear", 110 | "display": False, 111 | "position": "left", 112 | "ticks": { 113 | "max": int(int(max([max(bardata), max(bardata2)])+100)*2) 114 | }, 115 | "scaleLabel": { 116 | "fontSize": 20, 117 | "fontStyle": 'bold', 118 | } 119 | }, 120 | { 121 | "id": "y2", 122 | "type": "linear", 123 | "display": False, 124 | "ticks": { 125 | "min": int(min(linedate)*2-(max(linedate))-100), 126 | "max": int(int(max(linedate))) 127 | }, 128 | "position": "right" 129 | } 130 | ] 131 | } 132 | } 133 | } 134 | qc.to_file(_botimg) 135 | -------------------------------------------------------------------------------- /jbot/bot/cmd.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | import re 3 | from .. import jdbot, StartCMD, chat_id, logger 4 | from .utils import cmd 5 | 6 | 7 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/cmd')) 8 | async def mycmd(event): 9 | '''接收/cmd命令后执行程序''' 10 | if StartCMD: 11 | cmdreg = re.compile(r'^/cmd [\s\S]+') 12 | text = re.findall(cmdreg, event.raw_text) 13 | if len(text) == 0: 14 | msg = '''请正确使用/cmd命令,如 15 | /cmd jlog # 删除旧日志 16 | /cmd jup # 更新所有脚本 17 | /cmd jcode # 导出所有互助码 18 | /cmd jcsv # 记录豆豆变化情况 19 | 不建议直接使用cmd命令执行脚本,请使用/node或/snode 20 | ''' 21 | await jdbot.send_message(chat_id, msg) 22 | else: 23 | logger.info(text) 24 | await cmd(text[0].replace('/cmd ', '')) 25 | else: 26 | await jdbot.send_message(chat_id, '未开启CMD命令,如需使用请修改配置文件') 27 | -------------------------------------------------------------------------------- /jbot/bot/editfile.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | import os 3 | import shutil 4 | from asyncio import exceptions 5 | from .. import jdbot, chat_id, _JdDir 6 | from .utils import split_list, logger,press_event 7 | 8 | 9 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/edit')) 10 | async def myfileup(event): 11 | '''定义编辑文件操作''' 12 | SENDER = event.sender_id 13 | path = _JdDir 14 | page = 0 15 | if len(event.raw_text.split(' ')) > 1: 16 | text = event.raw_text.replace('/edit ','') 17 | else: 18 | text =None 19 | if text and os.path.isfile(text): 20 | try: 21 | with open(text,'r',encoding='utf-8') as f: 22 | lines = f.readlines() 23 | filelist = split_list(lines, 15) 24 | path = text 25 | except Exception as e: 26 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 27 | elif text and os.path.isdir(text): 28 | path = text 29 | filelist = None 30 | elif text: 31 | await jdbot.send_message(chat_id, 'please marksure it\'s a dir or a file') 32 | filelist = None 33 | else: 34 | filelist = None 35 | async with jdbot.conversation(SENDER, timeout=60) as conv: 36 | msg = await conv.send_message('正在查询,请稍后') 37 | while path: 38 | path, msg, page, filelist = await myedit(conv, SENDER, path, msg, page, filelist) 39 | 40 | 41 | async def myedit(conv, SENDER, path, msg, page, filelist): 42 | mybtn = [Button.inline('上一页', data='up'), Button.inline('下一页', data='next'), Button.inline( 43 | '上级', data='updir'), Button.inline('取消', data='cancel')] 44 | mybtn2 = [[Button.inline('上一页', data='up'), Button.inline( 45 | '下一页', data='next'), Button.inline('取消', data='cancel')], [Button.inline('上十页', data='up10'), Button.inline( 46 | '下十页', data='next10'), Button.inline('编辑', data='edit')]] 47 | try: 48 | if filelist and type(filelist[0][0]) == str: 49 | markup = filelist 50 | newmarkup = markup[page] 51 | msg = await jdbot.edit_message(msg, "".join(newmarkup), buttons=mybtn2) 52 | else: 53 | if filelist: 54 | markup = filelist 55 | newmarkup = markup[page] 56 | if mybtn not in newmarkup: 57 | newmarkup.append(mybtn) 58 | else: 59 | dir = os.listdir(path) 60 | dir.sort() 61 | markup = [Button.inline(file, data=str( 62 | file)) for file in dir] 63 | markup = split_list(markup, 3) 64 | if len(markup) > 30: 65 | markup = split_list(markup, 30) 66 | newmarkup = markup[page] 67 | newmarkup.append(mybtn) 68 | else: 69 | newmarkup = markup 70 | if path == _JdDir: 71 | newmarkup.append([Button.inline('取消', data='cancel')]) 72 | else: 73 | newmarkup.append( 74 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 75 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 76 | convdata = await conv.wait_event(press_event(SENDER)) 77 | res = bytes.decode(convdata.data) 78 | if res == 'cancel': 79 | msg = await jdbot.edit_message(msg, '对话已取消') 80 | conv.cancel() 81 | return None, None, None, None 82 | elif res == 'next': 83 | page = page + 1 84 | if page > len(markup) - 1: 85 | page = 0 86 | return path, msg, page, markup 87 | elif res == 'up': 88 | page = page - 1 89 | if page < 0: 90 | page = len(markup) - 1 91 | return path, msg, page, markup 92 | elif res == 'next10': 93 | page = page + 10 94 | if page > len(markup) - 1: 95 | page = 0 96 | return path, msg, page, markup 97 | elif res == 'up10': 98 | page = page - 10 99 | if page < 0: 100 | page = len(markup) - 1 101 | return path, msg, page, markup 102 | elif res == 'updir': 103 | path = '/'.join(path.split('/')[:-1]) 104 | if path == '': 105 | path = _JdDir 106 | return path, msg, page, None 107 | elif res == 'edit': 108 | await jdbot.send_message(chat_id, '请复制并修改以下内容,修改完成后发回机器人,2分钟内有效') 109 | await jdbot.delete_messages(chat_id, msg) 110 | msg = await conv.send_message("".join(newmarkup)) 111 | resp = await conv.get_response() 112 | markup[page] = resp.raw_text.split('\n') 113 | for a in range(len(markup[page])): 114 | markup[page][a] = markup[page][a]+'\n' 115 | shutil.copy(path, path+'.bak') 116 | with open(path, 'w+', encoding='utf-8') as f: 117 | markup = ["".join(a) for a in markup] 118 | f.writelines(markup) 119 | await jdbot.send_message(chat_id, '文件已修改成功,原文件备份为'+path+'.bak') 120 | conv.cancel() 121 | return None, None, None, None 122 | elif os.path.isfile(path+'/'+res): 123 | msg = await jdbot.edit_message(msg, '文件读取中...请稍候') 124 | with open(path+'/'+res, 'r', encoding='utf-8') as f: 125 | lines = f.readlines() 126 | lines = split_list(lines, 15) 127 | page = 0 128 | return path+'/'+res, msg, page, lines 129 | else: 130 | return path+'/'+res, msg, page, None 131 | except exceptions.TimeoutError: 132 | msg = await jdbot.edit_message(msg, '选择已超时,本次对话已停止') 133 | return None, None, None, None 134 | except Exception as e: 135 | msg = await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 136 | logger.error('something wrong,I\'m sorry\n'+str(e)) 137 | return None, None, None, None 138 | -------------------------------------------------------------------------------- /jbot/bot/getcookie.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | import requests 3 | import re 4 | import time 5 | import qrcode 6 | from asyncio import exceptions 7 | from .. import jdbot, chat_id, img_file 8 | from .utils import press_event 9 | 10 | cookiemsg = '' 11 | # 扫码获取cookie 直接采用LOF大佬代码 12 | # getSToken请求获取,s_token用于发送post请求是的必须参数 13 | s_token = "" 14 | # getSToken请求获取,guid,lsid,lstoken用于组装cookies 15 | guid, lsid, lstoken = "", "", "" 16 | # 由上面参数组装生成,getOKLToken函数发送请求需要使用 17 | cookies = "" 18 | # getOKLToken请求获取,token用户生成二维码使用、okl_token用户检查扫码登录结果使用 19 | token, okl_token = "", "" 20 | # 最终获取到的可用的cookie 21 | jd_cookie = "" 22 | 23 | 24 | def getSToken(): 25 | time_stamp = int(time.time() * 1000) 26 | 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 27 | get_header = { 28 | 'Connection': 'Keep-Alive', 29 | 'Content-Type': 'application/x-www-form-urlencoded', 30 | 'Accept': 'application/json, text/plain, */*', 31 | 'Accept-Language': 'zh-cn', 32 | '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, 33 | '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', 34 | 'Host': 'plogin.m.jd.com' 35 | } 36 | resp = requests.get(url=get_url, headers=get_header) 37 | parseGetRespCookie(resp.headers, resp.json()) 38 | 39 | 40 | def parseGetRespCookie(headers, get_resp): 41 | global s_token 42 | global cookies 43 | s_token = get_resp.get('s_token') 44 | set_cookies = headers.get('set-cookie') 45 | guid = re.findall(r"guid=(.+?);", set_cookies)[0] 46 | lsid = re.findall(r"lsid=(.+?);", set_cookies)[0] 47 | lstoken = re.findall(r"lstoken=(.+?);", set_cookies)[0] 48 | cookies = f"guid={guid}; lang=chs; lsid={lsid}; lstoken={lstoken}; " 49 | 50 | 51 | def getOKLToken(): 52 | post_time_stamp = int(time.time() * 1000) 53 | post_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=%s&v=%s&remember=true' % ( 54 | s_token, post_time_stamp) 55 | post_data = { 56 | 'lang': 'chs', 57 | 'appid': 300, 58 | '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, 59 | 'source': 'wq_passport' 60 | } 61 | post_header = { 62 | 'Connection': 'Keep-Alive', 63 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 64 | 'Accept': 'application/json, text/plain, */*', 65 | 'Cookie': cookies, 66 | '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, 67 | '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', 68 | 'Host': 'plogin.m.jd.com', 69 | } 70 | try: 71 | global okl_token 72 | resp = requests.post( 73 | url=post_url, headers=post_header, data=post_data, timeout=20) 74 | parsePostRespCookie(resp.headers, resp.json()) 75 | except Exception as error: 76 | print("Post网络请求错误", error) 77 | 78 | 79 | def parsePostRespCookie(headers, data): 80 | global token 81 | global okl_token 82 | token = data.get('token') 83 | okl_token = re.findall(r"okl_token=(.+?);", headers.get('set-cookie'))[0] 84 | 85 | 86 | def parseJDCookies(headers): 87 | global jd_cookie 88 | set_cookie = headers.get('Set-Cookie') 89 | pt_key = re.findall(r"pt_key=(.+?);", set_cookie)[0] 90 | pt_pin = re.findall(r"pt_pin=(.+?);", set_cookie)[0] 91 | jd_cookie = f'pt_key={pt_key};pt_pin={pt_pin};' 92 | 93 | 94 | def creatqr(text): 95 | '''实例化QRCode生成qr对象''' 96 | qr = qrcode.QRCode( 97 | version=1, 98 | error_correction=qrcode.constants.ERROR_CORRECT_H, 99 | box_size=10, 100 | border=4 101 | ) 102 | qr.clear() 103 | # 传入数据 104 | qr.add_data(text) 105 | qr.make(fit=True) 106 | # 生成二维码 107 | img = qr.make_image() 108 | # 保存二维码 109 | img.save(img_file) 110 | 111 | 112 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/getcookie')) 113 | async def mycookie(event): 114 | '''接收/getcookie后执行程序''' 115 | login = True 116 | msg = await jdbot.send_message(chat_id, '正在获取二维码,请稍后') 117 | global cookiemsg 118 | try: 119 | SENDER = event.sender_id 120 | async with jdbot.conversation(SENDER, timeout=30) as conv: 121 | getSToken() 122 | getOKLToken() 123 | url = 'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token='+token 124 | creatqr(url) 125 | markup = [Button.inline("已扫码", data='confirm'), 126 | Button.inline("取消", data='cancel')] 127 | await jdbot.delete_messages(chat_id, msg) 128 | cookiemsg = await jdbot.send_message(chat_id, '30s内点击取消将取消本次操作\n如不取消,扫码结果将于30s后显示\n扫码后不想等待点击已扫码', file=img_file, buttons=markup) 129 | convdata = await conv.wait_event(press_event(SENDER)) 130 | res = bytes.decode(convdata.data) 131 | if res == 'cancel': 132 | login = False 133 | await jdbot.delete_messages(chat_id, cookiemsg) 134 | msg = await conv.send_message('对话已取消') 135 | conv.cancel() 136 | else: 137 | raise exceptions.TimeoutError() 138 | except exceptions.TimeoutError: 139 | expired_time = time.time() + 60 * 2 140 | while login: 141 | check_time_stamp = int(time.time() * 1000) 142 | check_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=%s&ou_state=0&okl_token=%s' % ( 143 | token, okl_token) 144 | check_data = { 145 | 'lang': 'chs', 146 | 'appid': 300, 147 | '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, 148 | 'source': 'wq_passport' 149 | } 150 | check_header = { 151 | '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, 152 | 'Cookie': cookies, 153 | 'Connection': 'Keep-Alive', 154 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 155 | 'Accept': 'application/json, text/plain, */*', 156 | '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', 157 | } 158 | resp = requests.post( 159 | url=check_url, headers=check_header, data=check_data, timeout=30) 160 | data = resp.json() 161 | if data.get("errcode") == 0: 162 | parseJDCookies(resp.headers) 163 | await jdbot.delete_messages(chat_id, cookiemsg) 164 | await jdbot.send_message(chat_id, '以下为获取到的cookie') 165 | await jdbot.send_message(chat_id, jd_cookie) 166 | return 167 | if data.get("errcode") == 21: 168 | await jdbot.delete_messages(chat_id, cookiemsg) 169 | await jdbot.send_message(chat_id, '发生了某些错误\n'+data.get("errcode")) 170 | return 171 | if time.time() > expired_time: 172 | await jdbot.delete_messages(chat_id, cookiemsg) 173 | await jdbot.send_message(chat_id, '超过3分钟未扫码,二维码已过期') 174 | return 175 | except Exception as e: 176 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 177 | -------------------------------------------------------------------------------- /jbot/bot/getfile.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | import subprocess 3 | from asyncio import exceptions 4 | from .. import jdbot, chat_id, _ScriptsDir, _ConfigDir, logger 5 | from .utils import press_event, backfile, _DiyDir, jdcmd, V4 6 | 7 | 8 | @jdbot.on(events.NewMessage(from_users=chat_id)) 9 | async def myfile(event): 10 | '''定义文件操作''' 11 | try: 12 | v4btn = [[Button.inline('放入config', data=_ConfigDir), Button.inline('放入scripts', data=_ScriptsDir), Button.inline('放入OWN文件夹', data=_DiyDir)], [ 13 | Button.inline('放入scripts并运行', data='node1'), Button.inline('放入OWN并运行', data='node'), Button.inline('取消', data='cancel')]] 14 | btn = [[Button.inline('放入config', data=_ConfigDir), Button.inline('放入scripts', data=_ScriptsDir)], [ 15 | Button.inline('放入scripts并运行', data='node1'), Button.inline('取消', data='cancel')]] 16 | SENDER = event.sender_id 17 | if event.message.file: 18 | markup = [] 19 | filename = event.message.file.name 20 | async with jdbot.conversation(SENDER, timeout=30) as conv: 21 | msg = await conv.send_message('请选择您要放入的文件夹或操作:\n') 22 | if V4: 23 | markup = v4btn 24 | else: 25 | markup = btn 26 | msg = await jdbot.edit_message(msg, '请选择您要放入的文件夹或操作:', buttons=markup) 27 | convdata = await conv.wait_event(press_event(SENDER)) 28 | res = bytes.decode(convdata.data) 29 | if res == 'cancel': 30 | msg = await jdbot.edit_message(msg, '对话已取消') 31 | conv.cancel() 32 | elif res == 'node': 33 | await backfile(_DiyDir+'/'+filename) 34 | await jdbot.download_media(event.message, _DiyDir) 35 | cmdtext = '{} {}/{} now'.format(jdcmd, _DiyDir, filename) 36 | subprocess.Popen( 37 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 38 | await jdbot.edit_message(msg, '脚本已保存到DIY文件夹,并成功在后台运行,请稍后自行查看日志') 39 | conv.cancel() 40 | elif res == 'node1': 41 | await backfile(_ScriptsDir+'/'+filename) 42 | await jdbot.download_media(event.message, _ScriptsDir) 43 | if V4: 44 | cmdtext = '{} {}/{} now'.format(jdcmd, 45 | _ScriptsDir, filename) 46 | else: 47 | cmdtext = '{} {} now'.format(jdcmd, filename) 48 | subprocess.Popen( 49 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 50 | await jdbot.edit_message(msg, '脚本已保存到scripts文件夹,并成功在后台运行,请稍后自行查看日志') 51 | conv.cancel() 52 | else: 53 | await backfile(res+'/'+filename) 54 | await jdbot.download_media(event.message, res) 55 | await jdbot.edit_message(msg, filename+'已保存到'+res+'文件夹') 56 | if filename == 'crontab.list' and V4: 57 | cmdtext = 'crontab '+res+'/'+filename 58 | subprocess.Popen( 59 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 60 | await jdbot.edit_message(msg, '定时文件已保存,并更新') 61 | except exceptions.TimeoutError: 62 | msg = await jdbot.send_message(chat_id, '选择已超时,对话已停止') 63 | except Exception as e: 64 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 65 | logger.error('something wrong,I\'m sorry\n'+str(e)) 66 | -------------------------------------------------------------------------------- /jbot/bot/help.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id 3 | 4 | 5 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='^/help')) 6 | async def myhelp(event): 7 | '''接收/help命令后执行程序''' 8 | if len(event.raw_text) > 6: 9 | text = event.raw_text.replace('/help ', '') 10 | else: 11 | text = 'mhelp' 12 | mhelp = ''' 13 | a-我的自定义快捷按钮 14 | bean-获取收支 15 | edit-编辑文件 16 | start-开始使用本程序 17 | node-执行js脚本文件,绝对路径。 18 | cmd-执行cmd命令 19 | snode-选择脚本后台运行 20 | log-选择日志 21 | getfile-获取jd目录下文件 22 | setshort-设置自定义按钮 23 | getcookie-扫码获取cookie''' 24 | bean = '/bean 加数字,获取该账户近期收支情况\n/bean in\out获取所有账户近期收或支情况\n/bean 获取账户总豆数量' 25 | cmd = '/cmd用于执行cmd命令,如果命令持续10分钟仍未结束,将强行终止,以保障机器人响应' 26 | edit = '/edit 进入/jd目录选择文件进行编辑,仅限简易编辑\n/edit /jd/config进入config目录选择文件编辑\n/edit /jd/config/config.sh 直接编辑config.sh文件' 27 | getcookie = '/getcookie 扫码获取jdcookie' 28 | node = '/node 用于执行js脚本 用法:\n/node /jd/own/abc/def.js' 29 | getfile = '/getfile 进入/jd目录选择文件进行获取\n/getfile /jd/config进入config目录选择文件获取\n/getfile /jd/config/config.sh 直接获取config.sh文件' 30 | setshort = '/setshort 用于设置快捷方式,格式如下:\n更新-->jup\nAAA-->BBB这种格式使用/a选择\n/bean 1\n/edit /jd/config/config.sh\n以“/”开头的为机器人命令快捷,使用/b选择' 31 | snode = '/snode 选择脚本并运行' 32 | chart = '' 33 | helpme = {'bean': bean, 'cmd': cmd, 'edit': edit, 'getcookie': getcookie, 'node': node, 34 | 'getfile': getfile, 'setshort': setshort, 'snode': snode, 'chart': chart,'mhelp':mhelp} 35 | await jdbot.send_message(chat_id, helpme[text]) 36 | -------------------------------------------------------------------------------- /jbot/bot/node.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | import re 3 | from .. import jdbot, chat_id 4 | from .utils import cmd, jdcmd 5 | 6 | 7 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/node')) 8 | async def mynode(event): 9 | '''接收/node命令后执行程序''' 10 | nodereg = re.compile(r'^/node [\S]+') 11 | text = re.findall(nodereg, event.raw_text) 12 | if len(text) == 0: 13 | res = '''请正确使用/node命令,如 14 | /node /abc/123.js 运行abc/123.js脚本 15 | /node /own/abc.js 运行own/abc.js脚本 16 | ''' 17 | await jdbot.send_message(chat_id, res) 18 | else: 19 | await cmd('{} {} now'.format(jdcmd, text[0].replace('/node ', ''))) 20 | -------------------------------------------------------------------------------- /jbot/bot/quickchart.py: -------------------------------------------------------------------------------- 1 | """A python client for quickchart.io, a web service that generates static 2 | charts.""" 3 | 4 | import datetime 5 | import json 6 | import re 7 | try: 8 | from urllib import urlencode 9 | except: 10 | # For Python 3 11 | from urllib.parse import urlencode 12 | 13 | FUNCTION_DELIMITER_RE = re.compile('\"__BEGINFUNCTION__(.*?)__ENDFUNCTION__\"') 14 | 15 | 16 | class QuickChartFunction: 17 | def __init__(self, script): 18 | self.script = script 19 | 20 | def __repr__(self): 21 | return self.script 22 | 23 | 24 | def serialize(obj): 25 | if isinstance(obj, QuickChartFunction): 26 | return '__BEGINFUNCTION__' + obj.script + '__ENDFUNCTION__' 27 | if isinstance(obj, (datetime.date, datetime.datetime)): 28 | return obj.isoformat() 29 | return obj.__dict__ 30 | 31 | 32 | def dump_json(obj): 33 | ret = json.dumps(obj, default=serialize, separators=(',', ':')) 34 | ret = FUNCTION_DELIMITER_RE.sub( 35 | lambda match: json.loads('"' + match.group(1) + '"'), ret) 36 | return ret 37 | 38 | 39 | class QuickChart: 40 | def __init__(self): 41 | self.config = None 42 | self.width = 500 43 | self.height = 300 44 | self.background_color = '#ffffff' 45 | self.device_pixel_ratio = 1.0 46 | self.format = 'png' 47 | self.key = None 48 | self.scheme = 'https' 49 | self.host = 'quickchart.io' 50 | 51 | def is_valid(self): 52 | return self.config is not None 53 | 54 | def get_url_base(self): 55 | return '%s://%s' % (self.scheme, self.host) 56 | 57 | def get_url(self): 58 | if not self.is_valid(): 59 | raise RuntimeError( 60 | 'You must set the `config` attribute before generating a url') 61 | params = { 62 | 'c': dump_json(self.config) if type(self.config) == dict else self.config, 63 | 'w': self.width, 64 | 'h': self.height, 65 | 'bkg': self.background_color, 66 | 'devicePixelRatio': self.device_pixel_ratio, 67 | 'f': self.format, 68 | } 69 | if self.key: 70 | params['key'] = self.key 71 | return '%s/chart?%s' % (self.get_url_base(), urlencode(params)) 72 | 73 | def _post(self, url): 74 | try: 75 | import requests 76 | except: 77 | raise RuntimeError('Could not find `requests` dependency') 78 | 79 | postdata = { 80 | 'chart': dump_json(self.config) if type(self.config) == dict else self.config, 81 | 'width': self.width, 82 | 'height': self.height, 83 | 'backgroundColor': self.background_color, 84 | 'devicePixelRatio': self.device_pixel_ratio, 85 | 'format': self.format, 86 | } 87 | if self.key: 88 | postdata['key'] = self.key 89 | resp = requests.post(url, json=postdata) 90 | if resp.status_code != 200: 91 | raise RuntimeError( 92 | 'Invalid response code from chart creation endpoint') 93 | return resp 94 | 95 | def get_short_url(self): 96 | resp = self._post('%s/chart/create' % self.get_url_base()) 97 | parsed = json.loads(resp.text) 98 | if not parsed['success']: 99 | raise RuntimeError( 100 | 'Failure response status from chart creation endpoint') 101 | return parsed['url'] 102 | 103 | def get_bytes(self): 104 | resp = self._post('%s/chart' % self.get_url_base()) 105 | return resp.content 106 | 107 | def to_file(self, path): 108 | content = self.get_bytes() 109 | with open(path, 'wb') as f: 110 | f.write(content) 111 | -------------------------------------------------------------------------------- /jbot/bot/sendfile.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, _LogDir, _JdDir 3 | from .utils import logbtn 4 | import os 5 | 6 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/log')) 7 | async def mylog(event): 8 | '''定义日志文件操作''' 9 | SENDER = event.sender_id 10 | path = _LogDir 11 | page = 0 12 | filelist = None 13 | async with jdbot.conversation(SENDER, timeout=60) as conv: 14 | msg = await conv.send_message('正在查询,请稍后') 15 | while path: 16 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 17 | 18 | 19 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile')) 20 | async def mygetfile(event): 21 | '''定义获取文件命令''' 22 | SENDER = event.sender_id 23 | path = _JdDir 24 | page = 0 25 | if len(event.raw_text.split(' ')) > 1: 26 | text = event.raw_text.replace('/getfile ','') 27 | else: 28 | text =None 29 | if text and os.path.isfile(text): 30 | await jdbot.send_message(chat_id, '请查收文件',file=text) 31 | return 32 | elif text and os.path.isdir(text): 33 | path = text 34 | filelist = None 35 | elif text: 36 | await jdbot.send_message(chat_id, 'please marksure it\'s a dir or a file') 37 | filelist = None 38 | else: 39 | filelist = None 40 | async with jdbot.conversation(SENDER, timeout=60) as conv: 41 | msg = await conv.send_message('正在查询,请稍后') 42 | while path: 43 | path, msg, page, filelist = await logbtn(conv, SENDER, path, msg, page, filelist) 44 | -------------------------------------------------------------------------------- /jbot/bot/setshort.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, _shortcut 3 | 4 | 5 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/setshort$')) 6 | async def setshortcut(event): 7 | SENDER = event.sender_id 8 | async with jdbot.conversation(SENDER, timeout=60) as conv: 9 | await conv.send_message( 10 | '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部分功能青龙可能不支持,请自行测试,自行设定 ') 11 | shortcut = await conv.get_response() 12 | with open(_shortcut, 'w+', encoding='utf-8') as f: 13 | f.write(shortcut.raw_text) 14 | await conv.send_message('已设置成功可通过"/a"使用') 15 | conv.cancel() 16 | -------------------------------------------------------------------------------- /jbot/bot/short.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | from .utils import split_list, press_event, cmd 3 | import subprocess 4 | from asyncio import exceptions 5 | from .. import jdbot, chat_id, _shortcut, logger 6 | 7 | 8 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/a$')) 9 | async def shortcut(event): 10 | markup = [] 11 | SENDER = event.sender_id 12 | msg = await jdbot.send_message(chat_id, '正在查询您的常用命令,请稍后') 13 | with open(_shortcut, 'r', encoding='utf-8') as f: 14 | shortcuts = f.readlines() 15 | try: 16 | async with jdbot.conversation(SENDER, timeout=60) as conv: 17 | markup = [Button.inline(shortcut.split( 18 | '-->')[0], data=str(shortcut.split('-->')[-1])) for shortcut in shortcuts if '-->' in shortcut] 19 | markup = split_list(markup, 3) 20 | markup.append([Button.inline('取消', data='cancel')]) 21 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=markup) 22 | convdata = await conv.wait_event(press_event(SENDER)) 23 | res = bytes.decode(convdata.data) 24 | if res == 'cancel': 25 | msg = await jdbot.edit_message(msg, '对话已取消') 26 | conv.cancel() 27 | elif 'nohup ' in res: 28 | msg = await jdbot.edit_message(msg, '即将执行您的操作'+res) 29 | cmdtext = res.replace('nohup ', '') 30 | subprocess.Popen( 31 | cmdtext, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 32 | msg = await jdbot.edit_message(msg, '已在后台执行您的操作'+res.replace('nohup ', '')) 33 | conv.cancel() 34 | else: 35 | await jdbot.delete_messages(chat_id, msg) 36 | await cmd(res) 37 | conv.cancel() 38 | except exceptions.TimeoutError: 39 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 40 | except Exception as e: 41 | await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 42 | logger.error('something wrong,I\'m sorry\n'+str(e)) 43 | 44 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/b$')) 45 | async def shortcut(event): 46 | markup = [] 47 | msg = await jdbot.send_message(chat_id, '正在查询您的常用命令,请稍后') 48 | with open(_shortcut, 'r', encoding='utf-8') as f: 49 | shortcuts = f.readlines() 50 | try: 51 | await jdbot.delete_messages(chat_id,msg) 52 | markup = [Button.text(shortcut,single_use=True) for shortcut in shortcuts if '-->' not in shortcut] 53 | markup = split_list(markup, 3) 54 | await jdbot.send_message(chat_id, '请做出您的选择:', buttons=markup) 55 | except Exception as e: 56 | await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 57 | logger.error('something wrong,I\'m sorry\n'+str(e)) 58 | -------------------------------------------------------------------------------- /jbot/bot/snode.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, _JdDir 3 | from .utils import cmd, nodebtn 4 | 5 | 6 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode')) 7 | async def mysnode(event): 8 | '''定义supernode文件命令''' 9 | SENDER = event.sender_id 10 | path = _JdDir 11 | page = 0 12 | filelist = None 13 | async with jdbot.conversation(SENDER, timeout=60) as conv: 14 | msg = await conv.send_message('正在查询,请稍后') 15 | while path: 16 | path, msg, page, filelist = await nodebtn(conv, SENDER, path, msg, page, filelist) 17 | if filelist and filelist.startswith('CMD-->'): 18 | await cmd(filelist.replace('CMD-->', '')) 19 | -------------------------------------------------------------------------------- /jbot/bot/start.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id 3 | 4 | 5 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/start')) 6 | async def mystart(event): 7 | '''接收/start命令后执行程序''' 8 | msg = '''使用方法如下: 9 | /help 获取命令,可直接发送至botfather。 10 | /start 开始使用本程序。 11 | /a 使用你的自定义快捷按钮。 12 | /bean 获取京豆变化,默认为总京豆收支。/bean in 京豆进账,/bean out 京豆支出。 13 | /chart 获取京豆变化数据柱状图和曲线图。例,/chart 1,获取账号1的京豆变化。 14 | /cmd 执行cmd命令,例如/cmd python3 /python/bot.py 则将执行python目录下的bot.py 不建议使用机器人使用并发,可能产生不明原因的崩溃。 15 | /edit 从jd目录下选择文件编辑,需要将编辑好信息全部发给机器人,机器人会根据你发的信息进行替换。建议用来编辑config或crontab.list 其他文件慎用!!! 16 | /getcookie 扫码获取cookie 增加30s内取消按钮,30s后不能进行其他交互直到2分钟或获取到cookie。 17 | /getfile 获取jd目录下文件。 18 | /log 选择查看执行日志。 19 | /node 执行js脚本文件,直接输入/node jd_bean_change 如执行其他自己js,需输入绝对路径。即可进行执行。该命令会等待脚本执行完,期间不能使用机器人,建议使用snode命令。 20 | /setshort 设置自定义按钮,每次设置会覆盖原设置。 21 | /snode 命令可以选择脚本执行,只能选择/scripts 和/own目录下的脚本,选择完后直接后台运行,不影响机器人响应其他命令。 22 | 此外直接发送文件,会让您选择保存到哪个文件夹,如果选择运行,将保存至own目录下,并立即运行脚本,crontab.list文件会自动更新时间''' 23 | await jdbot.send_message(chat_id, msg) 24 | -------------------------------------------------------------------------------- /jbot/bot/update.py: -------------------------------------------------------------------------------- 1 | version = 'version :0.3.5' 2 | botlog = ''' 3 | **2021年5月18日** 4 | 本次更新内容如下: 5 | - 修改机器人断网重连 6 | - 默认断网自动重连 7 | - 如修改请在bot.json内添加括号内内容【```"noretry":true```】 8 | - 添加上述内容后,机器人断开连接重连5次,5次不能连接成功将自动结束,如需重新启用需通过终端运行```pm2 start jbot``` 9 | - 修正bean与chart命令 账号不一致问题 10 | ''' -------------------------------------------------------------------------------- /jbot/bot/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from telethon import events, Button 3 | import re 4 | from .. import jdbot, chat_id, _LogDir, logger, _JdDir, _OwnDir, _ConfigDir 5 | import asyncio 6 | import datetime 7 | 8 | bean_log = _LogDir + '/jd_bean_change/' 9 | _ConfigFile = _ConfigDir+'/config.sh' 10 | V4, QL = False, False 11 | if 'JD_DIR' in os.environ.keys(): 12 | V4 = True 13 | _ConfigFile = _ConfigDir+'/config.sh' 14 | _DiyDir = _OwnDir 15 | jdcmd = 'jtask' 16 | elif 'QL_DIR' in os.environ.keys(): 17 | QL = True 18 | _ConfigFile = _ConfigDir+'/cookie.sh' 19 | _DiyDir = None 20 | jdcmd = 'task' 21 | dirs = os.listdir(_LogDir) 22 | for mydir in dirs: 23 | if 'jd_bean_change' in mydir: 24 | bean_log = _LogDir + '/' + mydir 25 | break 26 | else: 27 | _DiyDir = None 28 | jdcmd = 'node' 29 | 30 | ckreg = re.compile(r'pt_key=\S*;pt_pin=\S*;') 31 | with open(_ConfigFile, 'r', encoding='utf-8') as f: 32 | lines = f.read() 33 | cookies = ckreg.findall(lines) 34 | for ck in cookies: 35 | if ck == 'pt_key=xxxxxxxxxx;pt_pin=xxxx;': 36 | cookies.remove(ck) 37 | break 38 | 39 | 40 | def split_list(datas, n, row: bool = True): 41 | """一维列表转二维列表,根据N不同,生成不同级别的列表""" 42 | length = len(datas) 43 | size = length / n + 1 if length % n else length/n 44 | _datas = [] 45 | if not row: 46 | size, n = n, size 47 | for i in range(int(size)): 48 | start = int(i * n) 49 | end = int((i + 1) * n) 50 | _datas.append(datas[start:end]) 51 | return _datas 52 | 53 | 54 | async def backfile(file): 55 | '''如果文件存在,则备份,并更新''' 56 | if os.path.exists(file): 57 | try: 58 | os.rename(file, file+'.bak') 59 | except WindowsError: 60 | os.remove(file+'.bak') 61 | os.rename(file, file+'.bak') 62 | 63 | 64 | def press_event(user_id): 65 | return events.CallbackQuery(func=lambda e: e.sender_id == user_id) 66 | 67 | 68 | async def cmd(cmdtext): 69 | '''定义执行cmd命令''' 70 | try: 71 | msg = await jdbot.send_message(chat_id, '开始执行命令') 72 | p = await asyncio.create_subprocess_shell( 73 | cmdtext, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) 74 | res_bytes, res_err = await p.communicate() 75 | res = res_bytes.decode('utf-8') 76 | if len(res) == 0: 77 | await jdbot.edit_message(msg, '已执行,但返回值为空') 78 | elif len(res) <= 4000: 79 | await jdbot.delete_messages(chat_id, msg) 80 | await jdbot.send_message(chat_id, res) 81 | elif len(res) > 4000: 82 | _log = _LogDir + '/bot/'+cmdtext.split('/')[-1].split( 83 | '.js')[0]+datetime.datetime.now().strftime('%H-%M-%S')+'.log' 84 | with open(_log, 'w+', encoding='utf-8') as f: 85 | f.write(res) 86 | await jdbot.delete_messages(chat_id, msg) 87 | await jdbot.send_message(chat_id, '执行结果较长,请查看日志', file=_log) 88 | os.remove(_log) 89 | except Exception as e: 90 | await jdbot.send_message(chat_id, 'something wrong,I\'m sorry\n'+str(e)) 91 | logger.error('something wrong,I\'m sorry'+str(e)) 92 | 93 | 94 | async def getname(path, dir): 95 | '''获取文件中文名称,如无则返回文件名''' 96 | names = [] 97 | reg = r'new Env\(\'[\S]+?\'\)' 98 | cname = False 99 | for file in dir: 100 | if os.path.isdir(path+'/'+file): 101 | names.append(file) 102 | elif file.endswith('.js') and file != 'jdCookie.js' and file != 'getJDCookie.js' and file != 'JD_extra_cookie.js' and 'ShareCode' not in file: 103 | with open(path+'/'+file, 'r', encoding='utf-8') as f: 104 | resdatas = f.readlines() 105 | for data in resdatas: 106 | if 'new Env' in data: 107 | data = data.replace('\"', '\'') 108 | res = re.findall(reg, data) 109 | if len(res) != 0: 110 | res = res[0].split('\'')[-2] 111 | names.append(res+'--->'+file) 112 | cname = True 113 | break 114 | if not cname: 115 | names.append(file+'--->'+file) 116 | cname = False 117 | else: 118 | continue 119 | return names 120 | 121 | 122 | async def logbtn(conv, SENDER, path, msg, page, filelist): 123 | '''定义log日志按钮''' 124 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 125 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 126 | try: 127 | if filelist: 128 | markup = filelist 129 | newmarkup = markup[page] 130 | if mybtn not in newmarkup: 131 | newmarkup.append(mybtn) 132 | else: 133 | dir = os.listdir(path) 134 | dir.sort() 135 | markup = [Button.inline(file, data=str(file)) 136 | for file in dir] 137 | markup = split_list(markup, 3) 138 | if len(markup) > 30: 139 | markup = split_list(markup, 30) 140 | newmarkup = markup[page] 141 | newmarkup.append(mybtn) 142 | else: 143 | newmarkup = markup 144 | if path == _JdDir: 145 | newmarkup.append([Button.inline('取消', data='cancel')]) 146 | else: 147 | newmarkup.append( 148 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 149 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 150 | convdata = await conv.wait_event(press_event(SENDER)) 151 | res = bytes.decode(convdata.data) 152 | if res == 'cancel': 153 | msg = await jdbot.edit_message(msg, '对话已取消') 154 | conv.cancel() 155 | return None, None, None, None 156 | elif res == 'next': 157 | page = page + 1 158 | if page > len(markup) - 1: 159 | page = 0 160 | return path, msg, page, markup 161 | elif res == 'up': 162 | page = page - 1 163 | if page < 0: 164 | page = len(markup) - 1 165 | return path, msg, page, markup 166 | elif res == 'updir': 167 | path = '/'.join(path.split('/')[:-1]) 168 | logger.info(path) 169 | if path == '': 170 | path = _JdDir 171 | return path, msg, page, None 172 | elif os.path.isfile(path+'/'+res): 173 | msg = await jdbot.edit_message(msg, '文件发送中,请注意查收') 174 | await conv.send_file(path+'/'+res) 175 | msg = await jdbot.edit_message(msg, res+'发送成功,请查收') 176 | conv.cancel() 177 | return None, None, None, None 178 | else: 179 | return path+'/'+res, msg, page, None 180 | except asyncio.exceptions.TimeoutError: 181 | msg = await jdbot.edit_message(msg, '选择已超时,本次对话已停止') 182 | return None, None, None, None 183 | except Exception as e: 184 | msg = await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 185 | logger.error('something wrong,I\'m sorry\n'+str(e)) 186 | return None, None, None, None 187 | 188 | 189 | async def nodebtn(conv, SENDER, path, msg, page, filelist): 190 | '''定义scripts脚本按钮''' 191 | mybtn = [Button.inline('上一页', data='up'), Button.inline( 192 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 193 | try: 194 | if filelist: 195 | markup = filelist 196 | newmarkup = markup[page] 197 | if mybtn not in newmarkup: 198 | newmarkup.append(mybtn) 199 | else: 200 | if path == _JdDir and V4: 201 | dir = ['scripts', _OwnDir.split('/')[-1]] 202 | elif path == _JdDir and QL: 203 | dir = ['scripts'] 204 | else: 205 | dir = os.listdir(path) 206 | dir = await getname(path, dir) 207 | dir.sort() 208 | markup = [Button.inline(file.split('--->')[0], data=str(file.split('--->')[-1])) 209 | for file in dir if os.path.isdir(path+'/'+file) or file.endswith('.js')] 210 | markup = split_list(markup, 3) 211 | if len(markup) > 30: 212 | markup = split_list(markup, 30) 213 | newmarkup = markup[page] 214 | newmarkup.append(mybtn) 215 | else: 216 | newmarkup = markup 217 | if path == _JdDir: 218 | newmarkup.append([Button.inline('取消', data='cancel')]) 219 | else: 220 | newmarkup.append( 221 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 222 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 223 | convdata = await conv.wait_event(press_event(SENDER)) 224 | res = bytes.decode(convdata.data) 225 | if res == 'cancel': 226 | msg = await jdbot.edit_message(msg, '对话已取消') 227 | conv.cancel() 228 | return None, None, None, None 229 | elif res == 'next': 230 | page = page + 1 231 | if page > len(markup) - 1: 232 | page = 0 233 | return path, msg, page, markup 234 | elif res == 'up': 235 | page = page - 1 236 | if page < 0: 237 | page = len(markup) - 1 238 | return path, msg, page, markup 239 | elif res == 'updir': 240 | path = '/'.join(path.split('/')[:-1]) 241 | if path == '': 242 | path = _JdDir 243 | return path, msg, page, None 244 | elif os.path.isfile(path+'/'+res): 245 | conv.cancel() 246 | logger.info(path+'/'+res+'脚本即将在后台运行') 247 | msg = await jdbot.edit_message(msg, res + '在后台运行成功') 248 | cmdtext = '{} {}/{} now'.format(jdcmd, path, res) 249 | return None, None, None, 'CMD-->'+cmdtext 250 | else: 251 | return path+'/'+res, msg, page, None 252 | except asyncio.exceptions.TimeoutError: 253 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 254 | return None, None, None, None 255 | except Exception as e: 256 | msg = await jdbot.edit_message(msg, 'something wrong,I\'m sorry\n'+str(e)) 257 | logger.error('something wrong,I\'m sorry\n'+str(e)) 258 | return None, None, None, None 259 | -------------------------------------------------------------------------------- /jbot/diy/example.py: -------------------------------------------------------------------------------- 1 | #引入库文件,基于telethon 2 | from telethon import events 3 | #从上级目录引入 jdbot,chat_id变量 4 | from .. import jdbot,chat_id 5 | #格式基本固定,本例子表示从chat_id处接收到包含hello消息后,要做的事情 6 | @jdbot.on(events.NewMessage(chats=chat_id,pattern=('hello'))) 7 | #定义自己的函数名称 8 | async def hi(event): 9 | #do something 10 | await jdbot.send_message(chat_id,'hello') -------------------------------------------------------------------------------- /jbot/ecosystem.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [{ 3 | name: "jbot", 4 | version: "0.3.5", 5 | cwd: "..", 6 | script: "python", 7 | args: "-m jbot", 8 | autorestart: true, 9 | watch: ["jbot"], 10 | ignore_watch: [ 11 | "jbot/__pycache__/*", 12 | "jbot/bot/__pycache__/*", 13 | "jbot/diy/__pycache__/*", 14 | "jbot/*.log", 15 | "jbot/*/*.log", 16 | "jbot/requirements.txt", 17 | "jbot/ecosystem.config.js" 18 | ], 19 | watch_delay: 15000, 20 | interpreter: "" 21 | }] 22 | } -------------------------------------------------------------------------------- /jbot/font/jet.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weizuo0408/jddockerbot/9faac473a21f8a03e8143b82ecad38fc8bb69c76/jbot/font/jet.ttf -------------------------------------------------------------------------------- /jbot/requirements.txt: -------------------------------------------------------------------------------- 1 | qrcode==6.1 2 | Telethon==1.21.1 3 | requests==2.25.1 4 | Pillow==8.1.2 5 | python-socks==1.2.4 6 | async_timeout==3.0.1 7 | prettytable>=2.1.0 -------------------------------------------------------------------------------- /jbot/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import importlib 3 | import os 4 | from . import logger 5 | 6 | 7 | def load_diy(module, path): 8 | files = os.listdir(path) 9 | for file in files: 10 | try: 11 | if file.endswith('.py'): 12 | filename = file.replace('.py', '') 13 | name = "jbot.{}.{}".format(module, filename) 14 | spec = importlib.util.spec_from_file_location(name, path+file) 15 | load = importlib.util.module_from_spec(spec) 16 | spec.loader.exec_module(load) 17 | sys.modules[f"jbot.{module}.{filename}"] = load 18 | logger.info("JBot加载 " + filename+" 完成") 19 | except Exception as e: 20 | logger.info("JBot加载失败"+str(e)) 21 | continue 22 | 23 | -------------------------------------------------------------------------------- /rebotV3.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ps -ef | grep botV3.py | grep -v grep | awk '{print $1}' | xargs kill -9 3 | 4 | nohup python3 -u /jd/config/botV3.py > /jd/log/botrun.log 2>&1 & -------------------------------------------------------------------------------- /rebotV4.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ps -ef | grep botV4.py | grep -v grep | awk '{print $1}' | xargs kill -9 3 | 4 | nohup python3 -u /jd/config/botV4.py > /jd/log/botrun.log 2>&1 & -------------------------------------------------------------------------------- /rebotql.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ps -ef | grep botql.py | grep -v grep | awk '{print $1}' | xargs kill -9 3 | 4 | nohup python3 -u /ql/config/botql.py > /ql/log/botrun.log 2>&1 & 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | qrcode==6.1 2 | Telethon==1.21.1 3 | requests==2.25.1 4 | Pillow==8.1.2 5 | python-socks==1.2.4 6 | async_timeout==3.0.1 7 | prettytable>=2.1.0 --------------------------------------------------------------------------------