├── README.md ├── config ├── bot.json └── bot.sh ├── jbot ├── __init__.py ├── __main__.py ├── bot │ ├── aff.py │ ├── auth.py │ ├── bean.py │ ├── beandata.py │ ├── chart.py │ ├── cmd.py │ ├── cron.py │ ├── editfile.py │ ├── forward.py │ ├── getfile.py │ ├── geturlfile.py │ ├── help.py │ ├── node.py │ ├── qlenv.py │ ├── quickchart.py │ ├── reboot.py │ ├── sendfile.py │ ├── setshort.py │ ├── setting.py │ ├── short.py │ ├── snode.py │ ├── start.py │ ├── up.py │ ├── update.py │ └── utils.py ├── diy │ └── example.py ├── ecosystem.config.js ├── font │ ├── aff.jpg │ └── jet.ttf ├── requirements.txt ├── set.json ├── user │ ├── login.py │ ├── start.py │ └── usermsg.py └── utils.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### 22年2月7日 4 | - 修改青龙 _id 字段为 id 5 | #### 21年12月2日 6 | - 更新telethon依赖版本 -------------------------------------------------------------------------------- /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,填入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 | -------------------------------------------------------------------------------- /config/bot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 从whyour 大佬的bot.sh 与E大的jup.sh 拼凑出来的 3 | ## 导入通用变量与函数 4 | if [ ! -d "/ql" ];then 5 | dir_root=/jd 6 | else 7 | dir_root=/ql 8 | fi 9 | dir_bot=$dir_root/jbot 10 | dir_repo=$dir_root/repo 11 | file_bot_setting_user=$dir_root/config/bot.json 12 | repo_path="${dir_repo}/SuMaiKaDe_bot" 13 | url="https://ghproxy.com/https://github.com/SuMaiKaDe/bot.git" 14 | repo_path="${dir_repo}/dockerbot" 15 | 16 | git_pull_scripts() { 17 | local dir_current=$(pwd) 18 | local dir_work="$1" 19 | local branch="$2" 20 | [[ $branch ]] && local cmd="origin/${branch}" 21 | cd $dir_work 22 | echo -e "开始更新仓库:$dir_work\n" 23 | git fetch --all 24 | exit_status=$? 25 | git reset --hard $cmd 26 | git pull 27 | cd $dir_current 28 | } 29 | 30 | git_clone_scripts() { 31 | local url=$1 32 | local dir=$2 33 | local branch=$3 34 | [[ $branch ]] && local cmd="-b $branch " 35 | echo -e "开始克隆仓库 $url 到 $dir\n" 36 | git clone $cmd $url $dir 37 | exit_status=$? 38 | } 39 | notify_telegram () { 40 | local message="$(echo -e $1)" 41 | local bot_token=$(cat $file_bot_setting_user | jq -r .bot_token) 42 | local user_id=$(cat $file_bot_setting_user | jq .user_id) 43 | local proxy=$(cat $file_bot_setting_user | jq .proxy) 44 | local proxy_type=$(cat $file_bot_setting_user | jq -r .proxy_type) 45 | local proxy_add=$(cat $file_bot_setting_user | jq -r .proxy_add) 46 | local proxy_port=$(cat $file_bot_setting_user | jq .proxy_port) 47 | local proxy_user=$(cat $file_bot_setting_user | jq -r .proxy_user) 48 | local proxy_password=$(cat $file_bot_setting_user | jq -r .proxy_password) 49 | local api_url="https://api.telegram.org/bot${bot_token}/sendMessage" 50 | local cmd_proxy_user cmd_proxy 51 | 52 | if [[ $proxy_user != *无则不用* ]] && [[ $proxy_password != *无则不用* ]]; then 53 | cmd_proxy_user="--proxy-user $proxy_user:$proxy_password" 54 | else 55 | cmd_proxy_user="" 56 | fi 57 | 58 | if [[ $proxy == true ]]; then 59 | cmd_proxy="--proxy $proxy_type://$proxy_add:$proxy_port $cmd_proxy_user" 60 | else 61 | cmd_proxy="" 62 | fi 63 | 64 | curl -Ss $cmd_proxy -H "Content-Type:application/x-www-form-urlencoded" -X POST -d "chat_id=${user_id}&text=${message}&disable_web_page_preview=true" "$api_url" &>/dev/null 65 | } 66 | echo -e "\n1、安装bot依赖...\n" 67 | apk --no-cache add -f zlib-dev gcc jpeg-dev python3-dev musl-dev freetype-dev 68 | echo -e "\nbot依赖安装成功...\n" 69 | 70 | echo -e "2、下载bot所需文件...\n" 71 | if [ -d ${repo_path}/.git ]; then 72 | jbot_md5sum_old=$(cd $dir_bot; find . -type f \( -name "*.py" -o -name "*.ttf" \) | xargs md5sum) 73 | git_pull_scripts ${repo_path} 74 | cp -rf "$repo_path/jbot" $dir_root 75 | jbot_md5sum_new=$(cd $dir_bot; find . -type f \( -name "*.py" -o -name "*.ttf" \) | xargs md5sum) 76 | if [[ "$jbot_md5sum_new" != "$jbot_md5sum_old" ]]; then 77 | notify_telegram "检测到BOT程序有更新,BOT将重启。\n\n友情提醒:如果当前有从BOT端发起的正在运行的任务,将被中断。\n\n本条消息由jup程序通过BOT发出。" 78 | fi 79 | else 80 | git_clone_scripts ${url} ${repo_path} "main" 81 | cp -rf "$repo_path/jbot" $dir_root 82 | fi 83 | if [[ ! -f "$dir_root/config/bot.json" ]]; then 84 | cp -f "$repo_path/config/bot.json" "$dir_root/config" 85 | fi 86 | echo -e "\nbot文件下载成功...\n" 87 | echo -e "3、安装python3依赖...\n" 88 | cd $dir_bot 89 | pip3 config set global.index-url https://mirrors.aliyun.com/pypi/simple/ 90 | pip3 --default-timeout=100 install -r requirements.txt --no-cache-dir 91 | echo -e "\npython3依赖安装成功...\n" 92 | echo -e "4、启动bot程序...\n" 93 | cd $dir_root 94 | if [ ! -d "/ql/log/bot" ]; then 95 | mkdir $dir_root/log/bot 96 | fi 97 | if [[ -z $(grep -E "123456789" $dir_root/config/bot.json) ]]; then 98 | if [ -d "/ql" ]; then 99 | ps -ef | grep "python3 -m jbot" | grep -v grep | awk '{print $1}' | xargs kill -9 2>/dev/null 100 | nohup python3 -m jbot >$dir_root/log/bot/bot.log 2>&1 & 101 | echo -e "bot启动成功...\n" 102 | else 103 | cd $dir_bot 104 | pm2 start ecosystem.config.js 105 | cd $dir_root 106 | pm2 restart jbot 107 | echo -e "bot启动成功...\n" 108 | fi 109 | else 110 | echo -e "似乎 $dir_root/config/bot.json 还未修改为你自己的信息,可能是首次部署容器,因此不启动Telegram Bot...\n配置好bot.json后再次运行本程序即可启动" 111 | fi 112 | exit 0 113 | -------------------------------------------------------------------------------- /jbot/__init__.py: -------------------------------------------------------------------------------- 1 | from telethon import TelegramClient, connection 2 | import json 3 | import os 4 | import logging 5 | 6 | JD_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 7 | CONFIG_DIR = f'{JD_DIR}/config' 8 | SCRIPTS_DIR = f'{JD_DIR}/scripts' 9 | OWN_DIR = f'{JD_DIR}/own' 10 | BOT_DIR = f'{JD_DIR}/jbot' 11 | _DiyScripts = f'{JD_DIR}/diyscripts' 12 | LOG_DIR = f'{JD_DIR}/log' 13 | SHORTCUT_FILE = f'{CONFIG_DIR}/shortcut.list' 14 | BOT_LOG_FILE = f'{LOG_DIR}/bot/run.log' 15 | BOT_JSON_FILE = f'{CONFIG_DIR}/bot.json' 16 | QR_IMG_FILE = f'{CONFIG_DIR}/qr.jpg' 17 | BOT_SET_JSON_FILE_USER = f'{CONFIG_DIR}/botset.json' 18 | BOT_SET_JSON_FILE = f'{BOT_DIR}/set.json' 19 | 20 | if not os.path.exists(f'{LOG_DIR}/bot'): 21 | os.mkdir(f'{LOG_DIR}/bot') 22 | logging.basicConfig( 23 | format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO, filename=BOT_LOG_FILE, 24 | filemode='w') 25 | logger = logging.getLogger(__name__) 26 | if os.path.exists(BOT_JSON_FILE): 27 | with open(BOT_JSON_FILE, 'r', encoding='utf-8') as f: 28 | BOT = json.load(f) 29 | if os.path.exists(BOT_SET_JSON_FILE_USER): 30 | with open(BOT_SET_JSON_FILE_USER, 'r', encoding='utf-8') as f: 31 | BOT_SET = json.load(f) 32 | else: 33 | with open(BOT_SET_JSON_FILE, 'r', encoding='utf-8') as f: 34 | BOT_SET = json.load(f) 35 | if BOT_SET.get('开启别名') and BOT_SET['开启别名'].lower() == 'true': 36 | ch_name = True 37 | else: 38 | ch_name = False 39 | chat_id = int(BOT['user_id']) 40 | # 机器人 TOKEN 41 | TOKEN = BOT['bot_token'] 42 | # HOSTAPI = bot['apihost'] 43 | # 发消息的TG代理 44 | # my.telegram.org申请到的api_id,api_hash 45 | API_ID = BOT['api_id'] 46 | API_HASH = BOT['api_hash'] 47 | PROXY_START = BOT['proxy'] 48 | START_CMD = BOT['StartCMD'] 49 | PROXY_TYPE = BOT['proxy_type'] 50 | connectionType = connection.ConnectionTcpMTProxyRandomizedIntermediate if PROXY_TYPE == "MTProxy" else connection.ConnectionTcpFull 51 | if BOT.get('proxy_user') and BOT['proxy_user'] != "代理的username,有则填写,无则不用动": 52 | proxy = { 53 | 'proxy_type': BOT['proxy_type'], 54 | 'addr': BOT['proxy_add'], 55 | 'port': BOT['proxy_port'], 56 | 'username': BOT['proxy_user'], 57 | 'password': BOT['proxy_password']} 58 | elif PROXY_TYPE == "MTProxy": 59 | proxy = (BOT['proxy_add'], BOT['proxy_port'], BOT['proxy_secret']) 60 | else: 61 | proxy = (BOT['proxy_type'], BOT['proxy_add'], BOT['proxy_port']) 62 | # 开启tg对话 63 | if PROXY_START and BOT.get('noretry') and BOT['noretry']: 64 | jdbot = TelegramClient('bot', API_ID, API_HASH, connection=connectionType, 65 | proxy=proxy).start(bot_token=TOKEN) 66 | elif PROXY_START: 67 | jdbot = TelegramClient('bot', API_ID, API_HASH, connection=connectionType, 68 | proxy=proxy, connection_retries=None).start(bot_token=TOKEN) 69 | elif BOT.get('noretry') and BOT['noretry']: 70 | jdbot = TelegramClient('bot', API_ID, API_HASH).start(bot_token=TOKEN) 71 | else: 72 | jdbot = TelegramClient('bot', API_ID, API_HASH, 73 | connection_retries=None).start(bot_token=TOKEN) 74 | -------------------------------------------------------------------------------- /jbot/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | 4 | import json 5 | from . import jdbot, chat_id, logger, LOG_DIR, BOT_SET_JSON_FILE_USER, BOT_SET_JSON_FILE, BOT_SET, BOT_DIR 6 | from .utils import load_module 7 | import os 8 | import random 9 | from .bot.update import version, botlog 10 | BOT_UP_LOG = f'{LOG_DIR}/bot/up.log' 11 | BOT_M_DIR = f'{BOT_DIR}/bot/' 12 | BOT_D_DIR = f'{BOT_DIR}/diy/' 13 | BOT_U_DIR = f'{BOT_DIR}/user/' 14 | logger.info('loading bot module...') 15 | load_module('bot', BOT_M_DIR) 16 | logger.info('loading diy module...') 17 | load_module('diy', BOT_D_DIR) 18 | logger.info('loading user module...') 19 | load_module('user', BOT_U_DIR) 20 | 21 | async def new_ver(): 22 | info = '[项目地址](https://github.com/SuMaiKaDe/) \t| \t[交流频道](https://t.me/tiangongtong) ' 23 | if os.path.exists(BOT_UP_LOG): 24 | is_new = False 25 | with open(BOT_UP_LOG, 'r', encoding='utf-8') as f: 26 | logs = f.read() 27 | if version in logs: 28 | is_new = True 29 | return 30 | if not is_new: 31 | with open(BOT_UP_LOG, 'a', encoding='utf-8') as f: 32 | f.writelines([version, botlog]) 33 | await jdbot.send_message(chat_id, f'[机器人上新了](https://github.com/SuMaiKaDe/jddockerbot/tree/master)\n{botlog}\n运行日志为log/bot/run.log\n\n\t{info}', link_preview=False) 34 | else: 35 | with open(BOT_UP_LOG, 'w+', encoding='utf-8') as f: 36 | f.writelines([version, botlog]) 37 | await jdbot.send_message(chat_id, f'[机器人上新了](https://github.com/SuMaiKaDe/jddockerbot/tree/master)\n{botlog}\n运行日志为log/bot/run.log\n\n\t{info}', link_preview=False) 38 | 39 | 40 | async def bot_set_init(): 41 | try: 42 | with open(BOT_SET_JSON_FILE, 'r', encoding='utf-8') as f: 43 | bot_set = json.load(f) 44 | if os.path.exists(BOT_SET_JSON_FILE_USER): 45 | with open(BOT_SET_JSON_FILE_USER, 'r', encoding='utf-8') as f: 46 | user_set = json.load(f) 47 | if user_set['版本'] != bot_set['版本']: 48 | for i in user_set: 49 | if '版本' not in i and not isinstance(user_set[i], dict): 50 | bot_set[i] = user_set[i] 51 | elif isinstance(user_set[i], dict): 52 | for j in user_set[i]: 53 | bot_set[i][j] = user_set[i][j] 54 | else: 55 | continue 56 | with open(BOT_SET_JSON_FILE_USER, 'w+', encoding='utf-8') as f: 57 | json.dump(bot_set, f) 58 | else: 59 | with open(BOT_SET_JSON_FILE_USER, 'w+', encoding='utf-8') as f: 60 | json.dump(bot_set, f) 61 | except Exception as e: 62 | logger.info(str(e)) 63 | 64 | 65 | async def hello(): 66 | if BOT_SET.get('启动问候') and BOT_SET['启动问候'].lower() == 'true': 67 | info = '[项目地址](https://github.com/SuMaiKaDe/) \t| \t[交流频道](https://t.me/tiangongtong) ' 68 | hello_words = BOT_SET["启动问候语"].split("|") 69 | hello_word = hello_words[random.randint(0, len(hello_words) - 1)] 70 | await jdbot.send_message(chat_id, f'{str(hello_word)}\n\n\t{info}', link_preview=False) 71 | 72 | 73 | if __name__ == "__main__": 74 | with jdbot: 75 | jdbot.loop.create_task(new_ver()) 76 | jdbot.loop.create_task(bot_set_init()) 77 | jdbot.loop.create_task(hello()) 78 | jdbot.loop.run_forever() 79 | -------------------------------------------------------------------------------- /jbot/bot/aff.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id,JD_DIR 3 | 4 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/aff$')) 5 | async def bot_aff(event): 6 | await jdbot.send_message(chat_id,'感谢您',file=f'{JD_DIR}/jbot/font/aff.jpg') 7 | -------------------------------------------------------------------------------- /jbot/bot/auth.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, logger, chat_id, BOT_SET, ch_name, CONFIG_DIR 3 | import requests 4 | import os 5 | import json 6 | import time 7 | 8 | if os.environ.get('QL_DIR'): 9 | AUTH_FILE = f'{CONFIG_DIR}/auth.json' 10 | else: 11 | AUTH_FILE = None 12 | 13 | 14 | @jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/auth')) 15 | async def bot_ql_login(event): 16 | if AUTH_FILE is None: 17 | await jdbot.send_message(chat_id, '此命令仅支持青龙面板') 18 | return None 19 | msg_text = event.raw_text.split(' ') 20 | msg = await jdbot.send_message(chat_id, '正在登录,请稍后') 21 | try: 22 | res = None 23 | if isinstance(msg_text, list) and len(msg_text) == 2: 24 | code_login = msg_text[-1] 25 | if len(code_login) == 6: 26 | res = ql_login(code_login) 27 | else: 28 | res = '两步验证的验证码有误' 29 | else: 30 | res = ql_login() 31 | await jdbot.edit_message(msg, res) 32 | except Exception as e: 33 | await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 34 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 35 | 36 | 37 | def ql_login(code: str = None): 38 | 39 | try: 40 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 41 | auth = json.load(f) 42 | token = auth['token'] 43 | if token and len(token) > 10: 44 | headers = { 45 | 'Authorization': f'Bearer {token}' 46 | } 47 | res = requests.get('http://127.0.0.1:5600/api/crons', params={ 48 | 'searchValue': '', 't': int(round(time.time() * 1000))}, headers=headers).text 49 | if res.find('code":200') > -1: 50 | return '当前登录状态未失效\n无需重新登录' 51 | if code: 52 | url = 'http://127.0.0.1:5600/api/user/two-factor/login' 53 | data = {'username': auth['username'], 54 | 'password': auth['password'], 'code': code} 55 | res = requests.put(url, json=data).json() 56 | else: 57 | url = 'http://127.0.0.1:5600/api/login' 58 | data = {'username': auth['username'], 'password': auth['password']} 59 | res = requests.post(url, json=data).json() 60 | if res['code'] == 200: 61 | return '自动登录成功,请重新执行命令' 62 | if res['message'].find('两步验证') > -1: 63 | return ' 当前登录已过期,且已开启两步登录验证,请使用命令/auth 六位验证码 完成登录' 64 | return res['message'] 65 | except Exception as e: 66 | return '自动登录出错:' + str(e) 67 | 68 | 69 | if ch_name: 70 | jdbot.add_event_handler(bot_ql_login, events.NewMessage( 71 | chats=chat_id, pattern=BOT_SET['命令别名']['auth'])) 72 | -------------------------------------------------------------------------------- /jbot/bot/bean.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageFont, ImageDraw 2 | from telethon import events 3 | from .. import LOG_DIR, jdbot, chat_id, BOT_SET, BOT_DIR, logger, ch_name 4 | from prettytable import PrettyTable 5 | import subprocess 6 | from .beandata import get_bean_data 7 | from .utils import V4 8 | 9 | BEAN_IN_FILE = f'{LOG_DIR}/bean_income.csv' 10 | BEAN_OUT_FILE = f'{LOG_DIR}/bean_outlay.csv' 11 | BEAN_TOTAL_FILE = f'{LOG_DIR}/bean_total.csv' 12 | BEAN_IMG = f'{LOG_DIR}/bean.jpg' 13 | FONT_FILE = f'{BOT_DIR}/font/jet.ttf' 14 | 15 | 16 | @jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/bean')) 17 | async def bot_bean(event): 18 | msg_text = event.raw_text.split(' ') 19 | try: 20 | msg = await jdbot.send_message(chat_id, '正在查询,请稍后') 21 | if isinstance(msg_text, list) and len(msg_text) == 2: 22 | text = msg_text[-1] 23 | else: 24 | text = None 25 | if V4 and text == 'in': 26 | subprocess.check_output( 27 | 'jcsv', shell=True, stderr=subprocess.STDOUT) 28 | creat_bean_counts(BEAN_IN_FILE) 29 | await jdbot.delete_messages(chat_id, msg) 30 | await jdbot.send_message(chat_id, '您的近日收入情况', file=BEAN_IMG) 31 | elif V4 and text == 'out': 32 | subprocess.check_output( 33 | 'jcsv', shell=True, stderr=subprocess.STDOUT) 34 | creat_bean_counts(BEAN_OUT_FILE) 35 | await jdbot.delete_messages(chat_id, msg) 36 | await jdbot.send_message(chat_id, '您的近日支出情况', file=BEAN_IMG) 37 | elif not V4 and (text == 'in' or text == 'out' or text is None): 38 | await jdbot.edit_message(msg, 'QL暂不支持使用bean in、out ,请使用/bean n n为数字') 39 | elif text and int(text): 40 | res = get_bean_data(int(text)) 41 | if res['code'] != 200: 42 | await jdbot.delete_messages(chat_id, msg) 43 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(res["data"])}') 44 | else: 45 | creat_bean_count(res['data'][3], res['data'] 46 | [0], res['data'][1], res['data'][2][1:]) 47 | await jdbot.delete_messages(chat_id, msg) 48 | await jdbot.send_message(chat_id, f'您的账号{text}收支情况', file=BEAN_IMG) 49 | elif not text: 50 | subprocess.check_output( 51 | 'jcsv', shell=True, stderr=subprocess.STDOUT) 52 | creat_bean_counts(BEAN_TOTAL_FILE) 53 | await jdbot.delete_messages(chat_id, msg) 54 | await jdbot.send_message(chat_id, '您的总京豆情况', file=BEAN_IMG) 55 | else: 56 | await jdbot.delete_messages(chat_id, msg) 57 | await jdbot.send_message(chat_id, '青龙暂仅支持/bean n n为账号数字') 58 | except Exception as e: 59 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 60 | logger.error(f'something wrong,I\'m sorry{str(e)}') 61 | 62 | if ch_name: 63 | jdbot.add_event_handler(bot_bean, events.NewMessage( 64 | chats=chat_id, pattern=BOT_SET['命令别名']['bean'])) 65 | 66 | 67 | def creat_bean_count(date, beansin, beansout, beanstotal): 68 | tb = PrettyTable() 69 | tb.add_column('DATE', date) 70 | tb.add_column('BEANSIN', beansin) 71 | tb.add_column('BEANSOUT', beansout) 72 | tb.add_column('TOTAL', beanstotal) 73 | font = ImageFont.truetype(FONT_FILE, 18) 74 | im = Image.new("RGB", (500, 260), (244, 244, 244)) 75 | dr = ImageDraw.Draw(im) 76 | dr.text((10, 5), str(tb), font=font, fill="#000000") 77 | im.save(BEAN_IMG) 78 | 79 | 80 | def creat_bean_counts(csv_file): 81 | with open(csv_file, 'r', encoding='utf-8') as f: 82 | data = f.readlines() 83 | tb = PrettyTable() 84 | num = len(data[-1].split(',')) - 1 85 | title = ['DATE'] 86 | for i in range(0, num): 87 | title.append('COUNT'+str(i+1)) 88 | tb.field_names = title 89 | data = data[-7:] 90 | for line in data: 91 | row = line.split(',') 92 | if len(row) > len(title): 93 | row = row[:len(title)] 94 | elif len(row) < len(title): 95 | i = len(title) - len(row) 96 | for _ in range(0, i): 97 | row.append(str(0)) 98 | tb.add_row(row) 99 | length = 172 + 100 * num 100 | im = Image.new("RGB", (length, 400), (244, 244, 244)) 101 | dr = ImageDraw.Draw(im) 102 | font = ImageFont.truetype(FONT_FILE, 18) 103 | dr.text((10, 5), str(tb), font=font, fill="#000000") 104 | im.save(BEAN_IMG) 105 | -------------------------------------------------------------------------------- /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 CONFIG_SH_FILE, get_cks, AUTH_FILE, QL,logger 8 | SHA_TZ = timezone( 9 | timedelta(hours=8), 10 | name='Asia/Shanghai', 11 | ) 12 | requests.adapters.DEFAULT_RETRIES = 5 13 | session = requests.session() 14 | session.keep_alive = False 15 | 16 | url = "https://api.m.jd.com/api" 17 | 18 | 19 | def gen_body(page): 20 | body = { 21 | "beginDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), 22 | "endDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), 23 | "pageNo": page, 24 | "pageSize": 20, 25 | } 26 | return body 27 | 28 | 29 | def gen_params(page): 30 | body = gen_body(page) 31 | params = { 32 | "functionId": "jposTradeQuery", 33 | "appid": "swat_miniprogram", 34 | "client": "tjj_m", 35 | "sdkName": "orderDetail", 36 | "sdkVersion": "1.0.0", 37 | "clientVersion": "3.1.3", 38 | "timestamp": int(round(time.time() * 1000)), 39 | "body": json.dumps(body) 40 | } 41 | return params 42 | 43 | 44 | def get_beans_7days(ck): 45 | try: 46 | day_7 = 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 | days = [] 59 | for i in range(0, 7): 60 | days.append( 61 | (datetime.date.today() - datetime.timedelta(days=i)).strftime("%Y-%m-%d")) 62 | beans_in = {key: 0 for key in days} 63 | beans_out = {key: 0 for key in days} 64 | while day_7: 65 | page = page + 1 66 | resp = session.get(url, params=gen_params(page), 67 | headers=headers, timeout=100).text 68 | res = json.loads(resp) 69 | if res['resultCode'] == 0: 70 | for i in res['data']['list']: 71 | for date in days: 72 | if str(date) in i['createDate'] and i['amount'] > 0: 73 | beans_in[str(date)] = beans_in[str( 74 | date)] + i['amount'] 75 | break 76 | elif str(date) in i['createDate'] and i['amount'] < 0: 77 | beans_out[str(date)] = beans_out[str( 78 | date)] + i['amount'] 79 | break 80 | if i['createDate'].split(' ')[0] not in str(days): 81 | day_7 = False 82 | else: 83 | return {'code': 400, 'data': res} 84 | return {'code': 200, 'data': [beans_in, beans_out, days]} 85 | except Exception as e: 86 | logger.error(str(e)) 87 | return {'code': 400, 'data': str(e)} 88 | 89 | 90 | def get_total_beans(ck): 91 | try: 92 | headers = { 93 | "Host": "wxapp.m.jd.com", 94 | "Connection": "keep-alive", 95 | "charset": "utf-8", 96 | "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", 97 | "Content-Type": "application/x-www-form-urlencoded;", 98 | "Accept-Encoding": "gzip, compress, deflate, br", 99 | "Cookie": ck, 100 | } 101 | jurl = "https://wxapp.m.jd.com/kwxhome/myJd/home.json" 102 | resp = session.get(jurl, headers=headers, timeout=100).text 103 | res = json.loads(resp) 104 | return res['user']['jingBean'] 105 | except Exception as e: 106 | logger.error(str(e)) 107 | 108 | def get_bean_data(i): 109 | try: 110 | if QL: 111 | ckfile = AUTH_FILE 112 | else: 113 | ckfile = CONFIG_SH_FILE 114 | cookies = get_cks(ckfile) 115 | if cookies: 116 | ck = cookies[i-1] 117 | beans_res = get_beans_7days(ck) 118 | beantotal = get_total_beans(ck) 119 | if beans_res['code'] != 200: 120 | return beans_res 121 | else: 122 | beans_in, beans_out = [], [] 123 | beanstotal = [int(beantotal), ] 124 | for i in beans_res['data'][0]: 125 | beantotal = int( 126 | beantotal) - int(beans_res['data'][0][i]) - int(beans_res['data'][1][i]) 127 | beans_in.append(int(beans_res['data'][0][i])) 128 | beans_out.append(int(str(beans_res['data'][1][i]).replace('-', ''))) 129 | beanstotal.append(beantotal) 130 | return {'code': 200, 'data': [beans_in[::-1], beans_out[::-1], beanstotal[::-1], beans_res['data'][2][::-1]]} 131 | except Exception as e: 132 | logger.error(str(e)) -------------------------------------------------------------------------------- /jbot/bot/chart.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, LOG_DIR, logger, BOT_SET, ch_name 3 | from ..bot.quickchart import QuickChart 4 | from .beandata import get_bean_data 5 | BEAN_IMG = f'{LOG_DIR}/bot/bean.jpeg' 6 | 7 | 8 | @jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/chart')) 9 | async def my_chart(event): 10 | msg_text = event.raw_text.split(' ') 11 | msg = await jdbot.send_message(chat_id, '正在查询,请稍后') 12 | try: 13 | if isinstance(msg_text, list) and len(msg_text) == 2: 14 | text = msg_text[-1] 15 | else: 16 | text = None 17 | if text and int(text): 18 | res = get_bean_data(int(text)) 19 | if res['code'] != 200: 20 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(res["data"])}') 21 | else: 22 | creat_chart(res['data'][3], f'账号{str(text)}', 23 | res['data'][0], res['data'][1], res['data'][2][1:]) 24 | await jdbot.delete_messages(chat_id, msg) 25 | msg = await jdbot.send_message(chat_id, f'您的账号{text}收支情况', file=BEAN_IMG) 26 | else: 27 | msg = await jdbot.edit_message(msg, '请正确使用命令\n/chart n n为第n个账号') 28 | except Exception as e: 29 | await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 30 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 31 | 32 | if ch_name: 33 | jdbot.add_event_handler(my_chart, events.NewMessage( 34 | chats=chat_id, pattern=BOT_SET['命令别名']['chart'])) 35 | 36 | 37 | def creat_chart(xdata, title, bardata, bardata2, linedate): 38 | qc = QuickChart() 39 | qc.background_color = '#fff' 40 | qc.width = "1000" 41 | qc.height = "600" 42 | qc.config = { 43 | "type": "bar", 44 | "data": { 45 | "labels": xdata, 46 | "datasets": [ 47 | { 48 | "label": "IN", 49 | "backgroundColor": [ 50 | "rgb(255, 99, 132)", 51 | "rgb(255, 159, 64)", 52 | "rgb(255, 205, 86)", 53 | "rgb(75, 192, 192)", 54 | "rgb(54, 162, 235)", 55 | "rgb(153, 102, 255)", 56 | "rgb(255, 99, 132)" 57 | ], 58 | "yAxisID": "y1", 59 | "data": bardata 60 | }, 61 | { 62 | "label": "OUT", 63 | "backgroundColor": [ 64 | "rgb(255, 99, 132)", 65 | "rgb(255, 159, 64)", 66 | "rgb(255, 205, 86)", 67 | "rgb(75, 192, 192)", 68 | "rgb(54, 162, 235)", 69 | "rgb(153, 102, 255)", 70 | "rgb(255, 99, 132)" 71 | ], 72 | "yAxisID": "y1", 73 | "data": bardata2 74 | }, 75 | { 76 | "label": "TOTAL", 77 | "type": "line", 78 | "fill": False, 79 | "backgroundColor": "rgb(201, 203, 207)", 80 | "yAxisID": "y2", 81 | "data": linedate 82 | } 83 | ] 84 | }, 85 | "options": { 86 | "plugins": { 87 | "datalabels": { 88 | "anchor": 'end', 89 | "align": -100, 90 | "color": '#666', 91 | "font": { 92 | "size": 20, 93 | } 94 | }, 95 | }, 96 | "legend": { 97 | "labels": { 98 | "fontSize": 20, 99 | "fontStyle": 'bold', 100 | } 101 | }, 102 | "title": { 103 | "display": True, 104 | "text": f'{title} 收支情况', 105 | "fontSize": 24, 106 | }, 107 | "scales": { 108 | "xAxes": [{ 109 | "ticks": { 110 | "fontSize": 24, 111 | } 112 | }], 113 | "yAxes": [ 114 | { 115 | "id": "y1", 116 | "type": "linear", 117 | "display": False, 118 | "position": "left", 119 | "ticks": { 120 | "max": int(int(max([max(bardata), max(bardata2)])+100)*2) 121 | }, 122 | "scaleLabel": { 123 | "fontSize": 20, 124 | "fontStyle": 'bold', 125 | } 126 | }, 127 | { 128 | "id": "y2", 129 | "type": "linear", 130 | "display": False, 131 | "ticks": { 132 | "min": int(min(linedate)*2-(max(linedate))-100), 133 | "max": int(int(max(linedate))) 134 | }, 135 | "position": "right" 136 | } 137 | ] 138 | } 139 | } 140 | } 141 | qc.to_file(BEAN_IMG) 142 | -------------------------------------------------------------------------------- /jbot/bot/cmd.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, START_CMD, chat_id, logger, BOT_SET, ch_name 3 | from .utils import cmd 4 | 5 | 6 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/cmd')) 7 | async def my_cmd(event): 8 | """接收/cmd命令后执行程序""" 9 | logger.info(f'即将执行{event.raw_text}命令') 10 | msg_text = event.raw_text.split(' ') 11 | try: 12 | if isinstance(msg_text, list): 13 | text = ' '.join(msg_text[1:]) 14 | else: 15 | text = None 16 | if START_CMD and text: 17 | await cmd(text) 18 | logger.info(text) 19 | elif START_CMD: 20 | msg = '''请正确使用/cmd命令,如 21 | /cmd jlog # 删除旧日志 22 | /cmd jup # 更新所有脚本 23 | /cmd jcode # 导出所有互助码 24 | /cmd jcsv # 记录豆豆变化情况 25 | 不建议直接使用cmd命令执行脚本,请使用/node或/snode 26 | ''' 27 | await jdbot.send_message(chat_id, msg) 28 | else: 29 | await jdbot.send_message(chat_id, '未开启CMD命令,如需使用请修改配置文件') 30 | logger.info(f'执行{event.raw_text}命令完毕') 31 | except Exception as e: 32 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 33 | logger.error(f'发生了某些错误\n{str(e)}') 34 | 35 | 36 | if ch_name: 37 | jdbot.add_event_handler(my_cmd, events.NewMessage( 38 | chats=chat_id, pattern=BOT_SET['命令别名']['cmd'])) 39 | -------------------------------------------------------------------------------- /jbot/bot/cron.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | import json 3 | import os 4 | from asyncio import exceptions 5 | from .. import jdbot, chat_id, logger, LOG_DIR, ch_name, BOT_SET 6 | from ..bot.utils import QL, press_event, split_list, cron_manage, AUTH_FILE 7 | 8 | 9 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/cron')) 10 | async def my_cron(event): 11 | """接收/cron后执行程序""" 12 | logger.info(f'即将执行{event.raw_text}命令') 13 | msg_text = event.raw_text.split(' ') 14 | try: 15 | SENDER = event.sender_id 16 | msg = await jdbot.send_message(chat_id, '正在查询请稍后') 17 | if QL: 18 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 19 | auth = json.load(f) 20 | buttons = [{'name': '运行', 'data': 'run'}, {'name': '日志', 'data': 'log'}, {'name': '编辑', 'data': 'edit'}, { 21 | 'name': '启用', 'data': 'enable'}, {'name': '禁用', 'data': 'disable'}, {'name': '删除', 'data': 'del'}, {'name': '取消', 'data': 'cancel'}, {'name': '上级', 'data': 'up'}] 22 | else: 23 | auth = {'token': ''} 24 | buttons = [{'name': '运行', 'data': 'run'}, {'name': '编辑', 'data': 'edit'}, { 25 | 'name': '启用', 'data': 'enable'}, {'name': '禁用', 'data': 'disable'}, {'name': '删除', 'data': 'del'}, {'name': '取消', 'data': 'cancel'}, {'name': '上级', 'data': 'up'}] 26 | if isinstance(msg_text, list) and len(msg_text) == 2: 27 | text = msg_text[-1] 28 | else: 29 | text = None 30 | logger.info(f'命令参数值为:{text}') 31 | if not text: 32 | await jdbot.edit_message(msg, '请正确使用cron命令,后边需跟关键字。/cron abcd') 33 | return 34 | go_up = True 35 | async with jdbot.conversation(SENDER, timeout=60) as conv: 36 | while go_up: 37 | res = cron_manage('search', text, auth['token']) 38 | logger.info(f'任务查询结果:{res}') 39 | if res['code'] == 200: 40 | await jdbot.delete_messages(chat_id, msg) 41 | if QL: 42 | markup = [Button.inline( 43 | i['name'], data=str(res['data'].index(i))) for i in res['data']] 44 | else: 45 | markup = [Button.inline(i, data=res['data'][i]) 46 | for i in res['data']] 47 | markup = split_list(markup, int(BOT_SET['每页列数'])) 48 | markup.append([Button.inline('取消', data='cancel')]) 49 | msg = await jdbot.send_message( 50 | chat_id, '查询结果如下,点击按钮查看详细信息', buttons=markup) 51 | conv_data = await conv.wait_event(press_event(SENDER)) 52 | resp = bytes.decode(conv_data.data) 53 | if resp == 'cancel': 54 | await jdbot.edit_message(msg, '对话已取消') 55 | conv.cancel() 56 | go_up = False 57 | return 58 | if QL: 59 | cron_info = '名称:\n\t{name}\n任务:\n\t{command}\n定时:\n\t{schedule}\n是否已禁用:\n\t{isDisabled}\n\t0--表示启用,1--表示禁用'.format( 60 | **res['data'][int(resp)]) 61 | markup = [Button.inline(i['name'], data=i['data']) 62 | for i in buttons] 63 | else: 64 | cron_info = f'{resp}' 65 | markup = [Button.inline(i['name'], data=i['data']) 66 | for i in buttons] 67 | markup = split_list(markup, int(BOT_SET['每页列数'])) 68 | msg = await jdbot.edit_message(msg, cron_info, buttons=markup) 69 | conv_data = await conv.wait_event(press_event(SENDER)) 70 | btnres = bytes.decode(conv_data.data) 71 | if btnres == 'cancel': 72 | msg = await jdbot.edit_message(msg, '对话已取消') 73 | conv.cancel() 74 | go_up = False 75 | return 76 | elif btnres == 'up': 77 | continue 78 | elif btnres == 'edit': 79 | go_up = False 80 | if QL: 81 | info = '```{name}-->{command}-->{schedule}```'.format( 82 | **res["data"][int(resp)]) 83 | else: 84 | info = f'```{resp}```' 85 | await jdbot.delete_messages(chat_id, msg) 86 | msg = await conv.send_message(f'{info}\n请复制信息并进行修改') 87 | respones = await conv.get_response() 88 | respones = respones.raw_text 89 | if QL: 90 | res['data'][int(resp)]['name'], res['data'][int( 91 | resp)]['command'], res['data'][int(resp)]['schedule'] = respones.split('-->') 92 | cronres = cron_manage( 93 | 'edit', res['data'][int(resp)], auth['token']) 94 | else: 95 | cronres = cron_manage( 96 | 'edit', f'{resp}-->{respones}\n', auth['token']) 97 | else: 98 | go_up = False 99 | if QL: 100 | crondata = res['data'][int(resp)] 101 | else: 102 | crondata = resp 103 | cronres = cron_manage( 104 | btnres, crondata, auth['token']) 105 | if cronres['code'] == 200: 106 | if 'data' not in cronres.keys(): 107 | cronres['data'] = 'success' 108 | await jdbot.delete_messages(chat_id, msg) 109 | if len(cronres['data']) <= 4000: 110 | msg = await jdbot.send_message(chat_id, f"指令发送成功,结果如下:\n{cronres['data']}") 111 | elif len(cronres['data']) > 4000: 112 | _log = f'{LOG_DIR}/bot/qlcron.log' 113 | with open(_log, 'w+', encoding='utf-8') as f: 114 | f.write(cronres['data']) 115 | msg = await jdbot.send_message(chat_id, '日志结果较长,请查看文件', file=_log) 116 | os.remove(_log) 117 | else: 118 | await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{cronres["data"]}') 119 | else: 120 | go_up = False 121 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(res["data"])}') 122 | logger.info(f'执行{event.raw_text}命令完毕') 123 | except exceptions.TimeoutError: 124 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 125 | logger.error(f'选择已超时,对话已停止') 126 | except Exception as e: 127 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 128 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 129 | 130 | if ch_name: 131 | jdbot.add_event_handler(my_cron, events.NewMessage( 132 | from_users=chat_id, pattern=BOT_SET['命令别名']['cron'])) 133 | 134 | 135 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/addcron')) 136 | async def my_addcron(event): 137 | try: 138 | SENDER = event.sender_id 139 | msg = await jdbot.send_message(chat_id, f'请稍后,正在查询') 140 | if QL: 141 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 142 | auth = json.load(f) 143 | info = '任务名称-->任务命令-->定时\n```测试2-->ql repo xxxxxx.git "jd"-->0 6 * * *```' 144 | else: 145 | info = '```0 0 * * * jtask /jd/own/abcd.js```' 146 | auth = {'token': ''} 147 | markup = [Button.inline('是', data='yes'), 148 | Button.inline('否', data='cancel')] 149 | async with jdbot.conversation(SENDER, timeout=30) as conv: 150 | await jdbot.delete_messages(chat_id, msg) 151 | msg = await conv.send_message('是否确认添加cron', buttons=markup) 152 | conv_data = await conv.wait_event(press_event(SENDER)) 153 | res = bytes.decode(conv_data.data) 154 | if res == 'cancel': 155 | msg = await jdbot.edit_message(msg, '对话已取消') 156 | conv.cancel() 157 | else: 158 | await jdbot.delete_messages(chat_id, msg) 159 | msg = await conv.send_message(f'点击复制下方信息进行修改,并发送给我\n{info}') 160 | resp = await conv.get_response() 161 | if QL: 162 | crondata = {} 163 | crondata['name'], crondata['command'], crondata['schedule'] = resp.raw_text.split( 164 | '-->') 165 | res = cron_manage('add', crondata, auth['token']) 166 | else: 167 | crondata = resp.raw_text 168 | res = cron_manage('add', crondata, auth['token']) 169 | if res['code'] == 200: 170 | await jdbot.delete_messages(chat_id, msg) 171 | msg = await jdbot.send_message(chat_id, '已成功添加定时任务') 172 | else: 173 | await jdbot.delete_messages(chat_id, msg) 174 | msg = await jdbot.send_message(chat_id, f'添加定时任务时发生了一些错误\n{res["data"]}') 175 | except exceptions.TimeoutError: 176 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 177 | except Exception as e: 178 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 179 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 180 | if ch_name: 181 | jdbot.add_event_handler(my_addcron, events.NewMessage( 182 | from_users=chat_id, pattern=BOT_SET['命令别名']['addcron'])) 183 | -------------------------------------------------------------------------------- /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, JD_DIR, BOT_SET, ch_name 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 my_edit(event): 11 | '''定义编辑文件操作''' 12 | logger.info(f'即将执行{event.raw_text}命令') 13 | msg_text = event.raw_text.split(' ') 14 | SENDER = event.sender_id 15 | path = JD_DIR 16 | page = 0 17 | if isinstance(msg_text,list) and len(msg_text) == 2: 18 | text = msg_text[-1] 19 | else: 20 | text = None 21 | logger.info(f'命令参数值为:{text}') 22 | if text and os.path.isfile(text): 23 | try: 24 | with open(text, 'r', encoding='utf-8') as f: 25 | lines = f.readlines() 26 | filelist = split_list(lines, 15) 27 | path = text 28 | except Exception as e: 29 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 30 | elif text and os.path.isdir(text): 31 | path = text 32 | filelist = None 33 | elif text: 34 | await jdbot.send_message(chat_id, 'please marksure it\'s a dir or a file') 35 | filelist = None 36 | else: 37 | filelist = None 38 | async with jdbot.conversation(SENDER, timeout=120) as conv: 39 | msg = await conv.send_message('正在查询,请稍后') 40 | while path: 41 | path, msg, page, filelist = await edit_file(conv, SENDER, path, msg, page, filelist) 42 | 43 | 44 | if ch_name: 45 | jdbot.add_event_handler(my_edit, events.NewMessage( 46 | from_users=chat_id, pattern=BOT_SET['命令别名']['edit'])) 47 | 48 | 49 | async def edit_file(conv, SENDER, path, msg, page, filelist): 50 | mybtn = [Button.inline('上一页', data='up'), Button.inline('下一页', data='next'), Button.inline( 51 | '上级', data='updir'), Button.inline('取消', data='cancel')] 52 | mybtn2 = [[Button.inline('上一页', data='up'), Button.inline( 53 | '下一页', data='next'), Button.inline('取消', data='cancel')], [Button.inline('上十页', data='up10'), Button.inline( 54 | '下十页', data='next10'), Button.inline('编辑', data='edit')]] 55 | try: 56 | if filelist and type(filelist[0][0]) == str: 57 | markup = filelist 58 | newmarkup = markup[page] 59 | msg = await jdbot.edit_message(msg, "".join(newmarkup), buttons=mybtn2) 60 | else: 61 | if filelist: 62 | markup = filelist 63 | newmarkup = markup[page] 64 | if mybtn not in newmarkup: 65 | newmarkup.append(mybtn) 66 | else: 67 | dir = os.listdir(path) 68 | dir.sort() 69 | markup = [Button.inline(file, data=str( 70 | file)) for file in dir] 71 | markup = split_list(markup, int(BOT_SET['每页列数'])) 72 | if len(markup) > 30: 73 | markup = split_list(markup, 30) 74 | newmarkup = markup[page] 75 | newmarkup.append(mybtn) 76 | else: 77 | newmarkup = markup 78 | if path == JD_DIR: 79 | newmarkup.append([Button.inline('取消', data='cancel')]) 80 | else: 81 | newmarkup.append( 82 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 83 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=newmarkup) 84 | convdata = await conv.wait_event(press_event(SENDER)) 85 | res = bytes.decode(convdata.data) 86 | if res == 'cancel': 87 | msg = await jdbot.edit_message(msg, '对话已取消') 88 | conv.cancel() 89 | return None, None, None, None 90 | elif res == 'next': 91 | page = page + 1 92 | if page > len(markup) - 1: 93 | page = 0 94 | return path, msg, page, markup 95 | elif res == 'up': 96 | page = page - 1 97 | if page < 0: 98 | page = len(markup) - 1 99 | return path, msg, page, markup 100 | elif res == 'next10': 101 | page = page + 10 102 | if page > len(markup) - 1: 103 | page = 0 104 | return path, msg, page, markup 105 | elif res == 'up10': 106 | page = page - 10 107 | if page < 0: 108 | page = len(markup) - 1 109 | return path, msg, page, markup 110 | elif res == 'updir': 111 | path = '/'.join(path.split('/')[:-1]) 112 | if path == '': 113 | path = JD_DIR 114 | return path, msg, page, None 115 | elif res == 'edit': 116 | await jdbot.send_message(chat_id, '请复制并修改以下内容,修改完成后发回机器人,2分钟内有效\n发送`cancel`或`取消`取消对话') 117 | await jdbot.delete_messages(chat_id, msg) 118 | msg = await conv.send_message(f'`{"".join(newmarkup)}`') 119 | resp = await conv.get_response() 120 | if resp.raw_text == 'cancel' or resp.raw_text == '取消': 121 | await jdbot.delete_messages(chat_id,msg) 122 | await jdbot.send_message(chat_id, '对话已取消') 123 | conv.cancel() 124 | return 125 | markup[page] = resp.raw_text.split('\n') 126 | for a in range(len(markup[page])): 127 | markup[page][a] = markup[page][a]+'\n' 128 | shutil.copy(path, f'{path}.bak') 129 | with open(path, 'w+', encoding='utf-8') as f: 130 | markup = ["".join(a) for a in markup] 131 | f.writelines(markup) 132 | await jdbot.send_message(chat_id, f'文件已修改成功,原文件备份为{path}.bak') 133 | conv.cancel() 134 | return None, None, None, None 135 | elif os.path.isfile(f'{path}/{res}'): 136 | msg = await jdbot.edit_message(msg, '文件读取中...请稍候') 137 | with open(f'{path}/{res}', 'r', encoding='utf-8') as f: 138 | lines = f.readlines() 139 | lines = split_list(lines, 15) 140 | page = 0 141 | return f'{path}/{res}', msg, page, lines 142 | else: 143 | return f'{path}/{res}', msg, page, None 144 | except exceptions.TimeoutError: 145 | msg = await jdbot.edit_message(msg, '选择已超时,本次对话已停止') 146 | return None, None, None, None 147 | except Exception as e: 148 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 149 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 150 | return None, None, None, None 151 | -------------------------------------------------------------------------------- /jbot/bot/forward.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, BOT_SET, ch_name 3 | import random 4 | 5 | 6 | @jdbot.on(events.NewMessage()) 7 | async def my_forward(event): 8 | try: 9 | if BOT_SET['开启机器人转发'].lower() != 'false' and event.chat_id != chat_id and str(event.chat_id) not in BOT_SET['机器人黑名单']: 10 | await jdbot.send_message(chat_id, f'您的机器人接收到消息。来自:```{event.chat_id}```') 11 | await jdbot.forward_messages(chat_id, event.id, event.chat_id) 12 | elif BOT_SET['开启机器人转发'].lower() != 'false' and str(event.chat_id) in BOT_SET['机器人黑名单']: 13 | words = BOT_SET['机器人垃圾话'].split('|') 14 | word = words[random.randint(0, len(words) - 1)] 15 | await jdbot.send_message(event.chat_id, str(word)) 16 | except Exception as e: 17 | await jdbot.send_message(chat_id, str(e)) 18 | 19 | 20 | @jdbot.on(events.NewMessage(chats=chat_id, pattern=r'^/reply')) 21 | async def my_reply(event): 22 | try: 23 | msg_text = event.raw_text.split(' ') 24 | if isinstance(msg_text,list) and len(msg_text) == 3: 25 | text = msg_text[1:] 26 | else: 27 | text = None 28 | if not text: 29 | info = '使用方法:/reply 123455676 你想说的话' 30 | await jdbot.send_message(chat_id, info) 31 | else: 32 | await jdbot.send_message(int(text[0]), text[1]) 33 | except Exception as e: 34 | await jdbot.send_message(chat_id, str(e)) 35 | 36 | if ch_name: 37 | jdbot.add_event_handler(my_reply, events.NewMessage( 38 | chats=chat_id, pattern=BOT_SET['命令别名']['reply'])) 39 | 40 | 41 | @jdbot.on(events.NewMessage(incoming=True, chats=chat_id)) 42 | async def resp(event): 43 | try: 44 | if event.reply_to: 45 | reply = await event.get_reply_message() 46 | if reply.fwd_from.from_id: 47 | await jdbot.send_message(reply.fwd_from.from_id.user_id, event.message.text) 48 | else: 49 | await jdbot.send_message(chat_id, '不能获取到对方的id,请使用/reply进行回复') 50 | except Exception as e: 51 | await jdbot.send_message(chat_id, str(e)) 52 | -------------------------------------------------------------------------------- /jbot/bot/getfile.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | from asyncio import exceptions 3 | from .. import jdbot, chat_id, SCRIPTS_DIR, CONFIG_DIR, logger 4 | from .utils import press_event, backup_file, add_cron, cmd, DIY_DIR, TASK_CMD, V4 5 | 6 | 7 | @jdbot.on(events.NewMessage(from_users=chat_id)) 8 | async def bot_get_file(event): 9 | '''定义文件操作''' 10 | try: 11 | v4btn = [[Button.inline('放入config', data=CONFIG_DIR), Button.inline('放入scripts', data=SCRIPTS_DIR), Button.inline('放入OWN文件夹', data=DIY_DIR)], [ 12 | Button.inline('放入scripts并运行', data='node1'), Button.inline('放入OWN并运行', data='node'), Button.inline('取消', data='cancel')]] 13 | btn = [[Button.inline('放入config', data=CONFIG_DIR), Button.inline('放入scripts', data=SCRIPTS_DIR)], [ 14 | Button.inline('放入scripts并运行', data='node1'), Button.inline('取消', data='cancel')]] 15 | SENDER = event.sender_id 16 | if event.message.file: 17 | markup = [] 18 | filename = event.message.file.name 19 | cmdtext = None 20 | async with jdbot.conversation(SENDER, timeout=180) 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 | markup = [Button.inline('是', data='yes'), 30 | Button.inline('否', data='no')] 31 | if res == 'cancel': 32 | msg = await jdbot.edit_message(msg, '对话已取消') 33 | conv.cancel() 34 | else: 35 | msg = await jdbot.edit_message(msg, '是否尝试自动加入定时', buttons=markup) 36 | convdata2 = await conv.wait_event(press_event(SENDER)) 37 | res2 = bytes.decode(convdata2.data) 38 | if res == 'node': 39 | backup_file(f'{DIY_DIR}/{filename}') 40 | await jdbot.download_media(event.message, DIY_DIR) 41 | cmdtext = f'{TASK_CMD} {DIY_DIR}/{filename} now' 42 | with open(f'{DIY_DIR}/{filename}', 'r', encoding='utf-8') as f: 43 | resp = f.read() 44 | if res2 == 'yes': 45 | await add_cron(jdbot, conv, resp, filename, msg, SENDER, markup, DIY_DIR) 46 | else: 47 | await jdbot.edit_message(msg, '脚本已保存到DIY文件夹,并成功运行') 48 | conv.cancel() 49 | elif res == 'node1': 50 | backup_file(f'{SCRIPTS_DIR}/{filename}') 51 | await jdbot.download_media(event.message, SCRIPTS_DIR) 52 | with open(f'{SCRIPTS_DIR}/{filename}', 'r', encoding='utf-8') as f: 53 | resp = f.read() 54 | cmdtext = f'{TASK_CMD} {SCRIPTS_DIR}/{filename} now' 55 | if res2 == 'yes': 56 | await add_cron(jdbot, conv, resp, filename, msg, SENDER, markup, SCRIPTS_DIR) 57 | else: 58 | await jdbot.edit_message(msg, '脚本已保存到SCRIPTS文件夹,并成功运行') 59 | conv.cancel() 60 | else: 61 | backup_file(f'{res}/{filename}') 62 | await jdbot.download_media(event.message, res) 63 | with open(f'{res}/{filename}', 'r', encoding='utf-8') as f: 64 | resp = f.read() 65 | if res2 == 'yes': 66 | await add_cron(jdbot, conv, resp, filename, msg, SENDER, markup, res) 67 | else: 68 | await jdbot.edit_message(msg, f'{filename}已保存到{res}文件夹') 69 | if cmdtext: 70 | await cmd(cmdtext) 71 | except exceptions.TimeoutError: 72 | msg = await jdbot.send_message(chat_id, '选择已超时,对话已停止') 73 | except Exception as e: 74 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 75 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 76 | -------------------------------------------------------------------------------- /jbot/bot/geturlfile.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | import requests 3 | from asyncio import exceptions 4 | from .. import jdbot, chat_id, logger, SCRIPTS_DIR, CONFIG_DIR, logger, BOT_SET, ch_name 5 | from .utils import press_event, backup_file, DIY_DIR, TASK_CMD, V4, cmd, add_cron 6 | 7 | 8 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/dl')) 9 | async def bot_url_file(event): 10 | '''接收github链接后执行程序''' 11 | msg_text = event.raw_text.split(' ') 12 | try: 13 | if isinstance(msg_text,list) and len(msg_text) == 2: 14 | url = msg_text[-1] 15 | else: 16 | url = None 17 | SENDER = event.sender_id 18 | if not url: 19 | await jdbot.send_message(chat_id, '请正确使用dl命令,需加入下载链接') 20 | return 21 | else: 22 | msg = await jdbot.send_message(chat_id, '请稍后正在下载文件') 23 | if '下载代理' in BOT_SET.keys() and str(BOT_SET['下载代理']).lower() != 'false' and 'github' in url: 24 | url = f'{str(BOT_SET["下载代理"])}/{url}' 25 | file_name = url.split('/')[-1] 26 | resp = requests.get(url).text 27 | v4btn = [[Button.inline('放入config', data=CONFIG_DIR), Button.inline('放入scripts', data=SCRIPTS_DIR), Button.inline('放入OWN文件夹', data=DIY_DIR)], [ 28 | Button.inline('放入scripts并运行', data='node1'), Button.inline('放入OWN并运行', data='node'), Button.inline('取消', data='cancel')]] 29 | btn = [[Button.inline('放入config', data=CONFIG_DIR), Button.inline('放入scripts', data=SCRIPTS_DIR)], [ 30 | Button.inline('放入scripts并运行', data='node1'), Button.inline('取消', data='cancel')]] 31 | if resp: 32 | cmdtext = None 33 | markup = [] 34 | async with jdbot.conversation(SENDER, timeout=30) as conv: 35 | await jdbot.delete_messages(chat_id, msg) 36 | msg = await conv.send_message('请选择您要放入的文件夹或操作:\n') 37 | if V4: 38 | markup = v4btn 39 | else: 40 | markup = btn 41 | msg = await jdbot.edit_message(msg, '请选择您要放入的文件夹或操作:', buttons=markup) 42 | convdata = await conv.wait_event(press_event(SENDER)) 43 | res = bytes.decode(convdata.data) 44 | markup = [Button.inline('是', data='yes'), 45 | Button.inline('否', data='no')] 46 | if res == 'cancel': 47 | msg = await jdbot.edit_message(msg, '对话已取消') 48 | conv.cancel() 49 | else: 50 | msg = await jdbot.edit_message(msg, '是否尝试自动加入定时', buttons=markup) 51 | convdata2 = await conv.wait_event(press_event(SENDER)) 52 | res2 = bytes.decode(convdata2.data) 53 | if res == 'node': 54 | backup_file(f'{DIY_DIR}/{file_name}') 55 | with open(f'{DIY_DIR}/{file_name}', 'w+', encoding='utf-8') as f: 56 | f.write(resp) 57 | cmdtext = f'{TASK_CMD} {DIY_DIR}/{file_name} now' 58 | if res2 == 'yes': 59 | await add_cron(jdbot, conv, resp, file_name, 60 | msg, SENDER, markup, DIY_DIR) 61 | else: 62 | await jdbot.edit_message(msg, '脚本已保存到DIY文件夹,并成功运行') 63 | conv.cancel() 64 | elif res == 'node1': 65 | backup_file(f'{SCRIPTS_DIR}/{file_name}') 66 | with open(f'{SCRIPTS_DIR}/{file_name}', 'w+', encoding='utf-8') as f: 67 | f.write(resp) 68 | cmdtext = f'{TASK_CMD} {SCRIPTS_DIR}/{file_name} now' 69 | if res2 == 'yes': 70 | await add_cron(jdbot, conv, resp, file_name, 71 | msg, SENDER, markup, SCRIPTS_DIR) 72 | else: 73 | await jdbot.edit_message(msg, '脚本已保存到SCRIPTS文件夹,并成功运行') 74 | conv.cancel() 75 | else: 76 | backup_file(f'{res}/{file_name}') 77 | with open(f'{res}/{file_name}', 'w+', encoding='utf-8') as f: 78 | f.write(resp) 79 | if res2 == 'yes': 80 | await add_cron(jdbot, conv, resp, file_name, 81 | msg, SENDER, markup, res) 82 | else: 83 | await jdbot.edit_message(msg, f'{file_name}已保存到{res}文件夹') 84 | if cmdtext: 85 | await cmd(cmdtext) 86 | except exceptions.TimeoutError: 87 | msg = await jdbot.send_message(chat_id, '选择已超时,对话已停止') 88 | except Exception as e: 89 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 90 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 91 | 92 | if ch_name: 93 | jdbot.add_event_handler(bot_url_file, events.NewMessage( 94 | from_users=chat_id, pattern=BOT_SET['命令别名']['dl'])) 95 | -------------------------------------------------------------------------------- /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 bot_help(event): 7 | '''接收/help命令后执行程序''' 8 | msg_text = event.raw_text.split(' ') 9 | if len(msg_text) == 2: 10 | text = msg_text[-1] 11 | else: 12 | text = 'mhelp' 13 | mhelp = ''' 14 | a-自定义快捷按钮 15 | addcron-增加定时 16 | bean-获取收支 17 | clearboard-删除快捷按钮 18 | chart-统计收支变化 19 | cron-管理定时设定 20 | cmd-执行cmd命令 21 | edit-编辑文件 22 | env-管理环境变量 23 | dl-下载文件 24 | getfile-获取目录下文件 25 | log-选择日志 26 | node-执行js脚本文件,绝对路径 27 | set-BOT设置 28 | setname-设置命令别名 29 | setshort-设置自定义按钮 30 | snode-选择脚本后台运行 31 | start-开始使用本程序''' 32 | bean = '/bean 加数字,获取该账户近期收支情况\n/bean in\out获取所有账户近期收或支情况\n/bean 获取账户总豆数量' 33 | cmd = '/cmd用于执行cmd命令,如果命令持续10分钟仍未结束,将强行终止,以保障机器人响应' 34 | edit = '/edit 进入/jd目录选择文件进行编辑,仅限简易编辑\n/edit /xx/config进入config目录选择文件编辑\n/edit /xx/config/config.sh 直接编辑config.sh文件' 35 | node = '/node 用于执行js脚本 用法:\n/node /jd/own/abc/def.js' 36 | getfile = '/getfile 进入/jd目录选择文件进行获取\n/getfile /xx/config进入config目录选择文件获取\n/getfile /xx/config/config.sh 直接获取config.sh文件' 37 | setshort = '/setshort 用于设置快捷方式,格式如下:\n更新-->jup\nAAA-->BBB这种格式使用/a选择\n/bean 1\n/edit /xx/config/config.sh\n以“/”开头的为机器人命令快捷,使用/b选择' 38 | snode = '/snode 选择脚本并运行' 39 | chart = '/chart 加数字,统计该账户近期收支情况' 40 | botset = '''/set 41 | - snode时中英文切换 42 | - 每列几个按钮 43 | - 是否开启机器人转发 44 | - 机器人聊天黑名单 45 | - 使用,或者空格等符号进行用户id区隔 46 | - 机器人黑名单垃圾话 47 | - 加入机器人黑名单后,使用 | 区隔设置垃圾话,会随机挑选垃圾话回复该用户''' 48 | cron = ''' - /cron 命令 49 | - /cron 加关键字 可进行cron管理''' 50 | help_me = {'bean': bean, 'cmd': cmd, 'edit': edit, 'node': node, 51 | 'getfile': getfile, 'setshort': setshort, 'snode': snode, 'chart': chart, 'mhelp': mhelp, 'set': botset, 'cron': cron} 52 | await jdbot.send_message(chat_id, help_me[text]) 53 | -------------------------------------------------------------------------------- /jbot/bot/node.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, BOT_SET, ch_name 3 | from .utils import cmd, TASK_CMD 4 | 5 | 6 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/node')) 7 | async def bot_node(event): 8 | '''接收/node命令后执行程序''' 9 | msg_text = event.raw_text.split(' ') 10 | if isinstance(msg_text,list) and len(msg_text) == 2: 11 | text = ''.join(msg_text[1:]) 12 | else: 13 | text = None 14 | if not text: 15 | res = '''请正确使用/node命令,如 16 | /node /abc/123.js 运行abc/123.js脚本 17 | /node /own/abc.js 运行own/abc.js脚本 18 | ''' 19 | await jdbot.send_message(chat_id, res) 20 | else: 21 | await cmd('{} {}'.format(TASK_CMD, text)) 22 | 23 | if ch_name: 24 | jdbot.add_event_handler(bot_node, events.NewMessage( 25 | from_users=chat_id, pattern=BOT_SET['命令别名']['node'])) 26 | -------------------------------------------------------------------------------- /jbot/bot/qlenv.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | import json 3 | import os 4 | from asyncio import exceptions 5 | from .. import jdbot, chat_id, logger, LOG_DIR, ch_name, BOT_SET 6 | from ..bot.utils import QL, press_event,env_manage_QL, split_list, AUTH_FILE 7 | 8 | 9 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/env')) 10 | async def bot_env_ql(event): 11 | '''接收/env后执行程序''' 12 | msg_text = event.raw_text.split(' ') 13 | try: 14 | SENDER = event.sender_id 15 | msg = await jdbot.send_message(chat_id, '正在查询请稍后') 16 | if QL: 17 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 18 | auth = json.load(f) 19 | buttons = [{'name': '编辑', 'data': 'edit'}, { 20 | 'name': '启用', 'data': 'enable'}, {'name': '禁用', 'data': 'disable'}, {'name': '删除', 'data': 'del'},{'name': '上级', 'data': 'up'}, {'name': '取消', 'data': 'cancel'}] 21 | else: 22 | await jdbot.edit_message(msg, '目前仅QL支持环境变量查询修改,其他环境开发中') 23 | return 24 | if isinstance(msg_text, list) and len(msg_text) == 2: 25 | text = msg_text[-1] 26 | else: 27 | text = None 28 | if not text: 29 | await jdbot.edit_message(msg, '请正确使用env命令,后边需跟关键字\n/env abcd') 30 | return 31 | go_up = True 32 | async with jdbot.conversation(SENDER, timeout=120) as conv: 33 | while go_up: 34 | res = env_manage_QL('search', text, auth['token']) 35 | if res['code'] == 200: 36 | await jdbot.delete_messages(chat_id, msg) 37 | markup = [Button.inline( 38 | i['name'], data=str(res['data'].index(i))) for i in res['data']] 39 | markup = split_list(markup, int(BOT_SET['每页列数'])) 40 | markup.append([Button.inline('取消', data='cancel')]) 41 | msg = await jdbot.send_message( 42 | chat_id, '查询结果如下,点击按钮查看详细信息', buttons=markup) 43 | convdata = await conv.wait_event(press_event(SENDER)) 44 | resp = bytes.decode(convdata.data) 45 | if resp == 'cancel': 46 | await jdbot.edit_message(msg, '对话已取消') 47 | conv.cancel() 48 | go_up = False 49 | return 50 | if 'remarks' in res['data'][int(resp)]: 51 | croninfo = '名称:\n\t{name}\n任务:\n\t{value}\n备注:\n\t{remarks}\n是否已禁用:\n\t{status}\n\t0--表示启用,1--表示禁用,2--表示未知'.format( 52 | **res['data'][int(resp)]) 53 | else: 54 | croninfo = '名称:\n\t{name}\n任务:\n\t{value}\n是否已禁用:\n\t{status}\n\t0--表示启用,1--表示禁用,2--表示未知'.format( 55 | **res['data'][int(resp)]) 56 | markup = [Button.inline(i['name'], data=i['data']) 57 | for i in buttons] 58 | markup = split_list(markup, int(BOT_SET['每页列数'])) 59 | msg = await jdbot.edit_message(msg, croninfo, buttons=markup) 60 | convdata = await conv.wait_event(press_event(SENDER)) 61 | btnres = bytes.decode(convdata.data) 62 | if btnres == 'cancel': 63 | msg = await jdbot.edit_message(msg, '对话已取消') 64 | conv.cancel() 65 | go_up = False 66 | return 67 | elif btnres == 'up': 68 | continue 69 | elif btnres == 'edit': 70 | go_up = False 71 | if 'remarks' in res['data'][int(resp)]: 72 | info = '```{name}-->{value}-->{remarks}```'.format( 73 | **res["data"][int(resp)]) 74 | else: 75 | info = '```{name}-->{value}-->备注```'.format( 76 | **res["data"][int(resp)]) 77 | await jdbot.delete_messages(chat_id, msg) 78 | msg = await conv.send_message(f'{info}\n请复制信息并进行修改') 79 | respones = await conv.get_response() 80 | respones = respones.raw_text 81 | res['data'][int(resp)]['name'], res['data'][int( 82 | resp)]['value'], res['data'][int(resp)]['remarks'] = respones.split('-->') 83 | cronres = env_manage_QL( 84 | 'edit', res['data'][int(resp)], auth['token']) 85 | else: 86 | go_up = False 87 | envdata = res['data'][int(resp)] 88 | cronres = env_manage_QL( 89 | btnres, envdata, auth['token']) 90 | if cronres['code'] == 200: 91 | if 'data' not in cronres.keys(): 92 | cronres['data'] = 'success' 93 | await jdbot.delete_messages(chat_id, msg) 94 | if len(cronres['data']) <= 4000: 95 | msg = await jdbot.send_message(chat_id, f"指令发送成功,结果如下:\n{cronres['data']}") 96 | elif len(res) > 4000: 97 | _log = f'{LOG_DIR}/bot/qlcron.log' 98 | with open(_log, 'w+', encoding='utf-8') as f: 99 | f.write(cronres['data']) 100 | msg = await jdbot.send_message(chat_id, '日志结果较长,请查看文件', file=_log) 101 | os.remove(_log) 102 | else: 103 | await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{cronres["data"]}') 104 | else: 105 | go_up = False 106 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(res["data"])}') 107 | except exceptions.TimeoutError: 108 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 109 | except Exception as e: 110 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 111 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 112 | 113 | 114 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/addenv')) 115 | async def bot_addenv(event): 116 | try: 117 | SENDER = event.sender_id 118 | if QL: 119 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 120 | auth = json.load(f) 121 | info = '名称-->变量值-->备注\n```JD_COOKIE-->pxxxxxxpxxxxxx;-->bot的cookie```' 122 | else: 123 | await jdbot.send_message(chat_id, '目前仅QL支持环境变量查询修改,其他环境开发中') 124 | return 125 | markup = [Button.inline('是', data='yes'), 126 | Button.inline('否', data='cancel')] 127 | async with jdbot.conversation(SENDER, timeout=30) as conv: 128 | msg = await conv.send_message('是否确认添加新变量', buttons=markup) 129 | convdata = await conv.wait_event(press_event(SENDER)) 130 | res = bytes.decode(convdata.data) 131 | if res == 'cancel': 132 | msg = await jdbot.edit_message(msg, '对话已取消') 133 | conv.cancel() 134 | else: 135 | await jdbot.delete_messages(chat_id, msg) 136 | msg = await conv.send_message(f'点击复制下方信息进行修改,并发送给我\n{info}') 137 | resp = await conv.get_response() 138 | envdata = {} 139 | envdata['name'], envdata['value'], envdata['remarks'] = resp.raw_text.split( 140 | '-->') 141 | res = env_manage_QL('add', envdata, auth['token']) 142 | if res['code'] == 200: 143 | await jdbot.delete_messages(chat_id, msg) 144 | msg = await jdbot.send_message(chat_id, '已成功添加新变量') 145 | else: 146 | await jdbot.delete_messages(chat_id, msg) 147 | msg = await jdbot.send_message(chat_id, f'添加新变量时发生了一些错误\n{res["data"]}') 148 | except exceptions.TimeoutError: 149 | msg = await jdbot.send_message(chat_id, '选择已超时,对话已停止') 150 | except Exception as e: 151 | msg = await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 152 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 153 | if ch_name: 154 | jdbot.add_event_handler(bot_addenv, events.NewMessage( 155 | from_users=chat_id, pattern=BOT_SET['命令别名']['addenv'])) 156 | jdbot.add_event_handler(bot_env_ql, events.NewMessage( 157 | from_users=chat_id, pattern=BOT_SET['命令别名']['myenv'])) 158 | -------------------------------------------------------------------------------- /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/reboot.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id 3 | from .utils import V4, cmd 4 | 5 | 6 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/reboot$')) 7 | async def bot_reboot(event): 8 | await jdbot.send_message(chat_id, '即将重启,请稍后') 9 | if V4: 10 | await cmd('pm2 restart jbot') 11 | else: 12 | await cmd('ql bot') 13 | -------------------------------------------------------------------------------- /jbot/bot/sendfile.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import LOG_DIR, jdbot, chat_id, LOG_DIR, JD_DIR, BOT_SET, ch_name 3 | from .utils import log_btn 4 | import os 5 | 6 | 7 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/log')) 8 | async def bot_log(event): 9 | '''定义日志文件操作''' 10 | SENDER = event.sender_id 11 | path = LOG_DIR 12 | page = 0 13 | filelist = None 14 | async with jdbot.conversation(SENDER, timeout=60) as conv: 15 | msg = await conv.send_message('正在查询,请稍后') 16 | while path: 17 | path, msg, page, filelist = await log_btn(conv, SENDER, path, msg, page, filelist) 18 | 19 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/botlog')) 20 | async def bot_run_log(event): 21 | '''定义日志文件操作''' 22 | await jdbot.send_message(chat_id,'bot运行日志',file=f'{LOG_DIR}/bot/run.log') 23 | 24 | 25 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/getfile')) 26 | async def bot_getfile(event): 27 | '''定义获取文件命令''' 28 | SENDER = event.sender_id 29 | path = JD_DIR 30 | page = 0 31 | msg_text = event.raw_text.split(' ') 32 | if len(msg_text) == 2: 33 | text = msg_text[-1] 34 | else: 35 | text = None 36 | if text and os.path.isfile(text): 37 | await jdbot.send_message(chat_id, '请查收文件', file=text) 38 | return 39 | elif text and os.path.isdir(text): 40 | path = text 41 | filelist = None 42 | elif text: 43 | await jdbot.send_message(chat_id, 'please marksure it\'s a dir or a file') 44 | filelist = None 45 | else: 46 | filelist = None 47 | async with jdbot.conversation(SENDER, timeout=60) as conv: 48 | msg = await conv.send_message('正在查询,请稍后') 49 | while path: 50 | path, msg, page, filelist = await log_btn(conv, SENDER, path, msg, page, filelist) 51 | 52 | if ch_name: 53 | jdbot.add_event_handler(bot_getfile, events.NewMessage( 54 | from_users=chat_id, pattern=BOT_SET['命令别名']['getfile'])) 55 | jdbot.add_event_handler(bot_log, events.NewMessage( 56 | from_users=chat_id, pattern=BOT_SET['命令别名']['log'])) 57 | -------------------------------------------------------------------------------- /jbot/bot/setshort.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, SHORTCUT_FILE, BOT_SET, ch_name 3 | 4 | 5 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/setshort$')) 6 | async def bot_set_short(event): 7 | SENDER = event.sender_id 8 | info = '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部分功能青龙可能不支持,请自行测试,自行设定 ' 9 | info += '\n回复`cancel`或`取消`即可取消本次对话' 10 | async with jdbot.conversation(SENDER, timeout=180) as conv: 11 | msg = await conv.send_message(info) 12 | shortcut = await conv.get_response() 13 | if shortcut.raw_text == 'cancel' or shortcut.raw_text == '取消': 14 | await jdbot.delete_messages(chat_id,msg) 15 | await jdbot.send_message(chat_id, '对话已取消') 16 | conv.cancel() 17 | return 18 | with open(SHORTCUT_FILE, 'w+', encoding='utf-8') as f: 19 | f.write(shortcut.raw_text) 20 | await conv.send_message('已设置成功可通过"/a或/b"使用') 21 | conv.cancel() 22 | 23 | if ch_name: 24 | jdbot.add_event_handler(bot_set_short, events.NewMessage( 25 | from_users=chat_id, pattern=BOT_SET['命令别名']['setshort'])) 26 | -------------------------------------------------------------------------------- /jbot/bot/setting.py: -------------------------------------------------------------------------------- 1 | import json 2 | from telethon import events, Button 3 | from asyncio import exceptions 4 | from .. import jdbot, chat_id, BOT_SET_JSON_FILE_USER, BOT_SET, ch_name 5 | from .utils import split_list, logger, press_event 6 | 7 | 8 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='^/set$')) 9 | async def bot_set(event): 10 | SENDER = event.sender_id 11 | try: 12 | msg = await jdbot.send_message(chat_id, '请稍后,正在查询') 13 | with open(BOT_SET_JSON_FILE_USER, 'r', encoding='utf-8') as f: 14 | myset = json.load(f) 15 | info = '您目前设置如下:\n' 16 | for i in myset: 17 | if '命令别名' in i: 18 | continue 19 | else: 20 | info = info + f'\t\t- {i}-->{myset[i]} \n' 21 | info = info + '请点击您要设置的项目,选择后,输入要设置的值,重启生效,垃圾话以 | 进行区隔,黑名单以空格或逗号或顿号区隔' 22 | btn = [Button.inline(i, i) for i in myset if not isinstance(myset[i],dict)] 23 | btn.append(Button.inline('取消', data='cancel')) 24 | btn = split_list(btn, 3) 25 | async with jdbot.conversation(SENDER, timeout=90) as conv: 26 | msg = await jdbot.edit_message(msg, info, buttons=btn, link_preview=False) 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 | else: 33 | await jdbot.delete_messages(chat_id, msg) 34 | msg = await conv.send_message(f'请输入您要修改的{res}\n如果需要取消,请输入`cancel`或`取消`\n如需自定义或快速修改,请直接修改config/botset.json\n如果为True或False首字符大写\n```{myset[res]}```') 35 | data = await conv.get_response() 36 | if data.raw_text == 'cancel' or data.raw_text == '取消': 37 | await jdbot.delete_messages(chat_id,msg) 38 | await jdbot.send_message(chat_id, '对话已取消') 39 | conv.cancel() 40 | else: 41 | markup = [Button.inline('确认',data='yes'),Button.inline('取消',data='cancel')] 42 | await jdbot.delete_messages(chat_id,msg) 43 | msg = await jdbot.send_message(chat_id, f'是否确认将 ** {res} ** 设置为 **{data.raw_text}**', buttons=markup) 44 | convdata2 = await conv.wait_event(press_event(SENDER)) 45 | res2 = bytes.decode(convdata2.data) 46 | if res2 == 'yes': 47 | myset[res] = data.raw_text 48 | with open(BOT_SET_JSON_FILE_USER, 'w+', encoding='utf-8') as f: 49 | json.dump(myset, f) 50 | await jdbot.delete_messages(chat_id, msg) 51 | msg = await jdbot.send_message(chat_id, '已完成修改,重启后生效') 52 | else: 53 | conv.cancel() 54 | await jdbot.delete_messages(chat_id, msg) 55 | msg = await jdbot.send_message(chat_id, '对话已取消') 56 | return 57 | except exceptions.TimeoutError: 58 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 59 | except Exception as e: 60 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 61 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 62 | 63 | 64 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='^/setname$')) 65 | async def bot_setname(event): 66 | SENDER = event.sender_id 67 | try: 68 | msg = await jdbot.send_message(chat_id, '请稍后,正在查询') 69 | with open(BOT_SET_JSON_FILE_USER, 'r', encoding='utf-8') as f: 70 | myset = json.load(f) 71 | info = '您目前命令别名设置如下:\n' 72 | for i in myset['命令别名']: 73 | info = info + f'\t\t- {i}-->{myset["命令别名"][i]} \n' 74 | info = info + '请点击您要设置的项目,选择后,输入要设置的值,重启生效\n**请注意尽量不要重复,否则可能发生未知错误**' 75 | btn = [Button.inline(i, i) for i in myset['命令别名']] 76 | btn.append(Button.inline('取消', data='cancel')) 77 | btn = split_list(btn, 3) 78 | async with jdbot.conversation(SENDER, timeout=90) as conv: 79 | msg = await jdbot.edit_message(msg, info, buttons=btn, link_preview=False) 80 | convdata = await conv.wait_event(press_event(SENDER)) 81 | res = bytes.decode(convdata.data) 82 | if res == 'cancel': 83 | msg = await jdbot.edit_message(msg, '对话已取消') 84 | conv.cancel() 85 | else: 86 | await jdbot.delete_messages(chat_id, msg) 87 | msg = await conv.send_message(f'请输入您要修改的{res}\n如果需要取消,请输入`cancel`或`取消`\n如需自定义或快速修改,请直接修改config/botset.json\n如果为True或False首字符大写\n```{myset["命令别名"][res]}```') 88 | data = await conv.get_response() 89 | if data.raw_text == 'cancel' or data.raw_text == '取消': 90 | await jdbot.delete_messages(chat_id,msg) 91 | msg = await jdbot.send_message(chat_id, '对话已取消') 92 | conv.cancel() 93 | return 94 | else: 95 | markup = [Button.inline('确认',data='yes'),Button.inline('取消',data='cancel')] 96 | await jdbot.delete_messages(chat_id,msg) 97 | msg = await jdbot.send_message(chat_id, f'是否确认将 ** {res} ** 设置为 **{data.raw_text}**', buttons=markup) 98 | convdata2 = await conv.wait_event(press_event(SENDER)) 99 | res2 = bytes.decode(convdata2.data) 100 | if res2 == 'yes': 101 | myset['命令别名'][res] = data.raw_text 102 | with open(BOT_SET_JSON_FILE_USER, 'w+', encoding='utf-8') as f: 103 | json.dump(myset, f) 104 | await jdbot.delete_messages(chat_id, msg) 105 | msg = await jdbot.send_message(chat_id, '已完成修改,重启后生效') 106 | else: 107 | conv.cancel() 108 | await jdbot.delete_messages(chat_id, msg) 109 | msg = await jdbot.send_message(chat_id, '对话已取消') 110 | return 111 | except exceptions.TimeoutError: 112 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 113 | except Exception as e: 114 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 115 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 116 | 117 | if ch_name: 118 | jdbot.add_event_handler(bot_set, events.NewMessage( 119 | from_users=chat_id, pattern=BOT_SET['命令别名']['set'])) 120 | jdbot.add_event_handler(bot_setname, events.NewMessage( 121 | from_users=chat_id, pattern=BOT_SET['命令别名']['setname'])) 122 | -------------------------------------------------------------------------------- /jbot/bot/short.py: -------------------------------------------------------------------------------- 1 | from telethon import events, Button 2 | from .utils import split_list, press_event, cmd 3 | from asyncio import exceptions 4 | from .. import jdbot, chat_id, SHORTCUT_FILE, logger, BOT_SET, ch_name 5 | 6 | 7 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/a$')) 8 | async def my_a(event): 9 | markup = [] 10 | SENDER = event.sender_id 11 | msg = await jdbot.send_message(chat_id, '正在查询您的常用命令,请稍后') 12 | with open(SHORTCUT_FILE, 'r', encoding='utf-8') as f: 13 | shortcuts = f.readlines() 14 | try: 15 | cmdtext = None 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 | else: 28 | await jdbot.delete_messages(chat_id, msg) 29 | cmdtext = res 30 | conv.cancel() 31 | if cmdtext: 32 | await cmd(cmdtext.replace('nohup ', '')) 33 | except exceptions.TimeoutError: 34 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 35 | except Exception as e: 36 | await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 37 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 38 | 39 | 40 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/b$')) 41 | async def my_b(event): 42 | markup = [] 43 | msg = await jdbot.send_message(chat_id, '正在查询您的常用命令,请稍后') 44 | with open(SHORTCUT_FILE, 'r', encoding='utf-8') as f: 45 | shortcuts = f.readlines() 46 | try: 47 | await jdbot.delete_messages(chat_id, msg) 48 | markup = [Button.text(shortcut, single_use=True) 49 | for shortcut in shortcuts if '-->' not in shortcut] 50 | markup = split_list(markup, int(BOT_SET['每页列数'])) 51 | await jdbot.send_message(chat_id, '请做出您的选择:', buttons=markup) 52 | except Exception as e: 53 | await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 54 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 55 | 56 | 57 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/clearboard$')) 58 | async def my_clear(event): 59 | try: 60 | await jdbot.send_message(chat_id, '已清空您的keyboard',buttons=Button.clear()) 61 | except Exception as e: 62 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 63 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 64 | 65 | if ch_name: 66 | jdbot.add_event_handler(my_a, events.NewMessage( 67 | from_users=chat_id, pattern=BOT_SET['命令别名']['a'])) 68 | jdbot.add_event_handler(my_b, events.NewMessage(from_users=chat_id, pattern=BOT_SET['命令别名']['b'])) 69 | -------------------------------------------------------------------------------- /jbot/bot/snode.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, JD_DIR, BOT_SET, ch_name 3 | from .utils import cmd, snode_btn 4 | 5 | 6 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/snode')) 7 | async def my_snode(event): 8 | '''定义supernode文件命令''' 9 | SENDER = event.sender_id 10 | path = JD_DIR 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 snode_btn(conv, SENDER, path, msg, page, filelist) 17 | if filelist and filelist.startswith('CMD-->'): 18 | await cmd(filelist.replace('CMD-->', '')) 19 | 20 | if ch_name: 21 | jdbot.add_event_handler(my_snode, events.NewMessage( 22 | from_users=chat_id, pattern=BOT_SET['命令别名']['snode'])) 23 | -------------------------------------------------------------------------------- /jbot/bot/start.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id,ch_name 3 | 4 | 5 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='/start')) 6 | async def bot_start(event): 7 | '''接收/start命令后执行程序''' 8 | msg = '''使用方法如下: 9 | /help 获取命令,可直接发送至botfather。 10 | /start 开始使用本程序。 11 | /a 使用你的自定义快捷按钮。 12 | /addcron 增加cron,例:0 0 * * * jtask 13 | /clearboard 删除快捷输入按钮。 14 | /cmd 执行命令,例:/cmd python3 /python/bot.py,则执行python目录下的bot.py。不建议使用BOT使用并发,可能产生不明原因的崩溃。 15 | /cron 进行cron管理。 16 | /dl 下载文件,例:/dl https://raw.githubusercontent.com/SuMaiKaDe/bot/main/requirements.txt 17 | /edit 从目录选择文件并编辑,需要将编辑好信息全部发给BOT,BOT会根据你发的信息进行替换。建议仅编辑config或crontab.list,其他文件慎用!!! 18 | /env 环境变量管理,仅支持青龙面板。 19 | /getfile 获取/jd目录下文件。 20 | /log 查看脚本执行日志。 21 | /node 执行js脚本,输入/node xxxxx.js。如执行非scripts目录js,需输入绝对路径执行。node命令会等待脚本执行完,期间不能使用BOT,建议使用snode命令。 22 | /set 设置。 23 | /setname 设置命令别名。 24 | /setshort 设置自定义按钮,每次设置会覆盖原设置。 25 | /snode 选择脚本执行,只能选择/scripts和/own目录下的脚本,选择完后直接后台运行,不影响BOT响应其他命令。 26 | 27 | 此外,直接发送文件至BOT,会让您选择保存到目标文件夹,支持保存并运行。''' 28 | await jdbot.send_message(chat_id, msg) 29 | 30 | if ch_name: 31 | jdbot.add_event_handler(bot_start,events.NewMessage(from_users=chat_id, pattern='开始')) 32 | -------------------------------------------------------------------------------- /jbot/bot/up.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from .. import jdbot, chat_id, CONFIG_DIR 3 | from .utils import cmd 4 | from .update import version, botlog 5 | import requests 6 | 7 | 8 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern='^/up$')) 9 | async def bot_up(event): 10 | try: 11 | msg = await jdbot.send_message(chat_id, '开始更新程序,请稍候,\n提示开始运行后,机器人会被杀死,并重新启动\n因此后边更新信息不会再推送,不代表机器人不理你了') 12 | res = requests.get( 13 | 'https://ghproxy.com/https://raw.githubusercontent.com/SuMaiKaDe/bot/main/config/bot.sh').text 14 | with open(f'{CONFIG_DIR}/bot.sh', 'w+', encoding='utf-8') as f: 15 | f.write(res) 16 | await cmd(f'bash {CONFIG_DIR}/bot.sh') 17 | await jdbot.delete_messages(chat_id, msg) 18 | except Exception as e: 19 | await jdbot.send_message(chat_id, str(e)) 20 | 21 | 22 | @jdbot.on(events.NewMessage(from_users=chat_id, pattern=r'^/ver$', incoming=True)) 23 | async def bot_ver(event): 24 | await jdbot.send_message(chat_id, f'当前版本\n{version}\n{botlog}') 25 | -------------------------------------------------------------------------------- /jbot/bot/update.py: -------------------------------------------------------------------------------- 1 | version = 'version :1.0.0.0' 2 | botlog = ''' 3 | **2021年9月24日** 4 | 本次更新内容如下: 5 | - 新增 /userlogin /codelogin 6 | codelogin 测试过了没问题, 7 | userlogin 摄像头坏了没办法测试,自行测试吧 8 | 使用二维码/验证码 登录tguser 9 | 配合 /set ‘开启user’ 可以开启tg用户登录 10 | - 几个user功能 id、del re 11 | 回复id 得到id 12 | del n 删除n条消息 13 | re n 转发n条消息 14 | - 新增 /reboot 重启机器人 15 | - 新增 /aff 来杯肥宅快乐水 16 | - NEW BUG... 17 | ''' 18 | 19 | -------------------------------------------------------------------------------- /jbot/bot/utils.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | from functools import wraps 4 | from telethon import events, Button 5 | import re 6 | import time 7 | import json 8 | from .. import jdbot, chat_id, LOG_DIR, logger, JD_DIR, OWN_DIR, CONFIG_DIR, BOT_SET 9 | import asyncio 10 | import datetime 11 | row = int(BOT_SET['每页列数']) 12 | CRON_FILE = f'{CONFIG_DIR}/crontab.list' 13 | BEAN_LOG_DIR = f'{LOG_DIR}/jd_bean_change/' 14 | CONFIG_SH_FILE = f'{CONFIG_DIR}/config.sh' 15 | V4, QL = False, False 16 | if os.environ.get('JD_DIR'): 17 | V4 = True 18 | AUTH_FILE = None 19 | if os.path.exists(f'{CONFIG_DIR}/cookie.sh'): 20 | CONFIG_SH_FILE = f'{CONFIG_DIR}/cookie.sh' 21 | DIY_DIR = OWN_DIR 22 | TASK_CMD = 'jtask' 23 | elif os.environ.get('QL_DIR'): 24 | QL = True 25 | AUTH_FILE = f'{CONFIG_DIR}/auth.json' 26 | DIY_DIR = None 27 | TASK_CMD = 'task' 28 | dirs = os.listdir(LOG_DIR) 29 | for mydir in dirs: 30 | if 'jd_bean_change' in mydir: 31 | BEAN_LOG_DIR = f'{LOG_DIR}/{mydir}' 32 | break 33 | else: 34 | DIY_DIR = None 35 | TASK_CMD = 'node' 36 | 37 | 38 | def Ver_Main(func): 39 | @wraps(func) 40 | def wrapper(*args, **kwargs): 41 | res = func(*args, **kwargs) 42 | if str(res).find('valid sign') > -1 : 43 | msg = ql_login() 44 | return {'code': 400, 'data': msg} 45 | return res 46 | return wrapper 47 | 48 | 49 | def ql_login(): 50 | url = 'http://127.0.0.1:5600/api/login' 51 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 52 | auth = json.load(f) 53 | data = {'username': auth['username'], 'password': auth['password']} 54 | try: 55 | res = requests.post(url, json=data).json() 56 | if res['code'] == 200: 57 | return '自动登录成功,请重新执行命令' 58 | elif res['message'].find('两步验证') > -1: 59 | return ' 当前登录已过期,且已开启两步登录验证,请使用命令/auth 六位验证码完成登录' 60 | except Exception as e: 61 | return '自动登录出错:' + str(e) 62 | 63 | 64 | def get_cks(ckfile): 65 | ck_reg = re.compile(r'pt_key=\S*?;.*?pt_pin=\S*?;') 66 | cookie_file = r'/ql/db/cookie.db' 67 | if QL and not os.path.exists(cookie_file): 68 | with open(ckfile, 'r', encoding='utf-8') as f: 69 | auth = json.load(f) 70 | lines = str(env_manage_QL('search', 'JD_COOKIE', auth['token'])) 71 | elif QL: 72 | with open(f'{CONFIG_DIR}/cookie.sh', 'r', encoding='utf-8') as f: 73 | lines = f.read() 74 | else: 75 | with open(ckfile, 'r', encoding='utf-8') as f: 76 | lines = f.read() 77 | cookies = ck_reg.findall(lines) 78 | for ck in cookies: 79 | if ck == 'pt_key=xxxxxxxxxx;pt_pin=xxxx;': 80 | cookies.remove(ck) 81 | break 82 | return cookies 83 | 84 | 85 | def split_list(datas, n, row: bool = True): 86 | """一维列表转二维列表,根据N不同,生成不同级别的列表""" 87 | length = len(datas) 88 | size = length / n + 1 if length % n else length/n 89 | _datas = [] 90 | if not row: 91 | size, n = n, size 92 | for i in range(int(size)): 93 | start = int(i * n) 94 | end = int((i + 1) * n) 95 | _datas.append(datas[start:end]) 96 | return _datas 97 | 98 | 99 | def backup_file(file): 100 | '''如果文件存在,则备份,并更新''' 101 | if os.path.exists(file): 102 | try: 103 | os.rename(file, f'{file}.bak') 104 | except WindowsError: 105 | os.remove(f'{file}.bak') 106 | os.rename(file, f'{file}.bak') 107 | 108 | 109 | def press_event(user_id): 110 | return events.CallbackQuery(func=lambda e: e.sender_id == user_id) 111 | 112 | 113 | async def cmd(cmdtext): 114 | '''定义执行cmd命令''' 115 | try: 116 | msg = await jdbot.send_message(chat_id, '开始执行命令') 117 | p = await asyncio.create_subprocess_shell( 118 | cmdtext, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) 119 | res_bytes, res_err = await p.communicate() 120 | res = res_bytes.decode('utf-8') 121 | if res.find('先登录') > -1: 122 | await jdbot.delete_messages(chat_id, msg) 123 | res, msg = ql_login() 124 | await jdbot.send_message(chat_id, msg) 125 | return 126 | if len(res) == 0: 127 | await jdbot.edit_message(msg, '已执行,但返回值为空') 128 | elif len(res) <= 4000: 129 | await jdbot.delete_messages(chat_id, msg) 130 | await jdbot.send_message(chat_id, res) 131 | elif len(res) > 4000: 132 | tmp_log = f'{LOG_DIR}/bot/{cmdtext.split("/")[-1].split(".js")[0]}-{datetime.datetime.now().strftime("%H-%M-%S")}.log' 133 | with open(tmp_log, 'w+', encoding='utf-8') as f: 134 | f.write(res) 135 | await jdbot.delete_messages(chat_id, msg) 136 | await jdbot.send_message(chat_id, '执行结果较长,请查看日志', file=tmp_log) 137 | os.remove(tmp_log) 138 | except Exception as e: 139 | await jdbot.send_message(chat_id, f'something wrong,I\'m sorry\n{str(e)}') 140 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 141 | 142 | 143 | def get_ch_names(path, dir): 144 | '''获取文件中文名称,如无则返回文件名''' 145 | file_ch_names = [] 146 | reg = r'new Env\(\'[\S]+?\'\)' 147 | ch_name = False 148 | for file in dir: 149 | try: 150 | if os.path.isdir(f'{path}/{file}'): 151 | file_ch_names.append(file) 152 | elif file.endswith('.js') and file != 'jdCookie.js' and file != 'getJDCookie.js' and file != 'JD_extra_cookie.js' and 'ShareCode' not in file: 153 | with open(f'{path}/{file}', 'r', encoding='utf-8') as f: 154 | lines = f.readlines() 155 | for line in lines: 156 | if 'new Env' in line: 157 | line = line.replace('\"', '\'') 158 | res = re.findall(reg, line) 159 | if len(res) != 0: 160 | res = res[0].split('\'')[-2] 161 | file_ch_names.append(f'{res}--->{file}') 162 | ch_name = True 163 | break 164 | if not ch_name: 165 | file_ch_names.append(f'{file}--->{file}') 166 | ch_name = False 167 | else: 168 | continue 169 | except: 170 | continue 171 | return file_ch_names 172 | 173 | 174 | async def log_btn(conv, sender, path, msg, page, files_list): 175 | '''定义log日志按钮''' 176 | my_btns = [Button.inline('上一页', data='up'), Button.inline( 177 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 178 | try: 179 | if files_list: 180 | markup = files_list 181 | new_markup = markup[page] 182 | if my_btns not in new_markup: 183 | new_markup.append(my_btns) 184 | else: 185 | dir = os.listdir(path) 186 | dir.sort() 187 | if path == LOG_DIR: 188 | markup = [Button.inline("_".join(file.split("_")[-2:]), data=str(file)) 189 | for file in dir] 190 | elif os.path.dirname(os.path.realpath(path)) == LOG_DIR: 191 | markup = [Button.inline("-".join(file.split("-")[-5:]), data=str(file)) 192 | for file in dir] 193 | else: 194 | markup = [Button.inline(file, data=str(file)) 195 | for file in dir] 196 | markup = split_list(markup, row) 197 | if len(markup) > 30: 198 | markup = split_list(markup, 30) 199 | new_markup = markup[page] 200 | new_markup.append(my_btns) 201 | else: 202 | new_markup = markup 203 | if path == JD_DIR: 204 | new_markup.append([Button.inline('取消', data='cancel')]) 205 | else: 206 | new_markup.append( 207 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 208 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=new_markup) 209 | convdata = await conv.wait_event(press_event(sender)) 210 | res = bytes.decode(convdata.data) 211 | if res == 'cancel': 212 | msg = await jdbot.edit_message(msg, '对话已取消') 213 | conv.cancel() 214 | return None, None, None, None 215 | elif res == 'next': 216 | page = page + 1 217 | if page > len(markup) - 1: 218 | page = 0 219 | return path, msg, page, markup 220 | elif res == 'up': 221 | page = page - 1 222 | if page < 0: 223 | page = len(markup) - 1 224 | return path, msg, page, markup 225 | elif res == 'updir': 226 | path = '/'.join(path.split('/')[:-1]) 227 | if path == '': 228 | path = JD_DIR 229 | return path, msg, page, None 230 | elif os.path.isfile(f'{path}/{res}'): 231 | msg = await jdbot.edit_message(msg, '文件发送中,请注意查收') 232 | await conv.send_file(f'{path}/{res}') 233 | msg = await jdbot.edit_message(msg, f'{res}发送成功,请查收') 234 | conv.cancel() 235 | return None, None, None, None 236 | else: 237 | return f'{path}/{res}', msg, page, None 238 | except asyncio.exceptions.TimeoutError: 239 | msg = await jdbot.edit_message(msg, '选择已超时,本次对话已停止') 240 | return None, None, None, None 241 | except Exception as e: 242 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 243 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 244 | return None, None, None, None 245 | 246 | 247 | async def snode_btn(conv, sender, path, msg, page, files_list): 248 | '''定义scripts脚本按钮''' 249 | my_btns = [Button.inline('上一页', data='up'), Button.inline( 250 | '下一页', data='next'), Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')] 251 | try: 252 | if files_list: 253 | markup = files_list 254 | new_markup = markup[page] 255 | if my_btns not in new_markup: 256 | new_markup.append(my_btns) 257 | else: 258 | if path == JD_DIR and V4: 259 | dir = ['scripts', OWN_DIR.split('/')[-1]] 260 | elif path == JD_DIR and QL: 261 | dir = ['scripts'] 262 | else: 263 | dir = os.listdir(path) 264 | if BOT_SET['中文'].lower() == "true": 265 | dir = get_ch_names(path, dir) 266 | dir.sort() 267 | markup = [Button.inline(file.split('--->')[0], data=str(file.split('--->')[-1])) 268 | for file in dir if os.path.isdir(f'{path}/{file}') or file.endswith('.js')] 269 | markup = split_list(markup, row) 270 | if len(markup) > 30: 271 | markup = split_list(markup, 30) 272 | new_markup = markup[page] 273 | new_markup.append(my_btns) 274 | else: 275 | new_markup = markup 276 | if path == JD_DIR: 277 | new_markup.append([Button.inline('取消', data='cancel')]) 278 | else: 279 | new_markup.append( 280 | [Button.inline('上级', data='updir'), Button.inline('取消', data='cancel')]) 281 | msg = await jdbot.edit_message(msg, '请做出您的选择:', buttons=new_markup) 282 | convdata = await conv.wait_event(press_event(sender)) 283 | res = bytes.decode(convdata.data) 284 | if res == 'cancel': 285 | msg = await jdbot.edit_message(msg, '对话已取消') 286 | conv.cancel() 287 | return None, None, None, None 288 | elif res == 'next': 289 | page = page + 1 290 | if page > len(markup) - 1: 291 | page = 0 292 | return path, msg, page, markup 293 | elif res == 'up': 294 | page = page - 1 295 | if page < 0: 296 | page = len(markup) - 1 297 | return path, msg, page, markup 298 | elif res == 'updir': 299 | path = '/'.join(path.split('/')[:-1]) 300 | if path == '': 301 | path = JD_DIR 302 | return path, msg, page, None 303 | elif os.path.isfile(f'{path}/{res}'): 304 | conv.cancel() 305 | logger.info(f'{path}/{res} 脚本即将在后台运行') 306 | msg = await jdbot.edit_message(msg, f'{res} 在后台运行成功') 307 | cmdtext = f'{TASK_CMD} {path}/{res} now' 308 | return None, None, None, f'CMD-->{cmdtext}' 309 | else: 310 | return f'{path}/{res}', msg, page, None 311 | except asyncio.exceptions.TimeoutError: 312 | msg = await jdbot.edit_message(msg, '选择已超时,对话已停止') 313 | return None, None, None, None 314 | except Exception as e: 315 | msg = await jdbot.edit_message(msg, f'something wrong,I\'m sorry\n{str(e)}') 316 | logger.error(f'something wrong,I\'m sorry\n{str(e)}') 317 | return None, None, None, None 318 | 319 | 320 | def mycron(lines): 321 | cronreg = re.compile(r'([0-9\-\*/,]{1,} ){4,5}([0-9\-\*/,]){1,}') 322 | return cronreg.search(lines).group() 323 | 324 | def add_cron_V4(cron): 325 | owninfo = '# mtask任务区域' 326 | with open(CRON_FILE, 'r', encoding='utf-8') as f: 327 | lines = f.readlines() 328 | for line in lines: 329 | if owninfo in line: 330 | i = lines.index(line) 331 | lines.insert(i+1, cron+'\n') 332 | break 333 | with open(CRON_FILE, 'w', encoding='utf-8') as f: 334 | f.write(''.join(lines)) 335 | 336 | 337 | async def add_cron(jdbot, conv, resp, filename, msg, sender, markup, path): 338 | try: 339 | if QL: 340 | crondata = { 341 | "name": f'{filename.split(".")[0]}', "command": f'task {path}/{filename}', "schedule": f'{mycron(resp)}'} 342 | else: 343 | crondata = f'{mycron(resp)} mtask {path}/{filename}' 344 | msg = await jdbot.edit_message(msg, f'已识别定时\n```{crondata}```\n是否需要修改', buttons=markup) 345 | except: 346 | if QL: 347 | crondata = { 348 | "name": f'{filename.split(".")[0]}', "command": f'task {path}/{filename}', "schedule": f'0 0 * * *'} 349 | else: 350 | crondata = f'0 0 * * * mtask {path}/{filename}' 351 | msg = await jdbot.edit_message(msg, f'未识别到定时,默认定时\n```{crondata}```\n是否需要修改', buttons=markup) 352 | convdata3 = await conv.wait_event(press_event(sender)) 353 | res3 = bytes.decode(convdata3.data) 354 | if res3 == 'yes': 355 | convmsg = await conv.send_message(f'```{crondata}```\n请输入您要修改内容,可以直接点击上方定时进行复制修改\n如果需要取消,请输入`cancel`或`取消`') 356 | crondata = await conv.get_response() 357 | crondata = crondata.raw_text 358 | if crondata == 'cancel' or crondata == '取消': 359 | conv.cancel() 360 | await jdbot.send_message(chat_id, '对话已取消') 361 | return 362 | await jdbot.delete_messages(chat_id, convmsg) 363 | await jdbot.delete_messages(chat_id, msg) 364 | if QL: 365 | with open(AUTH_FILE, 'r', encoding='utf-8') as f: 366 | auth = json.load(f) 367 | res = cron_manage_QL('add', json.loads( 368 | str(crondata).replace('\'', '\"')), auth['token']) 369 | if res['code'] == 200: 370 | await jdbot.send_message(chat_id, f'{filename}已保存到{path},并已尝试添加定时任务') 371 | else: 372 | await jdbot.send_message(chat_id, f'{filename}已保存到{path},定时任务添加失败,{res["data"]}') 373 | else: 374 | add_cron_V4(crondata) 375 | await jdbot.send_message(chat_id, f'{filename}已保存到{path},并已尝试添加定时任务') 376 | 377 | 378 | @Ver_Main 379 | def cron_manage_QL(fun, crondata, token): 380 | url = 'http://127.0.0.1:5600/api/crons' 381 | headers = { 382 | 'Authorization': f'Bearer {token}' 383 | } 384 | try: 385 | if fun == 'search': 386 | params = { 387 | 't': int(round(time.time() * 1000)), 388 | 'searchValue': crondata 389 | } 390 | res = requests.get(url, params=params, headers=headers).json() 391 | elif fun == 'add': 392 | data = { 393 | 'name': crondata['name'], 394 | 'command': crondata['command'], 395 | 'schedule': crondata['schedule'] 396 | } 397 | res = requests.post(url, data=data, headers=headers).json() 398 | elif fun == 'run': 399 | data = [crondata['id']] 400 | res = requests.put(f'{url}/run', json=data, headers=headers).json() 401 | elif fun == 'log': 402 | data = crondata['id'] 403 | res = requests.get(f'{url}/{data}/log', headers=headers).json() 404 | elif fun == 'edit': 405 | data = { 406 | 'name': crondata['name'], 407 | 'command': crondata['command'], 408 | 'schedule': crondata['schedule'], 409 | 'id': crondata['id'] 410 | } 411 | res = requests.put(url, json=data, headers=headers).json() 412 | elif fun == 'disable': 413 | data = [crondata['id']] 414 | res = requests.put(url+'/disable', json=data, 415 | headers=headers).json() 416 | elif fun == 'enable': 417 | data = [crondata['id']] 418 | res = requests.put(url+'/enable', json=data, 419 | headers=headers).json() 420 | elif fun == 'del': 421 | data = [crondata['id']] 422 | res = requests.delete(url, json=data, headers=headers).json() 423 | else: 424 | res = {'code': 400, 'data': '未知功能'} 425 | except Exception as e: 426 | res = {'code': 400, 'data': str(e)} 427 | finally: 428 | return res 429 | 430 | 431 | def cron_manage_V4(fun, crondata): 432 | file = f'{CONFIG_DIR}/crontab.list' 433 | with open(file, 'r', encoding='utf-8') as f: 434 | v4crons = f.readlines() 435 | try: 436 | if fun == 'search': 437 | res = {'code': 200, 'data': {}} 438 | for cron in v4crons: 439 | if str(crondata) in cron: 440 | res['data'][cron.split( 441 | 'task ')[-1].split(' ')[0].split('/')[-1].replace('\n', '')] = cron 442 | elif fun == 'add': 443 | v4crons.append(crondata) 444 | res = {'code': 200, 'data': 'success'} 445 | elif fun == 'run': 446 | cmd(f'jtask {crondata.split("task")[-1]}') 447 | res = {'code': 200, 'data': 'success'} 448 | elif fun == 'edit': 449 | ocron, ncron = crondata.split('-->') 450 | i = v4crons.index(ocron) 451 | v4crons.pop(i) 452 | v4crons.insert(i, ncron) 453 | res = {'code': 200, 'data': 'success'} 454 | elif fun == 'disable': 455 | i = v4crons.index(crondata) 456 | crondatal = list(crondata) 457 | crondatal.insert(0, '#') 458 | ncron = ''.join(crondatal) 459 | v4crons.pop(i) 460 | v4crons.insert(i, ncron) 461 | res = {'code': 200, 'data': 'success'} 462 | elif fun == 'enable': 463 | i = v4crons.index(crondata) 464 | ncron = crondata.replace('#', '') 465 | v4crons.pop(i) 466 | v4crons.insert(i, ncron) 467 | res = {'code': 200, 'data': 'success'} 468 | elif fun == 'del': 469 | i = v4crons.index(crondata) 470 | v4crons.pop(i) 471 | res = {'code': 200, 'data': 'success'} 472 | else: 473 | res = {'code': 400, 'data': '未知功能'} 474 | with open(file, 'w', encoding='utf-8') as f: 475 | f.write(''.join(v4crons)) 476 | except Exception as e: 477 | res = {'code': 400, 'data': str(e)} 478 | finally: 479 | return res 480 | 481 | 482 | def cron_manage(fun, crondata, token): 483 | if QL: 484 | res = cron_manage_QL(fun, crondata, token) 485 | else: 486 | res = cron_manage_V4(fun, crondata) 487 | return res 488 | 489 | 490 | @Ver_Main 491 | def env_manage_QL(fun, envdata, token): 492 | url = 'http://127.0.0.1:5600/api/envs' 493 | headers = { 494 | 'Authorization': f'Bearer {token}' 495 | } 496 | try: 497 | if fun == 'search': 498 | params = { 499 | 't': int(round(time.time() * 1000)), 500 | 'searchValue': envdata 501 | } 502 | res = requests.get(url, params=params, headers=headers).json() 503 | elif fun == 'add': 504 | data = { 505 | 'name': envdata['name'], 506 | 'value': envdata['value'], 507 | 'remarks': envdata['remarks'] if 'remarks' in envdata.keys() else '' 508 | } 509 | res = requests.post(url, json=[data], headers=headers).json() 510 | elif fun == 'edit': 511 | data = { 512 | 'name': envdata['name'], 513 | 'value': envdata['value'], 514 | 'id': envdata['id'], 515 | 'remarks': envdata['remarks'] if 'remarks' in envdata.keys() else '' 516 | } 517 | res = requests.put(url, json=data, headers=headers).json() 518 | elif fun == 'disable': 519 | data = [envdata['id']] 520 | res = requests.put(url+'/disable', json=data, 521 | headers=headers).json() 522 | elif fun == 'enable': 523 | data = [envdata['id']] 524 | res = requests.put(url+'/enable', json=data, 525 | headers=headers).json() 526 | elif fun == 'del': 527 | data = [envdata['id']] 528 | res = requests.delete(url, json=data, headers=headers).json() 529 | else: 530 | res = {'code': 400, 'data': '未知功能'} 531 | except Exception as e: 532 | res = {'code': 400, 'data': str(e)} 533 | finally: 534 | return res 535 | -------------------------------------------------------------------------------- /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.9.9", 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 | } 23 | -------------------------------------------------------------------------------- /jbot/font/aff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuMaiKaDe/bot/2bd554167295460efa5a514755bbc5e397621f48/jbot/font/aff.jpg -------------------------------------------------------------------------------- /jbot/font/jet.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuMaiKaDe/bot/2bd554167295460efa5a514755bbc5e397621f48/jbot/font/jet.ttf -------------------------------------------------------------------------------- /jbot/requirements.txt: -------------------------------------------------------------------------------- 1 | qrcode==6.1 2 | Telethon==1.25.4 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 8 | -------------------------------------------------------------------------------- /jbot/set.json: -------------------------------------------------------------------------------- 1 | { 2 | "版本": "0.1.1", 3 | "每页列数": 3, 4 | "中文": "True", 5 | "开启机器人转发": "False", 6 | "机器人黑名单": "", 7 | "机器人垃圾话": "", 8 | "下载代理": "https://ghproxy.com", 9 | "启动问候": "False", 10 | "启动问候语": "叮咚,我已成功启动,欢迎使用", 11 | "开启别名": "False", 12 | "开启user": "False", 13 | "命令别名": { 14 | "bean": "表格", 15 | "auth": "登录", 16 | "chart": "豆子", 17 | "cmd": "命令", 18 | "cron": "任务", 19 | "set": "设置", 20 | "edit": "编辑", 21 | "setname": "别名", 22 | "reply": "回复", 23 | "dl": "下载", 24 | "node": "运行脚本", 25 | "snode": "执行", 26 | "getfile": "文件", 27 | "log": "日志", 28 | "setshort": "快捷设定", 29 | "a": "快捷", 30 | "b": "键盘", 31 | "addcron":"新任务", 32 | "myenv":"变量", 33 | "addenv":"加量" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /jbot/user/login.py: -------------------------------------------------------------------------------- 1 | from telethon import TelegramClient, events 2 | import os 3 | import asyncio,qrcode 4 | from .. import API_HASH,API_ID,proxy,BOT,PROXY_START,PROXY_TYPE,connectionType,QR_IMG_FILE,jdbot,chat_id,CONFIG_DIR 5 | if BOT.get('proxy_user') and BOT['proxy_user'] != "代理的username,有则填写,无则不用动": 6 | proxy = { 7 | 'proxy_type': BOT['proxy_type'], 8 | 'addr': BOT['proxy_add'], 9 | 'port': BOT['proxy_port'], 10 | 'username': BOT['proxy_user'], 11 | 'password': BOT['proxy_password']} 12 | elif PROXY_TYPE == "MTProxy": 13 | proxy = (BOT['proxy_add'], BOT['proxy_port'], BOT['proxy_secret']) 14 | else: 15 | proxy = (BOT['proxy_type'], BOT['proxy_add'], BOT['proxy_port']) 16 | # 开启tg对话 17 | if PROXY_START and BOT.get('noretry') and BOT['noretry']: 18 | user = TelegramClient(f'{CONFIG_DIR}/user', API_ID, API_HASH, connection=connectionType, 19 | proxy=proxy) 20 | elif PROXY_START: 21 | user = TelegramClient(f'{CONFIG_DIR}/user', API_ID, API_HASH, connection=connectionType, 22 | proxy=proxy, connection_retries=None) 23 | elif BOT.get('noretry') and BOT['noretry']: 24 | user = TelegramClient(f'{CONFIG_DIR}/user', API_ID, API_HASH) 25 | else: 26 | user = TelegramClient(f'{CONFIG_DIR}/user', API_ID, API_HASH, 27 | connection_retries=None) 28 | 29 | def creat_qr(text): 30 | '''实例化QRCode生成qr对象''' 31 | qr = qrcode.QRCode( 32 | version=1, 33 | error_correction=qrcode.constants.ERROR_CORRECT_H, 34 | box_size=10, 35 | border=4 36 | ) 37 | qr.clear() 38 | # 传入数据 39 | qr.add_data(text) 40 | qr.make(fit=True) 41 | # 生成二维码 42 | img = qr.make_image() 43 | # 保存二维码 44 | img.save(QR_IMG_FILE) 45 | 46 | @jdbot.on(events.NewMessage(from_users=chat_id,pattern=r'^/userlogin$')) 47 | async def user_login(event): 48 | try: 49 | user.connect() 50 | qr_login = await user.qr_login() 51 | creat_qr(qr_login.url) 52 | await jdbot.send_message(chat_id,'请使用TG扫描二维码以开启USER',file=QR_IMG_FILE) 53 | await qr_login.wait(timeout=100) 54 | await jdbot.send_message(chat_id,'恭喜您已登录成功,请修改 /set 将开启user 改为True 并重启机器人 /reboot') 55 | except Exception as e: 56 | await jdbot.send_message(chat_id,'登录失败\n'+str(e)) 57 | 58 | @jdbot.on(events.NewMessage(from_users=chat_id,pattern=r'^/rmuser$')) 59 | async def user_login(event): 60 | try: 61 | await jdbot.send_message(chat_id,'即将删除user.session') 62 | os.remove(f'{CONFIG_DIR}/user.session') 63 | await jdbot.send_message(chat_id,'已经删除user.session\n请重新登录') 64 | except Exception as e: 65 | await jdbot.send_message(chat_id,'删除失败\n'+str(e)) 66 | 67 | 68 | @jdbot.on(events.NewMessage(from_users=chat_id,pattern=r'^/codelogin$')) 69 | async def user_login(event): 70 | try: 71 | await user.connect() 72 | async with jdbot.conversation(event.sender_id, timeout=100) as conv: 73 | msg = await conv.send_message('请输入手机号:\n例如:+8618888888888') 74 | phone = await conv.get_response() 75 | print(phone.raw_text) 76 | await user.send_code_request(phone.raw_text,force_sms=True) 77 | msg = await conv.send_message('请输入手机验证码:\n例如`code12345code`\n两侧code必须有') 78 | code = await conv.get_response() 79 | print(code.raw_text) 80 | await user.sign_in(phone.raw_text,code.raw_text.replace('code','')) 81 | await jdbot.send_message(chat_id,'恭喜您已登录成功,请修改 /set 将开启user 改为True 并重启机器人 /reboot') 82 | except asyncio.exceptions.TimeoutError: 83 | msg = await jdbot.edit_message(msg, '登录已超时,对话已停止') 84 | except Exception as e: 85 | await jdbot.send_message(chat_id,'登录失败\n 再重新登录\n'+str(e)) 86 | finally: 87 | await user.disconnect() 88 | -------------------------------------------------------------------------------- /jbot/user/start.py: -------------------------------------------------------------------------------- 1 | from .. import BOT_SET 2 | from .login import user 3 | if BOT_SET['开启user'].lower() == 'true': 4 | user.start() -------------------------------------------------------------------------------- /jbot/user/usermsg.py: -------------------------------------------------------------------------------- 1 | from .login import user 2 | from telethon import events 3 | import time 4 | 5 | 6 | @user.on(events.NewMessage(pattern=r'^re[ 0-9]*$', outgoing=True)) 7 | async def mycp(event): 8 | num = event.raw_text.split(' ') 9 | if isinstance(num, list) and len(num) == 2: 10 | num = int(num[-1]) 11 | else: 12 | num = 1 13 | reply = await event.get_reply_message() 14 | await event.delete() 15 | for _ in range(0, num): 16 | await reply.forward_to(int(event.chat_id)) 17 | 18 | 19 | @user.on(events.NewMessage(pattern=r'^id$', outgoing=True)) 20 | async def myid(event): 21 | reply = await event.get_reply_message() 22 | if reply: 23 | userid = reply.sender.id 24 | chat_id = event.chat_id 25 | await event.edit(f'当前聊天:`{chat_id}`\n你的user_id:`{userid}`') 26 | else: 27 | await event.delete() 28 | 29 | 30 | @user.on(events.NewMessage(pattern=r'^del[ 0-9]*$', outgoing=True)) 31 | async def selfprune(event): 32 | try: 33 | num = event.raw_text.split(' ') 34 | if isinstance(num, list) and len(num) == 2: 35 | count = int(num[-1]) 36 | else: 37 | count = 1 38 | await event.delete() 39 | count_buffer = 0 40 | async for message in user.iter_messages(event.chat_id, from_user="me"): 41 | if count_buffer == count: 42 | break 43 | await message.delete() 44 | count_buffer += 1 45 | notification = await user.send_message(event.chat_id, f'已删除{count_buffer}/{count}') 46 | time.sleep(.5) 47 | await notification.delete() 48 | except Exception as e: 49 | await user.send_message(event.chat_id, str(e)) 50 | -------------------------------------------------------------------------------- /jbot/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import importlib 3 | import os 4 | from . import logger 5 | 6 | 7 | def load_module(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(f"Bot加载-->{filename}-->完成") 19 | except Exception as e: 20 | logger.info(f"Bot加载失败-->{file}-->{str(e)}") 21 | continue 22 | 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | qrcode==6.1 2 | Telethon==1.25.4 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 8 | --------------------------------------------------------------------------------