├── conn ├── __init__.py ├── Plugin │ ├── __init__.py │ └── lottery.py ├── bots │ ├── __init__.py │ ├── json │ │ ├── __init__.py │ │ ├── channel_post.py │ │ └── message.py │ ├── tgbot.py │ ├── interaction.py │ └── getUpdate.py ├── ql │ ├── __init__.py │ ├── ql_timing.py │ ├── Timing.py │ └── ql.py ├── tools │ ├── __init__.py │ ├── Inspector.py │ ├── util.py │ ├── log.py │ ├── sql.py │ └── conn.py ├── Template │ ├── __init__.py │ ├── jdql.py │ ├── ancestors.py │ └── poadd.py ├── mission │ ├── __init__.py │ ├── sorting.py │ ├── core.py │ └── sundries.py └── Web │ ├── config.py │ ├── ws_send.py │ ├── __init__.py │ ├── but.py │ ├── htws.py │ └── index.py ├── repeat.sqlite ├── Web ├── static │ ├── img │ │ ├── gate.png │ │ └── left.png │ ├── css │ │ ├── global.css │ │ ├── reset.css │ │ └── login.css │ └── js │ │ ├── ws.js │ │ └── socket.io.min.js └── templates │ ├── aa.html │ ├── log.html │ ├── gi.html │ ├── login.html │ ├── yml.html │ └── module.html ├── docker ├── dockerfile ├── dockergi.sh ├── requirements.txt ├── detect.sh └── UpdateAll.sh ├── requirements.txt ├── SECURITY.md ├── LICENSE ├── fsbot.py ├── .github └── workflows │ └── codeql-analysis.yml └── README.md /conn/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/Plugin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/bots/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/ql/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/Template/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/bots/json/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conn/mission/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /repeat.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XgzK/QL_variable/HEAD/repeat.sqlite -------------------------------------------------------------------------------- /Web/static/img/gate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XgzK/QL_variable/HEAD/Web/static/img/gate.png -------------------------------------------------------------------------------- /Web/static/img/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XgzK/QL_variable/HEAD/Web/static/img/left.png -------------------------------------------------------------------------------- /conn/Web/config.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | # 给这个网站设置密钥 4 | SECRET_KEY = ''.join(str(uuid.uuid4()).split('-')) 5 | DEBUG = False 6 | SSL_DISABLE = False -------------------------------------------------------------------------------- /conn/Web/ws_send.py: -------------------------------------------------------------------------------- 1 | from . import connected_sids, socketio 2 | 3 | 4 | def send_message(data): 5 | for sid in connected_sids: 6 | socketio.emit('my_response', {'data': f'{data}'}, room=sid) -------------------------------------------------------------------------------- /docker/dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10.9-alpine3.17 2 | COPY . /root 3 | RUN apk add gcc g++ ccache nodejs && pip install -r /root/requirements.txt 4 | WORKDIR /val 5 | ENTRYPOINT ["sh", "/root/detect.sh"] -------------------------------------------------------------------------------- /Web/static/css/global.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background: #F3F2E2; 3 | } 4 | a{ 5 | text-decoration: none; 6 | } 7 | 8 | /* 内容居中 */ 9 | div{ 10 | /* background: #71a879; */ 11 | text-align: center; 12 | } 13 | .api{ 14 | width: 300px; 15 | } 16 | -------------------------------------------------------------------------------- /docker/dockergi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /root/ || exit 3 | # 制作更新指令 4 | cd /root/QL_variable/ || exit 5 | #pip3 install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt 6 | nuitka3 --follow-import-to=com --output-dir=out fsbot.py 7 | cp out/fsbot.bin /root/QL_variable/fsbot 8 | rm -rf out -------------------------------------------------------------------------------- /docker/requirements.txt: -------------------------------------------------------------------------------- 1 | # pip3 install -r requirements.txt 2 | 3 | # 其他库 4 | requests 5 | PyYAML > 1 6 | Flask > 1 7 | aiohttp >= 3.8.3 8 | Flask-APScheduler > 1 9 | colorlog >= 6.7.0 10 | httpx 11 | socksio >= 1.0.0 12 | Flask-Cors 13 | 14 | # ws需要插件 15 | Flask-SocketIO==5.2.0 16 | simple-websocket 17 | Werkzeug 18 | # 加密 19 | Nuitka 20 | ordered-set 21 | 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # pip3 install -r requirements.txt 2 | 3 | # 其他库 4 | requests 5 | requests[socks] 6 | pysocks 7 | ruamel.yaml == 0.17.21 8 | Flask > 1 9 | APScheduler > 1 10 | colorlog >= 6.7.0 11 | socksio >= 1.0.0 12 | Flask-Cors 13 | pyinstaller 14 | # ws需要插件 15 | simple-websocket 16 | Flask-SocketIO==5.2.0 17 | gevent-websocket 18 | gevent 19 | eventlet 20 | dnspython==2.2.1 -------------------------------------------------------------------------------- /conn/bots/json/channel_post.py: -------------------------------------------------------------------------------- 1 | from conn.mission.sorting import Sorting 2 | 3 | 4 | class Channel_post: 5 | 6 | def __init__(self): 7 | """ 8 | 频道帖子数据更新 9 | """ 10 | self.sorting = Sorting() 11 | 12 | def channel_main(self, channel_post): 13 | """ 14 | 处理频道消息进行分类 15 | :param channel_post: 16 | :return: 17 | """ 18 | self.sorting.dispatch(channel_post['text']) 19 | -------------------------------------------------------------------------------- /conn/Web/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import Flask 4 | from flask_cors import CORS 5 | from flask_socketio import SocketIO 6 | 7 | from . import config 8 | 9 | app = Flask(__name__, static_folder=os.getcwd().replace("\\", "/") + "/Web/static", static_url_path='/static', 10 | template_folder=os.getcwd().replace("\\", "/") + '/Web/templates') 11 | app.config.from_object(config) 12 | # 防止跨域 13 | CORS(app, resources=r'/*', supports_credentials=True) 14 | # ws 配置 15 | connected_sids = set() 16 | socketio = SocketIO(app, cors_allowed_origins='*') 17 | -------------------------------------------------------------------------------- /Web/templates/aa.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends 'module.html' %} 3 | {% block content %} 4 |

{% block title %}查看执行过的参数{% endblock %}

5 | 6 | 7 | 8 | 9 | 10 | {% for se_repeat in se_repeat %} 11 | 12 | 13 | 14 | 15 | {% endfor %} 16 |
重复的活动执行时间
{{ se_repeat[0] }}{{ se_repeat[1] }}
17 | {% endblock %} -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /Web/static/js/ws.js: -------------------------------------------------------------------------------- 1 | var socket = io("ws://" + document.domain + ":" + location.port); 2 | console.log('ws连接状态:' + socket.readyState); 3 | socket.on("my_response", function (msg) { 4 | console.log(msg) 5 | // $("#log").append("

" + msg.data + "

"); // 收消息 6 | var aa = "" 7 | if(msg.data.indexOf("[info]") !== -1){ 8 | aa = "

"+ msg.data +"

" 9 | }else if (msg.data.indexOf("[warning]") !== -1){ 10 | aa = "

"+ msg.data +"

" 11 | }else if (msg.data.indexOf("[debug]") !== -1){ 12 | aa = "

"+ msg.data +"

" 13 | }else if (msg.data.indexOf("[error]") !== -1){ 14 | aa = "

"+ msg.data +"

" 15 | } 16 | $("#log").append(aa) 17 | }); 18 | -------------------------------------------------------------------------------- /Web/templates/log.html: -------------------------------------------------------------------------------- 1 | {% extends 'module.html' %} 2 | {% block content %} 3 |

{% block title %}日志{% endblock %}

4 |

3.4 23/4/3 13/00

5 |
6 | {% for rz in rz %} 7 | {% if "[info]" in rz %} 8 |

{{ rz }}

9 | {% elif "[warning]" in rz %} 10 |

{{ rz }}

11 | {% elif "[debug]" in rz %} 12 |

{{ rz }}

13 | {% elif "[error]" in rz %} 14 |

{{ rz }}

15 | {% else %} 16 |

{{ rz }}

17 | {% endif %} 18 | {% endfor %} 19 |
20 | 21 | {% endblock %} -------------------------------------------------------------------------------- /conn/Template/jdql.py: -------------------------------------------------------------------------------- 1 | class JdQl: 2 | def __init__(self, gone: tuple): 3 | """ 4 | JdQl数据库的表 5 | :param gone: 6 | """ 7 | self.id = gone[0] 8 | self.jd_name = gone[1] 9 | self.jd_js = gone[2] 10 | self.jd_value1 = gone[3] 11 | self.jd_value2 = gone[4] 12 | self.jd_value3 = gone[5] 13 | self.jd_url = gone[6] 14 | self.jd_re = gone[7] 15 | self.jd_type = gone[8] 16 | self.partition = gone[9] 17 | self.interval = gone[10] 18 | self.Change = [gone[3], gone[4], gone[5]] 19 | 20 | def toString(self): 21 | """ 22 | 打印输出 23 | :return: 24 | """ 25 | print(f"ID: {self.id}\t任务名称: {self.jd_name}\t脚本名称: {self.jd_js}\n" 26 | f"参数一: {self.jd_value1}\t参数二: {self.jd_value2}\t参数三: {self.jd_value3}\t" 27 | f"首次匹配链接: {self.jd_url}\t最终获取关键字: {self.jd_re}\t支持类型: {self.jd_type}\n" 28 | f"分割关键字: {self.partition}\t延迟秒数: {self.interval}") 29 | -------------------------------------------------------------------------------- /conn/tools/Inspector.py: -------------------------------------------------------------------------------- 1 | """ 2 | 督查用来检测环境是否正常 3 | """ 4 | import os 5 | import re 6 | 7 | import requests 8 | 9 | from conn.tools.conn import ConnYml 10 | from conn.tools.sql import Sql 11 | 12 | 13 | class Check: 14 | def __init__(self): 15 | self.conn = ConnYml() 16 | 17 | def cpath(self): 18 | """ 19 | 检测环境是否存在,不存在返回-1 20 | :return: -1 or 0 21 | """ 22 | # 检测目录是否存在 23 | pa = re.findall('(.*?)/', self.conn.read_yaml()['json']) 24 | if pa: 25 | if not os.path.exists(pa[0]): 26 | os.makedirs(pa[0]) 27 | 28 | def sql(self) -> int: 29 | """ 30 | 清空数据库重新添加新的 31 | :return: 32 | """ 33 | try: 34 | jd = requests.get("https://xgzq.tk/library/jd.sql", timeout=60) 35 | if jd.status_code == 200 and len(jd.text) > 100: 36 | Sql().exe_sql(jd.text) 37 | return 0 38 | return -1 39 | except Exception as e: 40 | return -1 41 | -------------------------------------------------------------------------------- /Web/templates/gi.html: -------------------------------------------------------------------------------- 1 | {% extends 'module.html' %} 2 | {% block content %} 3 |

{% block title %}菜单{% endblock %}

4 | {# 禁用重复任务#} 5 |
6 | 12 | 13 |
14 |
15 | 21 | 22 |
23 | {% endblock %} -------------------------------------------------------------------------------- /Web/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 登录界面 11 | 12 | 13 | 14 |
15 |
16 |
17 |

登 录

18 | {% for message in get_flashed_messages() %} 19 |
{{ message }}
20 | {% endfor %} 21 |
22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /docker/detect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | get_arch='arch' 3 | # 容器保活 4 | # 判断文件是否存在不存在则拉取 5 | if [ ! -f "/val/repeat.sqlite" ]; then 6 | echo "没有检测到文件存在拉取新项目" 7 | python3 -m pip install --upgrade pip 8 | sh /root/UpdateAll.sh 9 | fi 10 | while true 11 | do 12 | # 停止返回0 正常返回1 13 | # shellcheck disable=SC2009 14 | # shellcheck disable=SC2126 15 | stillRunning=$(ps -ef |grep fsbot |grep -v "grep" |wc -l) 16 | if [ "$stillRunning" ]; then 17 | echo 程序死亡开始执行 18 | # 判断文件是否存在不存在则拉取 19 | if [ ! -f "/val/repeat.sqlite" ]; then 20 | sh /root/UpdateAll.sh 21 | fi 22 | # shellcheck disable=SC2046 23 | if [[ $get_arch =~ "x86_64" ]];then 24 | echo 检测到系统是 $get_arch 执行 fsbot 25 | kill -9 $(netstat -nlp | grep fsbot | awk '{print $7}' | awk -F"/" '{ print $1 }') 26 | cd /val && ./fsbot 27 | else 28 | echo 检测到系统是 $get_arch 执行 fsbot.py 29 | kill -9 $(netstat -nlp | grep fsbot.py | awk '{print $7}' | awk -F"/" '{ print $1 }') 30 | cd /val && python fsbot.py 31 | fi 32 | fi 33 | done -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 XGZ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Web/static/css/reset.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/) 3 | * http://cssreset.com 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; 24 | } 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, hgroup, menu, nav, section { 28 | display: block; 29 | } 30 | body { 31 | line-height: 1; 32 | } 33 | ol, ul { 34 | list-style: none; 35 | } 36 | blockquote, q { 37 | quotes: none; 38 | } 39 | blockquote:before, blockquote:after, 40 | q:before, q:after { 41 | content: ''; 42 | content: none; 43 | } 44 | table { 45 | border-collapse: collapse; 46 | border-spacing: 0; 47 | } -------------------------------------------------------------------------------- /conn/tools/util.py: -------------------------------------------------------------------------------- 1 | #: Contains all media content types. 2 | content_type_media = [ 3 | 'text', 'audio', 'document', 'animation', 'game', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 4 | 'location', 'venue', 'dice', 'invoice', 'successful_payment', 'connected_website', 'poll', 'passport_data', 5 | 'web_app_data', 6 | ] 7 | 8 | #: Contains all service content types such as `User joined the group`. 9 | content_type_service = [ 10 | 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', 11 | 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 12 | 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', 13 | 'video_chat_participants_invited', 'message_auto_delete_timer_changed', 'forum_topic_created', 'forum_topic_closed', 14 | 'forum_topic_reopened', 15 | ] 16 | 17 | #: All update types, should be used for allowed_updates parameter in polling. 18 | update_types = [ 19 | "message", "edited_message", "channel_post", "edited_channel_post", "inline_query", "chosen_inline_result", 20 | "callback_query", "shipping_query", "pre_checkout_query", "poll", "poll_answer", "my_chat_member", "chat_member", 21 | "chat_join_request", 22 | ] -------------------------------------------------------------------------------- /docker/UpdateAll.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 部分更新就是保留配置文件的更新 3 | # shellcheck disable=SC2034 4 | get_arch='arch' 5 | # 进入/root目录 6 | cd /root/ || exit 7 | # 下载 8 | wget https://xgzq.tk/library/qlva.tgz 9 | # 解压 10 | tar -zxvf qlva.tgz 11 | # 如果是1保留配置文件 12 | if test "$1" = "1"; then 13 | echo "保留配置文件更新项目" 14 | # 把配置文件复制出来 15 | cp -f /val/conn.yml / 16 | cp -f /val/repeat.sqlite / 17 | rm -rf /val/* 18 | pip3 install -i https://mirrors.aliyun.com/pypi/simple/ -r /root/QL_variable/requirements.txt 19 | if [[ $get_arch =~ "x86_64" ]];then 20 | source ./dockergi.sh 21 | fi 22 | # 移动文件 23 | cp -rf /root/QL_variable/* /val 24 | # 把配置文件移动到项目目录 25 | mv -f /conn.yml /val 26 | mv -f /repeat.sqlite /val 27 | else 28 | echo "删除配置文件更新项目" 29 | # 删除ip下所有文件 30 | rm -rf /val/* 31 | pip3 install -i https://mirrors.aliyun.com/pypi/simple/ -r /root/QL_variable/requirements.txt 32 | if [[ $get_arch =~ "x86_64" ]];then 33 | source ./dockergi.sh 34 | fi 35 | # 移动文件 36 | cp -rf /root/QL_variable/* /val 37 | fi 38 | # 判断文件是否存在存在则执行 39 | if [ -f "/val/test.sh" ]; then 40 | sh /val/test.sh 41 | else 42 | echo "没有检测到文件不需要额外执行其他任务" 43 | fi 44 | # 删除压缩包, 删除文件夹 45 | rm -rf /root/qlva.tgz 46 | rm -rf /root/QL_variable 47 | # shellcheck disable=SC2046 48 | if [[ $get_arch =~ "x86_64" ]];then 49 | kill -9 $(netstat -nlp | grep fsbot | awk '{print $7}' | awk -F"/" '{ print $1 }') 50 | else 51 | kill -9 $(netstat -nlp | grep fsbot.py | awk '{print $7}' | awk -F"/" '{ print $1 }') 52 | fi -------------------------------------------------------------------------------- /conn/Web/but.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from flask import render_template, Blueprint, request, flash, redirect, url_for 4 | 5 | from conn.Template.poadd import upgrade, to_stop 6 | from conn.tools.log import LoggerClass 7 | from conn.tools.sql import Sql 8 | 9 | apg = Blueprint('but', __name__) 10 | conn = Sql() 11 | logger = LoggerClass() 12 | 13 | 14 | @apg.route('/log', methods=['GET']) 15 | def log(): 16 | """ 17 | 日志 18 | :return: 19 | """ 20 | return render_template('log.html', rz=logger.read_log()) 21 | 22 | 23 | @apg.route('/repeat', methods=['GET']) 24 | def repeat(): 25 | """ 26 | 重复的数据库值 27 | :return: 28 | """ 29 | se_repeat = conn.selectAll(table=conn.surface[1]) 30 | return render_template('aa.html', se_repeat=se_repeat) 31 | 32 | 33 | @apg.route("/gi", methods=['POST']) 34 | def gi(): 35 | """ 36 | 更新软件 37 | :return: 38 | """ 39 | if request.method == 'POST': 40 | git = request.values.get('gi') 41 | t1 = threading.Thread(target=upgrade, args=(int(git),)) 42 | t1.start() 43 | # 提示语 44 | flash('20秒后刷新浏览器') 45 | # 重定向到首页 46 | return redirect(url_for('apg.under')) 47 | 48 | 49 | @apg.route('/under') 50 | def under(): 51 | """ 52 | 未分类功能 53 | :return: 54 | """ 55 | return render_template('gi.html') 56 | 57 | 58 | @apg.route("/pare", methods=['GET']) 59 | def pare(): 60 | """ 61 | 禁用活动任务 62 | :return: 63 | """ 64 | res = to_stop(request.values.get('sun')) 65 | flash(res) 66 | return redirect(url_for('apg.under')) 67 | -------------------------------------------------------------------------------- /conn/Web/htws.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from flask import request, session, flash, redirect, url_for 4 | 5 | from . import app, index, but, connected_sids, socketio 6 | from ..tools.sql import Sql 7 | 8 | app.register_blueprint(index.ind, name='ind', url_prefix='/') 9 | app.register_blueprint(but.apg, name='apg', url_prefix='/config') 10 | conn = Sql() 11 | 12 | 13 | @app.before_request 14 | def befores(): 15 | """ 16 | 每次请求都执行 17 | :return: 18 | """ 19 | ht_ip = request.environ 20 | # 检查的IP不是127.0.0.1 和session为空 不等于 登录 21 | if ht_ip['REMOTE_ADDR'] != '127.0.0.1' and ht_ip['PATH_INFO'] != '/login' and len( 22 | re.findall('^(/static/[jscimg]+)/', ht_ip['PATH_INFO'])) == 0: 23 | username = session.get('username') 24 | user_ck = conn.selectTopone(table=conn.surface[4], where=f"username='{username}'") 25 | # 检测有没有这个用户存在 26 | if not user_ck: 27 | new_user = conn.selectAll(table=conn.surface[4]) 28 | # 如果返回的有用户存在 29 | if not new_user: 30 | flash("没有检测到账户存在请设置密码和账户") 31 | return redirect(url_for('ind.login')) 32 | # else: 33 | # # 发生未知事件 34 | # return redirect(url_for('ind.login')) 35 | 36 | 37 | @socketio.on('connect') 38 | def on_connect(): 39 | """ 40 | 链接来了 41 | :return: 42 | """ 43 | connected_sids.add(request.sid) 44 | # print(f'{request.sid} 已连接') 45 | 46 | 47 | @socketio.on('disconnect') 48 | def on_disconnect(): 49 | """ 50 | 断开连接 51 | :return: 52 | """ 53 | connected_sids.remove(request.sid) 54 | # print(f'{request.sid} 已断开') 55 | -------------------------------------------------------------------------------- /Web/templates/yml.html: -------------------------------------------------------------------------------- 1 | {% extends 'module.html' %} 2 | {% block content %} 3 |

{% block title %}配置文件控制台{% endblock %}

4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |

反代和代理只能使用一个,如果代理和反代都填写将优先使用代理

14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 |
29 |
30 | {% endblock %} -------------------------------------------------------------------------------- /fsbot.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import threading 4 | 5 | from apscheduler.schedulers.background import BackgroundScheduler 6 | 7 | from conn.Template.ancestors import Father 8 | from conn.Web.htws import app, socketio 9 | from conn.bots.tgbot import Filter 10 | from conn.mission.core import Main_core 11 | from conn.ql.Timing import Timing 12 | from conn.tools.Inspector import Check 13 | 14 | 15 | class RunMain(Father): 16 | 17 | def __init__(self): 18 | super().__init__() 19 | self.yml.creat_yml() 20 | self.chech = Check() 21 | self.timing = Timing() 22 | self.filter = Filter() 23 | os.environ['marking_time'] = '0' 24 | self.core = Main_core() 25 | 26 | def ti_ck(self): 27 | """ 28 | 定时清空数据库 29 | :return: 30 | """ 31 | self.log_object.read_log() 32 | self.timing.clear_list() 33 | 34 | def timing_ck(self): 35 | """ 36 | 设置每半个月获取一次新的ck,青龙作者是的是一个月保质期,不过这里设置为半个月 37 | :return: 0 or -1 38 | """ 39 | self.timing.check_ct() 40 | 41 | def run_web(self): 42 | socketio.run(app, 43 | debug=False, 44 | log_output=False, 45 | host='0.0.0.0', 46 | port=5008) 47 | 48 | def bot_main(self): 49 | self.chech.cpath() 50 | scheduler = BackgroundScheduler() 51 | # 使用多线程防止任务阻塞 52 | t1 = threading.Thread(target=self.run_web) 53 | t2 = threading.Thread(target=self.core.main_while) 54 | 55 | t1.start() 56 | 57 | scheduler.add_job(self.ti_ck, 'interval', hours=12) 58 | scheduler.add_job(self.timing_ck, 'interval', days=15) 59 | scheduler.start() 60 | 61 | self.timing_ck() 62 | self.ti_ck() 63 | t2.start() 64 | self.log_write("云端数据库同步成功") if self.chech.sql() == 0 else self.log_write("云端数据库同步失败") 65 | asyncio.run(self.filter.main_bots()) 66 | 67 | 68 | if __name__ == '__main__': 69 | RunMain().bot_main() 70 | -------------------------------------------------------------------------------- /conn/Template/ancestors.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from ..tools.log import LoggerClass 4 | from ..tools.conn import ConnYml 5 | 6 | 7 | class Father: 8 | AdReg = { 9 | # value记录值,初始化随便填,目前只适配了高级配置的key 10 | } 11 | 12 | def __init__(self): 13 | # 配置文件有关 14 | self.yml = ConnYml() 15 | self.read = self.yml.read_yaml 16 | self.revise = self.yml.revise_yml 17 | self.delete = self.yml.delete_first_lines 18 | self.log_object = LoggerClass() 19 | self.log_write = self.log_object.write_log 20 | # 用于记录是否需要更新 21 | self.Marking_time = 0 22 | 23 | def flash_Config(self): 24 | """ 25 | 把配置文件内容读取到AdReg中 26 | :return: 27 | """ 28 | conf = self.read() 29 | for i in conf.keys(): 30 | self.AdReg[i] = conf.get(i) 31 | 32 | def revise_Config(self, Key: str, Value): 33 | """ 34 | 修改配置文件覆盖原始值形式,动态录入 35 | :param Key: 36 | :param Value: 37 | :return: -1:修改的值和原先值类型不符,适用于bool-bool的验证 38 | """ 39 | # 检测KEY在不在配置文件中 40 | Key = Key.split('.') 41 | if Key[0] in self.AdReg.keys(): 42 | config = self.read() 43 | if len(Key) == 2: 44 | config[Key[0]][Key[1]] = Value 45 | else: 46 | config[Key[0]] = Value 47 | # 动态录入 48 | if type(Value) is bool: 49 | if not type(self.AdReg[Key[0]]) is bool: 50 | self.log_write(f"提交的 {Key[0]} : {Value} 值类型不符") 51 | return -1 52 | if len(Key) == 2: 53 | config[Key[0]][Key[1]] = Value 54 | else: 55 | config[Key[0]] = Value 56 | self.revise(config) 57 | else: 58 | self.log_write(f"提交的 {Key[0]} : {Value} 不在配置文件中") 59 | 60 | def marking_time(self): 61 | # 这一点是为了检测标记是否需要更新 62 | if self.Marking_time < int(os.environ.get('marking_time', 0)): 63 | self.flash_Config() 64 | self.Marking_time = int(os.environ.get('marking_time', 0)) 65 | print("配置文件发生改变重新获取新的内容") 66 | return True 67 | return Father 68 | -------------------------------------------------------------------------------- /conn/Web/index.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, request, redirect, url_for, flash, session, Blueprint 2 | 3 | from conn.Template.poadd import ym_change 4 | from conn.tools.conn import ConnYml 5 | from conn.tools.sql import Sql 6 | 7 | conn = Sql() 8 | connyml = ConnYml() 9 | ind = Blueprint('index', __name__) 10 | 11 | 12 | @ind.route("/", methods=['GET', 'POST']) 13 | def index(): 14 | """ 15 | 表单 16 | :return: 17 | """ 18 | if request.method == 'POST': 19 | books = request.values.getlist('books[]') 20 | # 把表单传递给方法添加到conn.yml 21 | q = ym_change(books) 22 | flash(q[1]) 23 | return redirect(url_for('ind.index')) 24 | else: 25 | yml = connyml.read_yaml() 26 | if yml['deduplication'] == 1: 27 | return render_template('yml.html', chec='') 28 | else: 29 | return render_template('yml.html', chec='checked') 30 | 31 | 32 | @ind.route("/login", methods=["GET", "POST"]) 33 | def login(): 34 | """ 35 | 登录 36 | :return: 37 | """ 38 | # 清空session 39 | session.pop('username', None) 40 | if request.method == 'POST': 41 | username = request.values.get("username") 42 | password = request.values.get("password") 43 | # 如果有空 44 | if not all([username, password]): 45 | flash("密码或账户为空") 46 | return redirect(url_for('ind.login')) 47 | else: 48 | new_user = conn.selectAll(table=conn.surface[4]) 49 | # 查询是否有密码和账户存在,如果没有让用户注册 50 | if not new_user: 51 | # 插入用户名和密码 52 | conn.insert(table=conn.surface[4], username=f"{username}", password=f"{password}") 53 | session['username'] = username 54 | session.permanent = True 55 | return redirect(url_for('ind.index')) 56 | # 如果验证成功,将用户名加密并保存在cookie中 57 | user_ck = conn.selectTopone(table=conn.surface[4], where=f"username='{username}'") 58 | if not user_ck: 59 | # 没有查询到用户用户信息 60 | flash("输入的密码或账户不正确") 61 | return redirect(url_for('ind.login')) 62 | # 判断用户是否存在 63 | elif username == user_ck[0] and password == user_ck[1]: 64 | session['username'] = username 65 | session.permanent = True 66 | return redirect(url_for('ind.index')) 67 | else: 68 | flash("输入的密码或账户不正确") 69 | return redirect(url_for('ind.login')) 70 | else: 71 | return render_template('login.html') 72 | -------------------------------------------------------------------------------- /Web/templates/module.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% block title %} {% endblock %} 12 | 13 | 14 | 40 |
41 | {# 取出来flash的内容 #} 42 | {% for message in get_flashed_messages() %} 43 |
{{ message }}
44 | {% endfor %} 45 | 46 | {% block content %} {% endblock %} 47 |
48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Web/static/css/login.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | html { 8 | font-size: 10px; 9 | } 10 | 11 | html::before { 12 | content: ''; 13 | width: 100%; 14 | height: 100%; 15 | position: fixed; 16 | z-index: -1; 17 | background: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%) no-repeat; 18 | } 19 | 20 | ::selection { 21 | color: #fff; 22 | background-color: rgb(144,129,241); 23 | 24 | } 25 | 26 | .box { 27 | display: flex; 28 | overflow: hidden; 29 | width: 90rem; 30 | height: 55rem; 31 | background-color: rgba(255,255,255,60%); 32 | border-radius: 1.5rem; 33 | margin: 10% auto; 34 | box-shadow: 0 0 1rem 0.2rem rgb(0 0 0 / 10%); 35 | } 36 | 37 | .box .left { 38 | position: relative; 39 | width: 35%; 40 | height: 100%; 41 | background-color:skyblue; 42 | } 43 | 44 | .box .left::before { 45 | content: ''; 46 | position: absolute; 47 | width: 100%; 48 | height: 100%; 49 | background-image: url(../img/left.png); 50 | background-size: cover; 51 | opacity: 80%; 52 | } 53 | 54 | .box .right { 55 | display: flex; 56 | width: 65%; 57 | flex-direction: column; 58 | align-items: center; 59 | } 60 | 61 | .box .right h4{ 62 | color: rgb(144,129,241); 63 | font-size: 3rem; 64 | margin-top: 5rem; 65 | } 66 | 67 | .box .right form { 68 | display: flex; 69 | flex-wrap: wrap; 70 | justify-content: center; 71 | } 72 | 73 | .box .right form .acc { 74 | outline: none; 75 | width: 80%; 76 | height: 5rem; 77 | font-size: 1.6rem; 78 | margin-top: 5rem; 79 | padding: 1rem 0 0 1.6rem; 80 | border: none; 81 | border-bottom:1px solid rgb(144,129,241) ; 82 | color: rgb(144,129,241); 83 | background-color: rgba(0,0,0,0); 84 | } 85 | 86 | .right form .acc:focus { 87 | outline: none; 88 | color: rgb(144,129,241); 89 | padding: 1rem 0 0 1.6rem; 90 | } 91 | 92 | .right .submit { 93 | width: 60%; 94 | height: 5rem; 95 | color: #f6f6f6; 96 | background-image: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%); 97 | font-size: 1.4rem; 98 | border: none; 99 | border-radius: 0.5rem; 100 | margin: 6rem 0 0 50%; 101 | transform: translateX(-50%); 102 | } 103 | 104 | .right .submit:hover { 105 | box-shadow: 0 0 2rem -0.5rem rgb(0 0 0 / 15%); 106 | } 107 | 108 | 109 | @media screen and (max-width: 800px){ 110 | .left{ 111 | display: none; 112 | } 113 | } 114 | @media screen and (max-width: 600px){ 115 | .left{ 116 | display: none; 117 | } 118 | .box .right{ 119 | width: 50%; 120 | } 121 | } -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '34 15 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /conn/bots/json/message.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from conn.Template.ancestors import Father 4 | from conn.bots.interaction import Interaction 5 | from conn.mission.sorting import Sorting 6 | 7 | 8 | class Message(Father): 9 | """ 10 | 接收到的消息 11 | """ 12 | 13 | def __init__(self): 14 | super().__init__() 15 | self.sorting = Sorting() 16 | self.inter = Interaction() 17 | 18 | def filter_message(self, message): 19 | """ 20 | 获取发送的消息 21 | :param message: 22 | :return: 23 | """ 24 | # 判断消息来源 25 | if message['chat']['type'] == 'private': 26 | if not 'text' in message: 27 | return 28 | # 私聊消息 29 | 30 | self.log_write( 31 | f"{datetime.datetime.now()} ID: {message['chat']['id']} {'用户: @' + message['chat']['username'] if 'username' in message['chat'] else ''} 用户名: {message['chat']['first_name'] if 'first_name' in message['chat'] else ''} {message['chat']['last_name'] if 'last_name' in message['chat'] else ''} 发送内容: {message['text']}") 32 | if message['text'].startswith('/'): 33 | if message['chat']['id'] == self.AdReg.get('Administrator'): 34 | self.inter.main_white(message['text']) 35 | return 36 | elif "forward_from_chat" in message: 37 | # 转发消息 38 | self.forward_from_chat(message) 39 | return 40 | else: 41 | self.sorting.dispatch(message['text']) 42 | 43 | elif 'text' in message: 44 | if message['text'].startswith('/'): 45 | if message['text'] == "/id": 46 | self.group_id(message) 47 | else: 48 | self.sorting.dispatch(message['text']) 49 | # 群聊用户 50 | return 51 | 52 | def forward_from_chat(self, message): 53 | """ 54 | 处理转发的消息 55 | :param message: 56 | :return: 57 | """ 58 | tx = f"{'你的个人ID是: ' + str(message['from']['id'])}\n" \ 59 | f"{'用户名: ' + message['from']['first_name'] if 'first_name' in message['from'] else ''} {message['from']['last_name'] if 'last_name' in message['from'] else ' '}\n" \ 60 | f"个人链接: @{message['from']['username'] if 'username' in message['from'] else ''}\n" \ 61 | f"{'转发频道名称: ' + message['forward_from_chat']['title'] if 'title' in message['forward_from_chat'] else ''}\n" \ 62 | f"{'转发频道ID: ' + str(message['forward_from_chat']['id']) if 'id' in message['forward_from_chat'] else ''}\n" \ 63 | f"{'频道链接: @' + message['forward_from_chat']['username'] if 'username' in message['forward_from_chat'] else ''}" 64 | self.inter.for_message(tx, False) 65 | 66 | def group_id(self, message): 67 | """ 68 | 获取群聊ID 69 | :param message: 70 | :return: 71 | """ 72 | tx = f"群组名称: {message['chat']['title']}\n" \ 73 | f"群组ID: {message['chat']['id']}" 74 | self.inter.for_message(tx, False, message['chat']['id']) 75 | -------------------------------------------------------------------------------- /conn/bots/tgbot.py: -------------------------------------------------------------------------------- 1 | """ 2 | TG机器人 3 | """ 4 | import time 5 | 6 | from conn.bots.getUpdate import GetUpdate 7 | from conn.bots.json.channel_post import Channel_post 8 | from conn.bots.json.message import Message 9 | from conn.tools import util 10 | 11 | 12 | class Filter: 13 | 14 | def __init__(self): 15 | self.getdata = GetUpdate() 16 | self.message = Message() 17 | self.channel = Channel_post() 18 | self.filtration = {"message", "channel_post"} 19 | 20 | def _points(self, tg_list: list, tf: bool = False): 21 | """ 22 | 对TG消息进行分类处理 23 | :param tg_list: 接收到的TG小时数组 24 | :return: 25 | """ 26 | for i in tg_list: 27 | if tf: 28 | tgkey = list(self.filtration & i.keys()) 29 | if tgkey and i[tgkey[0]]['date'] < int(time.time()) - 3600: 30 | continue 31 | if type(i) == int: 32 | continue 33 | elif type(i) == dict: 34 | if 'message' in i: 35 | # print('文本消息 ', i) 36 | self.message.filter_message(i['message']) 37 | continue 38 | elif 'channel_post' in i: 39 | # print('频道帖子 ', i) 40 | self.channel.channel_main(i['channel_post']) 41 | # elif 'chat_member' in i: 42 | # print('加入请求', i) 43 | # return chatmember.filter_chatmessage(i['chat_member'] 44 | # elif 'edited_message' in i: 45 | # print('消息被编辑 ', i) 46 | # elif 'edited_channel_post' in i: 47 | # print('频道帖子被编辑 ', i) 48 | # elif 'inline_query' in i: 49 | # print('内联查询 ', i) 50 | # elif 'chosen_inline_result' in i: 51 | # print('选择的内联结果 ', i) 52 | # elif 'callback_query' in i: 53 | # print('回调查询 ', i) 54 | # elif 'shipping_query' in i: 55 | # print('运费查询 ', i) 56 | # elif 'pre_checkout_query' in i: 57 | # print('结帐前查询 ', i) 58 | # elif 'poll' in i: 59 | # print('投票 ', i) 60 | # elif 'poll_answer' in i: 61 | # print('投票答案 ', i) 62 | # elif 'my_chat_member' in i: 63 | # print('我的聊天会员 ', i) 64 | # elif 'chat_member' in i: 65 | # print('聊天会员 ', i) 66 | # elif 'chat_join_request' in i: 67 | # print('聊天加入请求 ', i) 68 | # else: 69 | # print(i) 70 | 71 | async def main_bots(self): 72 | """ 73 | 循环请求TG获取消息 74 | :return: 75 | """ 76 | tf = False 77 | while not tf: 78 | self.getdata.marking_time() 79 | if self.getdata.AdReg.get('Token'): 80 | self.getdata.Update() 81 | tf = True 82 | else: 83 | print('没有填写到机器人Token') 84 | time.sleep(15) 85 | while True: 86 | self.getdata.marking_time() 87 | tg_list = await self.getdata.get_long_link(allowed_updates=util.update_types, timeout=100) 88 | self._points(tg_list, tf) 89 | if tf: 90 | tf = False 91 | -------------------------------------------------------------------------------- /conn/Template/poadd.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import time 4 | 5 | from conn.Template.ancestors import Father 6 | from conn.ql.ql import QL 7 | from conn.tools.sql import Sql 8 | 9 | conn = Sql() 10 | ql = QL() 11 | father = Father() 12 | 13 | 14 | def ym_change(li: list): 15 | """ 16 | 根据用户表单提交的值往conn.yml添加内容 17 | :param li: 表单返回的数组 18 | :return: 19 | """ 20 | father.marking_time() 21 | st = '' 22 | # 任务是否去重复 23 | if len(li) == 4: 24 | father.revise_Config("deduplication", 1) 25 | st += '任务不去重复' 26 | elif len(li) == 5: 27 | father.revise_Config("deduplication", 0) 28 | st += '任务去重复' 29 | if li[0] != '': 30 | if str(li[0]).isdigit(): 31 | father.revise_Config("Administrator", int(li[0])) 32 | st += f' 你设置机器人的管理员是: {li[0]} ' 33 | else: 34 | return [0, 35 | "机器人交互的ID必须是数字,如果不知道请 https://t.me/InteIJ 群回复/id@KinhRoBot 查看自己ID,所有内容请重新填写"] 36 | if li[1] != '': 37 | father.revise_Config("Token", li[1]) 38 | st += f' 机器人密钥添加成功' 39 | if li[2] != '': 40 | # 由于使用的是requests库对socks5改成socks5h 41 | if li[2].startswith('socks5://'): 42 | li[2] = li[2].replace('socks5://', 'socks5h://') 43 | father.revise_Config("Proxy.Proxy", li[2]) 44 | st += f'代理添加成功' 45 | if li[3] != '': 46 | tg_url = re.findall('^(http.*)', li[3]) 47 | if tg_url: 48 | father.revise_Config("Proxy.TG_API_HOST", tg_url[0]) 49 | st += f'反代添加成功' 50 | # 把时间更新 51 | os.environ['marking_time'] = str(int(time.time())) 52 | return [0, st] 53 | 54 | 55 | def upgrade(sun: int): 56 | """ 57 | 根据sun的值不同采用不同的方式升级 58 | :param sun: 0 or 1 59 | :return: 60 | """ 61 | time.sleep(3) 62 | if int(sun) == 0: 63 | print("不保留配置更新") 64 | os.system("sh /root/UpdateAll.sh") 65 | elif int(sun) == 1: 66 | print("保留配置更新") 67 | os.system("sh /root/UpdateAll.sh 1") 68 | 69 | 70 | def to_stop(sun: int): 71 | """ 72 | 禁止活动任务脚本 73 | 根据sun的值不同采用不同的方式禁用 74 | :param sun: 1 禁用所有 别的禁用活动 75 | :return: 76 | """ 77 | try: 78 | st = "" 79 | lis = set() 80 | lines = conn.selectAll(table=conn.surface[0]) 81 | value1 = conn.selectAll(table=conn.surface[3], where=f"state=0") 82 | if not value1: 83 | return f'没有可正常执行的青龙' 84 | # 循环所有青龙 85 | for ql_tk in value1: 86 | js = father.read(ql_tk[5]) 87 | st += ql_tk[0] 88 | if sun == 0: 89 | for i in js.keys(): 90 | if js[i] == 1: 91 | continue 92 | for j in range(len(js[i]) - 1): 93 | lis.add(js[i][list(js[i])[j]]['id']) 94 | # 循环数据库 95 | for i in lines: 96 | # 跳过不在json文件的脚本 97 | if not (i[2] in js): 98 | continue 99 | for j in js[i[2]].keys(): 100 | if js[i[2]][j]['isDisabled'] == 0: 101 | lis.add(js[i[2]][j]['id']) 102 | st += f": 禁止任务成功禁用ID{list(lis)}\n" if ql.disable(list(lis), 103 | ql_tk) == 0 else f": 禁止任务失败或没有可禁用任务\n" 104 | return st 105 | except Exception as e: 106 | return '异常信息' + str(e) 107 | -------------------------------------------------------------------------------- /conn/ql/ql_timing.py: -------------------------------------------------------------------------------- 1 | """ 2 | 执行青龙有关的定时任务有关 3 | """ 4 | import json 5 | import os 6 | import re 7 | import time 8 | 9 | from conn.ql.ql import QL 10 | from conn.tools.log import LoggerClass 11 | from conn.tools.sql import Sql 12 | 13 | 14 | class Timing: 15 | def __init__(self): 16 | self.ql = QL() 17 | self.sql = Sql() 18 | self.logger = LoggerClass() 19 | 20 | def check_ct(self, state=0) -> str: 21 | """ 22 | 获取新青龙tk 23 | :param state: 0 or 1 24 | :return: 返回删除的字符串 25 | """ 26 | li = "" 27 | value1 = self.sql.selectAll(table=self.sql.surface[3], where=f"state={state}") 28 | for ql_tk in value1: 29 | for i in range(3): 30 | ck = self.ql.ql_tk(ql_tk) 31 | if ck[0] == 200: 32 | self.sql.update(table=self.sql.surface[3], Authorization=ck[1], state=0, 33 | where=f"name='{ql_tk[0]}'") 34 | self.logger.write_log(f"{ql_tk[0]} 获取tk成功") 35 | break 36 | # 两次获取不到删除 37 | elif i == 2 or ck[0] == 403: 38 | self.sql.delete(table=self.sql.surface[3], where=f"name='{ql_tk[0]}'") 39 | os.remove(ql_tk[5]) if os.path.isfile(ql_tk[5]) else "没有文件跳过" 40 | li += ql_tk[0] + "\n" 41 | elif ck[0] == 500: 42 | self.logger.write_log(f"{ql_tk[0]}容器的 Bearer添加失败, 30s后再次获取") 43 | time.sleep(30) 44 | return li 45 | 46 | def clear_list(self, state=0): 47 | """ 48 | 清空参数和获取任务列表 49 | :param state: 0表示执行正常的 1表示执行异常的,默认 0 50 | :return: 返回删除的字符串 51 | """ 52 | self.sql.delete(table=self.sql.surface[1]) 53 | try: 54 | li = "" 55 | value1 = self.sql.selectAll(table=self.sql.surface[3], where=f"state={state}") 56 | for ql_tk in value1: 57 | js_ql = self.ql.crons(ql_tk) 58 | # 跳过检测 59 | if js_ql[0] != 200: 60 | # 获取到非正常状态自动删除 61 | self.logger.write_log(f'{ql_tk[0]} 获取列表异常自动删除任务') 62 | self.sql.delete(table=self.sql.surface[3], where=f"name='{ql_tk[0]}'") 63 | os.remove(ql_tk[5]) if os.path.isfile(ql_tk[5]) else "没有文件跳过" 64 | li += ql_tk[0] + "获取列表异常\n" 65 | continue 66 | # 执行到这里把异常的改为正常 67 | if state == 1: 68 | self.sql.update(table=self.sql.surface[3], state=0, where=f"name='{ql_tk[0]}'") 69 | js = dict() 70 | # 如果青龙里面有层data就解包 71 | for i in js_ql[1]['data'] if 'data' in js_ql[1] else js_ql[1]: 72 | if len(i['command'].split('/')) == 2: 73 | aa = re.findall('task .*?/([a-zA-Z0-9&=_/-]+\.\w+)', i['command']) 74 | else: 75 | aa = re.findall('task ([a-zA-Z0-9&=_/-]+\.\w+)', i['command'].split('/')[-1]) 76 | if aa: 77 | if not (aa[0] in js): 78 | js[aa[0]] = {} 79 | # 用来区分 版本json格式差异 80 | if 'id' in i: 81 | js[aa[0]].setdefault(i['command'], 82 | {'id': i['id'], "name": i["name"], "isDisabled": i["isDisabled"]}) 83 | else: 84 | js[aa[0]].setdefault(i['command'], 85 | {'id': i['_id'], "name": i["name"], "isDisabled": i["isDisabled"]}) 86 | else: 87 | self.logger.write_log(f"{ql_tk[0]} 跳过录入任务: {i['command']}") 88 | with open(ql_tk[5], mode='w+', encoding='utf-8') as f: 89 | json.dump(js, f, ensure_ascii=False) 90 | self.logger.write_log(f"{ql_tk[0]} 获取任务列表成功") 91 | return li 92 | except Exception as e: 93 | self.logger.write_log(f'获取列表异常: {e}') 94 | return "" 95 | -------------------------------------------------------------------------------- /conn/ql/Timing.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | import time 5 | 6 | from conn.bots.interaction import Interaction 7 | from conn.ql.ql import QL 8 | from conn.tools.log import LoggerClass 9 | from conn.tools.sql import Sql 10 | 11 | 12 | class Timing: 13 | def __init__(self): 14 | """ 15 | 获取TK和对任务列表进行重新序列化 16 | """ 17 | self.sql = Sql() 18 | self.logger = LoggerClass() 19 | self.ql = QL() 20 | self.interaction = Interaction() 21 | 22 | def check_ct(self, state=0) -> str: 23 | """ 24 | 获取新青龙tk 25 | :param state: 0 or 1 26 | :return: 返回删除的字符串 27 | """ 28 | li = "" 29 | value1 = self.sql.selectAll(table=self.sql.surface[3], where=f"state={state}") 30 | for ql_tk in value1: 31 | for i in range(3): 32 | ck = self.ql.ql_tk(ql_tk) 33 | if ck[0] == 200: 34 | self.sql.update(table=self.sql.surface[3], Authorization=ck[1], state=0, 35 | where=f"name='{ql_tk[0]}'") 36 | self.logger.write_log(f"{ql_tk[0]} 获取tk成功") 37 | break 38 | # 两次获取不到删除 39 | elif i == 2 or ck[0] == 403: 40 | self.sql.delete(table=self.sql.surface[3], where=f"name='{ql_tk[0]}'") 41 | os.remove(ql_tk[5]) if os.path.isfile(ql_tk[5]) else "没有文件跳过" 42 | li += ql_tk[0] + "\n" 43 | elif ck[0] == 500: 44 | self.logger.write_log(f"{ql_tk[0]}容器的 Bearer添加失败, 30s后再次获取") 45 | time.sleep(30) 46 | if li: 47 | # 这一点是为了检测标记是否需要更新 48 | self.interaction.marking_time() 49 | self.interaction.for_message(li, False) 50 | return li 51 | 52 | def clear_list(self, state=0): 53 | """ 54 | 清空参数和获取任务列表 55 | :param state: 0表示执行正常的 1表示执行异常的,默认 0 56 | :return: 返回删除的字符串 57 | """ 58 | self.sql.delete(table=self.sql.surface[1]) 59 | try: 60 | li = "" 61 | value1 = self.sql.selectAll(table=self.sql.surface[3], where=f"state={state}") 62 | for ql_tk in value1: 63 | js_ql = self.ql.crons(ql_tk) 64 | # 跳过检测 65 | if js_ql[0] != 200: 66 | # 获取到非正常状态自动删除 67 | self.logger.write_log(f'{ql_tk[0]} 获取列表异常自动删除任务') 68 | self.sql.delete(table=self.sql.surface[3], where=f"name='{ql_tk[0]}'") 69 | os.remove(ql_tk[5]) if os.path.isfile(ql_tk[5]) else "没有文件跳过" 70 | li += ql_tk[0] + "获取列表异常\n" 71 | continue 72 | # 执行到这里把异常的改为正常 73 | if state == 1: 74 | self.sql.update(table=self.sql.surface[3], state=0, where=f"name='{ql_tk[0]}'") 75 | js = dict() 76 | # 如果青龙里面有层data就解包 77 | for i in js_ql[1]['data'] if 'data' in js_ql[1] else js_ql[1]: 78 | if len(i['command'].split('/')) == 2: 79 | aa = re.findall('task .*?/([a-zA-Z0-9&=_/-]+\.\w+)', i['command']) 80 | else: 81 | aa = re.findall('task ([a-zA-Z0-9&=_/-]+\.\w+)', i['command'].split('/')[-1]) 82 | if aa: 83 | if not (aa[0] in js): 84 | js[aa[0]] = {} 85 | # 用来区分 版本json格式差异 86 | if 'id' in i: 87 | js[aa[0]].setdefault(i['command'], 88 | {'id': i['id'], "name": i["name"], "isDisabled": i["isDisabled"]}) 89 | else: 90 | js[aa[0]].setdefault(i['command'], 91 | {'id': i['_id'], "name": i["name"], "isDisabled": i["isDisabled"]}) 92 | else: 93 | self.logger.write_log(f"{ql_tk[0]} 跳过录入任务: {i['command']}") 94 | with open(ql_tk[5], mode='w+', encoding='utf-8') as f: 95 | json.dump(js, f, ensure_ascii=False) 96 | self.logger.write_log(f"{ql_tk[0]} 获取任务列表成功") 97 | if li: 98 | # 这一点是为了检测标记是否需要更新 99 | self.interaction.marking_time() 100 | self.interaction.for_message(li, False) 101 | return li 102 | except Exception as e: 103 | self.logger.write_log(f'获取列表异常: {e}') 104 | return "" 105 | 106 | -------------------------------------------------------------------------------- /conn/tools/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | from datetime import datetime 4 | from sys import stdout 5 | 6 | import colorlog 7 | import os 8 | 9 | from logging.handlers import RotatingFileHandler 10 | 11 | from conn.Web.ws_send import send_message 12 | from conn.tools.conn import ConnYml 13 | 14 | yml = ConnYml() 15 | yml.creat_yml() 16 | read = yml.read_yaml() 17 | 18 | 19 | class LoggerClass: 20 | logFile = read['log'] # 定义日志存储的文件夹 21 | log_colors_config = { 22 | 'DEBUG': 'cyan', 23 | 'INFO': 'purple', 24 | 'WARNING': 'yellow', 25 | 'ERROR': 'red', 26 | 'CRITICAL': 'bold_red', 27 | } 28 | 29 | def __init__(self, level='info'): 30 | self.level = level 31 | self.norm_fomatter = colorlog.ColoredFormatter(f'%(log_color)s[%(asctime)s]\t' 32 | '%(message)s', 33 | log_colors=self.log_colors_config) 34 | # 提取路径 35 | pa = re.findall('(.*?)/\w+\.log', self.logFile) 36 | if pa: 37 | if not os.path.exists(pa[0]): # 判断日志存储文件夹是否存在,不存在,则新建 38 | os.makedirs(pa[0]) 39 | # 初始化日志类参数 40 | self.logger = logging.getLogger(__name__) 41 | # self.logger.setLevel(self.level_relations.get(level)) 42 | self.logger.setLevel('DEBUG') 43 | # 生成以当天日期为名称的日志文件 44 | self.filename = self.logFile 45 | # 定义日志输出到前面定义的filename中 46 | self.filelogger = RotatingFileHandler(self.logFile, 'a+', encoding="UTF-8") 47 | self.filelogger.setLevel('DEBUG') # 设置Handler级别 48 | # self.filelogger.setLevel(self.level_relations.get(level)) 49 | # 定义日志输出的格式 50 | # formatter = logging.Formatter(fmt) 51 | # asctime可能用不了 52 | self.filelogger.setFormatter(self.norm_fomatter) 53 | 54 | # 控制台 55 | self.norm_hdl_std = logging.StreamHandler(stdout) 56 | self.norm_hdl_std.setLevel('DEBUG') # 设置Handler级别 57 | self.norm_hdl_std.setFormatter(self.norm_fomatter) 58 | 59 | if not self.logger.handlers: 60 | self.logger.addHandler(self.norm_hdl_std) 61 | self.logger.addHandler(self.filelogger) 62 | 63 | def write_log(self, message, level=""): 64 | """ 65 | #日志输出到控制台 66 | console=logging.StreamHandler() 67 | self.logger.addHandler(console) 68 | """ 69 | lev = level if level else self.level 70 | msg_format = f'[{lev}]-\t{message}' 71 | try: 72 | if lev == 'debug': 73 | self.logger.debug(msg=msg_format) 74 | send_message(f"{self.TimeStampToTime()}\t{msg_format}") 75 | elif lev == 'info': 76 | self.logger.info(msg=msg_format) 77 | send_message(f"{self.TimeStampToTime()}\t{msg_format}") 78 | elif lev == 'warning': 79 | self.logger.warning(msg=msg_format) 80 | send_message(f"{self.TimeStampToTime()}\t{msg_format}") 81 | elif lev == 'error': 82 | self.logger.error(msg=msg_format) 83 | send_message(f"{self.TimeStampToTime()}\t{msg_format}") 84 | else: 85 | self.logger.critical(msg=msg_format) 86 | send_message(f"{self.TimeStampToTime()}\t{msg_format}") 87 | except Exception as e: 88 | print("日志写入权限错误:", e) 89 | 90 | def TimeStampToTime(self): 91 | """ 92 | 伪造日志时间 93 | :return: 94 | """ 95 | return str("[" + datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f]')[:-4] + "]") 96 | 97 | def read_log(self): 98 | """ 99 | 读取日志文件 100 | :return: 打印html格式的日志,异常返回-1 101 | """ 102 | try: 103 | st = [] 104 | rz1 = yml.read_txt(self.logFile) 105 | if rz1 == -1: 106 | return [] 107 | if len(rz1) > 100: 108 | yml.delete_first_lines(self.logFile, -100) 109 | sun = 0 110 | # 颠倒数组顺序 111 | rz1.reverse() 112 | # 遍历所有行 113 | for i in rz1: 114 | if sun < 100: 115 | sun += 1 116 | else: 117 | break 118 | # 如果就\n则跳过 119 | if i == '\n': 120 | continue 121 | j = re.sub("(?:\[35m|\[0m|\[33m|\[36m|\[31m|\n)", '', i) 122 | st.append(j) 123 | # 把颠倒的顺序颠倒回来 124 | st.reverse() 125 | return st 126 | except Exception as e: 127 | return [f'com.gheaders.log. LoggerClass.read_log 日志文件异常: {e}'] 128 | -------------------------------------------------------------------------------- /conn/bots/interaction.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import re 4 | import time 5 | 6 | from conn.bots.getUpdate import GetUpdate 7 | from conn.ql.ql_timing import Timing 8 | from conn.tools.sql import Sql 9 | 10 | 11 | class Interaction(GetUpdate): 12 | def __init__(self): 13 | """ 14 | 机器人交互类 15 | """ 16 | super().__init__() 17 | self.sql = Sql() 18 | self.timing = Timing() 19 | 20 | def main_white(self, text: str): 21 | """ 22 | 根据用户命令不同进行交互 23 | :return: 24 | """ 25 | self.marking_time() 26 | # 把用户指令分为两种一种只有指令 27 | res = re.findall("^(/\w+)\s+([0-9a-zA-z_@:/.—-]+)", text) 28 | if res: 29 | if res[0][0] == "/forward": 30 | return self.from_forward(res[0][1]) 31 | elif res[0][0] == "/prohibit": 32 | return self.from_prohibit(res[0][1]) 33 | elif res[0][0] == "/quit": 34 | return self.from_quit(res[0][1]) 35 | elif res[0][0] == "/putk": 36 | return self.from_putk(res[0][1]) 37 | elif res[0][0] == "/delay": 38 | return self.from_delay(res[0][1]) 39 | else: 40 | return 41 | res = re.findall("^(/\w+)$", text) 42 | if res: 43 | if res[0] == "/start": 44 | return self.start() 45 | else: 46 | return 47 | 48 | def for_message(self, texts: str, tf: bool = True, chat_id: str = None): 49 | """ 50 | 转发消息 51 | :param texts: 52 | :param tf: 默认转发到频道 53 | :param chat_id: 54 | :return: 55 | """ 56 | try: 57 | self.marking_time() 58 | send = 0 59 | for i in range(5): 60 | if chat_id: 61 | send = self.send_message(text=texts, chat_id=chat_id) 62 | elif tf and self.AdReg.get('Send_IDs'): 63 | send = self.send_message(text=texts, chat_id=self.AdReg.get('Send_IDs')) 64 | elif not tf and self.AdReg.get('Administrator'): 65 | send = self.send_message(text=texts, chat_id=self.AdReg.get('Administrator')) 66 | if send == 200: 67 | return 68 | time.sleep(random.randint(1, 10)) 69 | except Exception as e: 70 | print(e) 71 | 72 | def from_forward(self, param): 73 | """ 74 | 提交转发频道或者群组 75 | :param param: 76 | :return: 77 | """ 78 | self.revise_Config('Send_IDs', param) 79 | os.environ['marking_time'] = str(int(time.time())) 80 | 81 | def from_prohibit(self, param): 82 | """ 83 | 脚本加入黑名单 84 | :param param: 85 | :return: 86 | """ 87 | li = self.AdReg.get("prohibit") + [param] if self.AdReg.get("prohibit") is not None else [param] 88 | self.revise_Config('prohibit', li) 89 | os.environ['marking_time'] = str(int(time.time())) 90 | 91 | def from_quit(self, param): 92 | """ 93 | 退出群聊或频道 94 | :param param: 95 | :return: 96 | """ 97 | self.leaveChat(param) 98 | 99 | def from_putk(self, param: str): 100 | """ 101 | 提交青龙 别名@青龙URL@Client_ID@Client_Secre 102 | :param param: 103 | :return: 104 | """ 105 | try: 106 | puts = param.split('@') 107 | if len(puts) != 4: 108 | return self.for_message("提交青龙参数不合法", False) 109 | st = re.findall('^(https?://[\w.:]+)', puts[1]) 110 | if st: 111 | inst = self.sql.insert(table=self.sql.surface[3], name=f"{puts[0]}", ip=f"{st[0]}", 112 | Client_ID=f"{puts[2]}", Client_Secret=f"{puts[3]}", Authorization="", 113 | json=f"{self.AdReg.get('json')}{puts[0]}.json", state=1) 114 | if inst > 0: 115 | return self.for_message(f"提交 {puts[0]} 成功", False) 116 | elif inst == -1: 117 | return self.for_message(f"提交 {puts[0]} 失败,提交的内容和之前提交的内容冲突", 118 | False) 119 | else: 120 | return self.for_message(f"提交 {puts[0]} 失败,失败原因: {inst}", 121 | False) 122 | else: 123 | return self.for_message(f"获取到的青龙链接地址是: {puts[1]},不合法", False) 124 | except Exception as e: 125 | print("conn.bots.interaction.Interaction.from_putk 发生异常", e) 126 | return self.for_message(f"添加青龙获取异常: {e}", False) 127 | 128 | def start(self): 129 | """ 130 | 初始化主动获取列表 131 | :return: 132 | """ 133 | self.timing.check_ct(state=1) 134 | self.timing.clear_list() 135 | 136 | def from_delay(self, param): 137 | """ 138 | 对脚本添加延迟 139 | :param param: 140 | :return: 141 | """ 142 | if param.isdigit(): 143 | self.revise_Config('Delay', int(param)) 144 | os.environ['marking_time'] = str(int(time.time())) 145 | else: 146 | self.for_message('请输入类型为数字', False) 147 | 148 | -------------------------------------------------------------------------------- /conn/mission/sorting.py: -------------------------------------------------------------------------------- 1 | import re 2 | from urllib import parse 3 | 4 | from conn.mission.sundries import Sundries 5 | from conn.tools.log import LoggerClass 6 | 7 | 8 | class Sorting: 9 | 10 | def __init__(self): 11 | """ 12 | 分拣活动的类型,根据类型分配不同地方 13 | """ 14 | self.logger = LoggerClass() 15 | self.sundries = Sundries() 16 | 17 | def dispatch(self, tg_text: str): 18 | """ 19 | 把tg的消息进行分类派送到他们需要去的地方 20 | :param tg_text: text文本 21 | :return: 22 | """ 23 | try: 24 | self.sundries.marking_time() 25 | 26 | # 对URL进行处理去掉关键字和URL解码 27 | tg_text = re.sub("[()`*]*(?:export NOT_TYPE=\".*?\";)*", "", parse.unquote(tg_text)) 28 | # 直接结束 29 | if re.findall(r'(https://u\.jd\.com/.*?)', tg_text, re.S): 30 | return 31 | 32 | if re.findall('(?:NOTexport|RUNexport|export)\s+[0-9a-zA-Z_]+=', tg_text): 33 | self.finishing_text(tg_text) 34 | return 35 | self.finishing_url(tg_text) 36 | except Exception as e: 37 | self.logger.write_log(f"com.txt.txt.zil.Delivery.dispatch 异常 {e}") 38 | 39 | def finishing_url(self, text_url: str): 40 | """ 41 | 处理URL类型 42 | :param text_url: 43 | :return: 44 | """ 45 | try: 46 | mark = "" 47 | mark_text = re.findall("((?:NOT|RUN)+)", text_url) 48 | if mark_text: 49 | mark = mark_text[0] 50 | # 获取链接 51 | ht_tx = re.findall(r'(https://[\w\-.]+(?:isv|jd).*?\.com/[a-zA-Z0-9&?=_/-].*)"?', text_url) 52 | if not ht_tx: 53 | return [] 54 | for i in ht_tx: 55 | text_list = self.sundries.https_txt(i) 56 | if not text_list: 57 | continue 58 | for j in text_list: 59 | # 发送去队列了 60 | self.sundries.tx_compared([mark, j[1], j[0]]) 61 | return [200] 62 | except Exception as e: 63 | self.logger.write_log(f"conn.mission.sorting.Sorting.finishing_url 异常 {e}") 64 | return [] 65 | 66 | def finishing_text(self, text_str: str): 67 | """ 68 | 对非链接进行处理 69 | :param text_str: 70 | :return: 71 | """ 72 | global ex_name 73 | try: 74 | # 对多个参数支持 75 | points_list = text_str.split('\n') 76 | mark = None 77 | text1 = '' 78 | spell = '' 79 | # 遍历数组 80 | for poi_str in points_list: 81 | # 检测是否在黑名单 82 | re_text = re.findall(r'((?:NOTexport|RUNexport|export)\s+[0-9a-zA-Z_]+)="?([A-Za-z0-9&_/:.?=\-]{5,})"?', 83 | poi_str, re.S) 84 | # 如果获取数组为空跳过 85 | if not re_text or len(re_text[0]) != 2: 86 | 87 | # 如果断开表示是两个活动 88 | if spell: 89 | # 如果关键字在数组中执行并且清空字典 90 | ex_name = self.sundries.looking(text1) 91 | 92 | if ex_name: 93 | for ex_list in ex_name: 94 | TYPE = re.findall("https://(\w{2})", spell) 95 | if TYPE: 96 | spell += f'export NOT_TYPE="{TYPE[0]}";' 97 | else: 98 | spell += 'export NOT_TYPE="no";' 99 | # 发送去队列了 100 | self.sundries.tx_compared([mark, ex_list, spell]) 101 | mark = None 102 | spell = '' 103 | continue 104 | 105 | # 如果一行出现两个关键字 106 | for text2 in re_text: 107 | text2 = list(text2) 108 | 109 | # 如果值存在关键字跳过执行 110 | if re.findall('(?:shopId\d|venderId\d|shopid\d|venderid\d)', text2[-1]): 111 | self.logger.write_log(f"检测到屏蔽关键字屏蔽内容是: {poi_str}") 112 | continue 113 | 114 | mark_text = re.findall("((?:NOT|RUN)+)", text2[0]) 115 | # 如果是关键字则切割和记录 116 | if mark_text: 117 | mark = mark_text[0] 118 | text2[0] = text2[0][3::] 119 | text1 = text2[0] 120 | 121 | spell += text2[0] + '="' + text2[1] + '";' 122 | # 获取脚本 123 | if not text1: 124 | return [] 125 | ex_name = self.sundries.looking(text1) 126 | 127 | if ex_name: 128 | for ex_list in ex_name: 129 | TYPE = re.findall("https://(\w{2})", spell) 130 | if TYPE: 131 | spell += f'export NOT_TYPE="{TYPE[0]}";' 132 | else: 133 | spell += 'export NOT_TYPE="no";' 134 | # 发送去队列了 135 | self.sundries.tx_compared([mark, ex_list, spell]) 136 | else: 137 | self.logger.write_log(f"没有查询到 {poi_str}") 138 | return [] 139 | except Exception as e: 140 | self.logger.write_log(f"conn.mission.sorting.Sorting.finishing_text 异常 {e}") 141 | return [] 142 | -------------------------------------------------------------------------------- /conn/ql/ql.py: -------------------------------------------------------------------------------- 1 | """ 2 | 青龙接口类 3 | """ 4 | import json 5 | 6 | import requests 7 | 8 | from conn.tools.log import LoggerClass 9 | 10 | logger = LoggerClass() 11 | 12 | 13 | class QL: 14 | def __init__(self): 15 | self.headers = [ 16 | { 17 | 'Accept': 'application/json', 18 | 'Content-Type': 'application/json;charset=UTF-8' 19 | }, 20 | { 21 | 'Accept': 'application/json', 22 | 'Authorization': "", 23 | 'Content-Type': 'application/json;charset=UTF-8' 24 | } 25 | ] 26 | self.timeout = 60 27 | 28 | def ql_tk(self, ql_tk): 29 | """ 30 | 用于获取登录用的ck,ck有效期一个月 31 | :param ql_tk: 青龙数据库 32 | :return: 返回登录用的Bearer XXXXXXXXXXX,[状态码,内容] 33 | """ 34 | url = ql_tk[1] + "/open/auth/token" 35 | data = { 36 | 'client_id': ql_tk[2], 37 | 'client_secret': ql_tk[3] 38 | } 39 | try: 40 | tk = requests.get(url=url, params=data, timeout=self.timeout, headers=self.headers[0]) 41 | js_tk = tk.json() 42 | if js_tk['code'] == 200: 43 | return [js_tk['code'], js_tk['data']['token_type'] + " " + js_tk['data']['token']] 44 | else: 45 | logger.write_log(f"{ql_tk[0]} 获取青龙Authorization异常 状态码: {js_tk['code']} 原因: {js_tk['data']}", 46 | level='debug') 47 | return [403] 48 | except Exception as e: 49 | logger.write_log(f"{ql_tk[0]} 请求青龙异常: {e}", level='debug') 50 | return [500] 51 | 52 | def ql_run(self, data, ql_tk: tuple): 53 | """ 54 | 执行青龙任务 55 | :param data: int数组 56 | :param ql_tk: 青龙数据库 57 | :return: 0 or -1 58 | """ 59 | try: 60 | url = ql_tk[1] + '/open/crons/run' 61 | headers = self.headers[1] 62 | headers['Authorization'] = ql_tk[4] 63 | ss = requests.put(url=url, headers=headers, data=json.dumps(data), timeout=self.timeout) 64 | status = ss.status_code 65 | # 获取返回的状态码 66 | if status == 200: 67 | return 0 68 | else: 69 | logger.write_log(f"任务执行失败: {status}", level='debug') 70 | return -1 71 | except Exception as e: 72 | logger.write_log(f"执行青龙任务失败,错误信息: {e}", level='debug') 73 | return -1 74 | 75 | def crons(self, ql_tk: list): 76 | """ 77 | 获取任务列表 78 | :param ql_tk: 青龙数据库的列表 79 | :return: [状态码, 列表] 80 | """ 81 | try: 82 | url = ql_tk[1] + '/open/crons' 83 | headers = self.headers[1] 84 | headers['Authorization'] = ql_tk[4] 85 | lis = requests.get(url=url, headers=headers, timeout=self.timeout) 86 | li = lis.json() 87 | if li['code'] == 200: 88 | return [li['code'], li['data']] 89 | logger.write_log(f"状态码: {li['code']} 请给相应权限", level='debug') 90 | return li['code'] 91 | except Exception as e: 92 | logger.write_log(f"获取青龙任务列表失败: {e}", level='debug') 93 | return [403] 94 | 95 | def configs_check(self, path, ql_tk: tuple): 96 | """ 97 | 获取配置文件的内容 98 | :param path: 配置文件名称 99 | :param ql_tk: 青龙数据库 100 | :return: 返回文件内容,错误返回{'code': 404} 101 | """ 102 | try: 103 | url = ql_tk[1] + '/open/configs/' + path 104 | headers = self.headers[1] 105 | headers['Authorization'] = ql_tk[4] 106 | ss = requests.get(url=url, headers=headers, timeout=self.timeout) 107 | status = ss.status_code 108 | # 获取返回的状态码 109 | if status == 200: 110 | return ss.json() 111 | return {'code': 404} 112 | except Exception as e: 113 | logger.write_log(f"获取配置文件内容失败: {e}", level='debug') 114 | return {'code': 404} 115 | 116 | def configs_revise(self, path, data, ql_tk: tuple): 117 | """ 118 | 修改配置文件 119 | :param path: 配置文件名称 120 | :param data: 传输的内容 121 | :param ql_tk: 青龙数据库 122 | :return: 返回{"code":200,"message":"保存成功"},错误返回{'code': 404} 123 | """ 124 | try: 125 | url = ql_tk[1] + '/open/configs/save' 126 | cs = {"content": data, "name": path} 127 | headers = self.headers[1] 128 | headers['Authorization'] = ql_tk[4] 129 | ss = requests.post(url=url, headers=headers, data=json.dumps(cs), timeout=self.timeout) 130 | status = ss.status_code 131 | # 获取返回的状态码 132 | if status == 200: 133 | return ss.json() 134 | return {'code': 404} 135 | except Exception as e: 136 | logger.write_log(f"修改配置文件内容失败: {e}", level='debug') 137 | return {'code': 404} 138 | 139 | def disable(self, data: list, ql_tk: tuple): 140 | """ 141 | 禁用青龙任务 142 | :param data: int数组 143 | :param ql_tk: 青龙数据库 144 | :return: 0 or -1 145 | """ 146 | try: 147 | url = ql_tk[1] + '/open/crons/disable' 148 | headers = self.headers[1] 149 | headers['Authorization'] = ql_tk[4] 150 | ss = requests.put(url=url, headers=headers, data=json.dumps(data), timeout=self.timeout) 151 | status = ss.status_code 152 | # 获取返回的状态码 153 | if status == 200: 154 | logger.write_log("禁用任务成功") 155 | return 0 156 | else: 157 | logger.write_log(f"任务禁用失败: {status}", level='debug') 158 | return -1 159 | except Exception as e: 160 | logger.write_log(f"执行青龙禁用任务失败,错误信息: {e}", level='debug') 161 | return -1 162 | -------------------------------------------------------------------------------- /conn/tools/sql.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from sqlite3 import IntegrityError 3 | 4 | from conn.tools.conn import ConnYml 5 | 6 | """ 7 | 数据库类 8 | """ 9 | connyml = ConnYml() 10 | 11 | 12 | class Sql: 13 | """ 14 | 数据库类 15 | """ 16 | 17 | def __init__(self): 18 | # 172.17.0.2 localhost 19 | self.conn = sqlite3.connect(connyml.read_yaml()["repeat"], timeout=20, check_same_thread=False) 20 | self.cursor = self.conn.cursor() 21 | self.surface = ['JdQl', 'repeat', 'turn_url', 'QL', 'User'] 22 | 23 | def execute(self, sql): 24 | """ 25 | 返回执行execute()方法后影响的行数 26 | :param self: 27 | :param sql: 28 | :return: 29 | """ 30 | self.cursor.execute(sql) 31 | rowcount = self.cursor.rowcount 32 | return rowcount 33 | 34 | def delete(self, **kwargs): 35 | """ 36 | 删除并返回影响行数 37 | table="表", where="列 = 值" 38 | :param kwargs: 39 | :return: 40 | """ 41 | table = kwargs['table'] 42 | where = kwargs['where'] if 'where' in kwargs else None 43 | if where: 44 | sql = f'DELETE FROM {table} where {where}' 45 | else: 46 | sql = f'DELETE FROM {table}' 47 | # print(sql) 48 | global rowcount 49 | try: 50 | # 执行SQL语句 51 | self.cursor.execute(sql) 52 | # 提交到数据库执行 53 | self.conn.commit() 54 | # 影响的行数 55 | rowcount = self.cursor.rowcount 56 | except Exception as e: 57 | # 发生错误时回滚 58 | self.conn.rollback() 59 | return str(e) 60 | return rowcount 61 | 62 | def insert(self, **kwargs): 63 | """ 64 | 返回添加的ID 65 | table="表", 列1="值", 列2="值" 66 | :param kwargs: 67 | :return: 68 | """ 69 | table = kwargs['table'] 70 | del kwargs['table'] 71 | sql = 'insert into %s(' % table 72 | fields = "" 73 | values = "" 74 | for k, v in kwargs.items(): 75 | fields += "%s," % k 76 | values += "'%s'," % v 77 | fields = fields.rstrip(',') 78 | values = values.rstrip(',') 79 | sql = sql + fields + ") values(" + values + ")" 80 | # print(sql) 81 | global res 82 | try: 83 | # 执行SQL语句 84 | self.cursor.execute(sql) 85 | # 提交到数据库执行 86 | self.conn.commit() 87 | # 获取自增id 88 | res = self.cursor.lastrowid 89 | except IntegrityError: 90 | # 发生错误时回滚 91 | self.conn.rollback() 92 | return -1 93 | except Exception as e: 94 | # 发生错误时回滚 95 | self.conn.rollback() 96 | return str(e) 97 | return res 98 | 99 | def update(self, **kwargs): 100 | """ 101 | 修改数据返回影响的行 102 | table="表", 列="值", 列="值", where="列="值"" 103 | :param kwargs: 104 | :return: 105 | """ 106 | table = kwargs['table'] 107 | # del kwargs['table'] 108 | kwargs.pop('table') 109 | where = kwargs['where'] 110 | kwargs.pop('where') 111 | sql = 'update %s set ' % table 112 | for k, v in kwargs.items(): 113 | sql += "%s='%s'," % (k, v) 114 | sql = sql.rstrip(',') 115 | sql += ' where %s' % where 116 | # print(sql) 117 | global rowcount 118 | try: 119 | # 执行SQL语句 120 | self.cursor.execute(sql) 121 | # 提交到数据库执行 122 | self.conn.commit() 123 | # 影响的行数 124 | rowcount = self.cursor.rowcount 125 | except Exception as e: 126 | # 发生错误时回滚 127 | self.conn.rollback() 128 | return str(e) 129 | return rowcount 130 | 131 | def selectTopone(self, **kwargs): 132 | """ 133 | 查-一条条数据 134 | :param kwargs: 135 | :return: 136 | """ 137 | table = kwargs['table'] 138 | field = 'field' in kwargs and kwargs['field'] or '*' 139 | where = 'where' in kwargs and 'where ' + kwargs['where'] or '' 140 | order = 'order' in kwargs and 'order by ' + kwargs['order'] or '' 141 | sql = 'select %s from %s %s %s limit 1' % (field, table, where, order) 142 | # print(sql) 143 | global data 144 | try: 145 | # 实时刷新 self.conn.commit() 146 | # self.conn.commit() 147 | # 执行SQL语句 148 | self.cursor.execute(sql) 149 | # 使用 fetchone() 方法获取单条数据. 150 | data = self.cursor.fetchone() 151 | except Exception as e: 152 | # 发生错误时回滚 153 | self.conn.rollback() 154 | return str(e) 155 | return data 156 | 157 | def selectAll(self, **kwargs): 158 | """ 159 | 查所有数据 160 | table="表", where="列 = '值'" 161 | :param kwargs: 162 | :return: 163 | """ 164 | # table 165 | table = kwargs['table'] 166 | field = 'field' in kwargs and kwargs['field'] or '*' 167 | where = 'where' in kwargs and 'where ' + kwargs['where'] or '' 168 | order = 'order' in kwargs and 'order by ' + kwargs['order'] or '' 169 | sql = 'select %s from %s %s %s ' % (field, table, where, order) 170 | # print(sql) 171 | try: 172 | # 实时刷新 self.conn.commit() 173 | # self.conn.commit() 174 | # 执行SQL语句 175 | self.cursor.execute(sql) 176 | # 使用 fetchone() 方法获取单条数据. 177 | data = self.cursor.fetchall() 178 | except Exception as e: 179 | # 发生错误时回滚 180 | self.conn.rollback() 181 | return str(e) 182 | return data 183 | 184 | def exe_sql(self, sql: str) -> list: 185 | """ 186 | 执行多个sql,用于更新表格数据 187 | :param sql: 读取sql文件后的 188 | :return: 0 or -1 189 | """ 190 | try: 191 | self.conn.executescript(sql) 192 | return [0] 193 | except Exception as e: 194 | # 发生错误时回滚 195 | self.conn.rollback() 196 | return [-1] 197 | -------------------------------------------------------------------------------- /conn/tools/conn.py: -------------------------------------------------------------------------------- 1 | """ 2 | 文件有关的函数 3 | """ 4 | import os 5 | 6 | from ruamel.yaml import YAML 7 | 8 | yaml = YAML(typ='safe') 9 | 10 | 11 | class ConnYml: 12 | 13 | def __init__(self): 14 | self.file_name = "conn.yml" 15 | self.template = { 16 | "Administrator": "", 17 | "Token": "", 18 | "Send_IDs": "", 19 | "prohibit": [' '], 20 | "Proxy": { 21 | "Proxy": "", 22 | "TG_API_HOST": "https://api.telegram.org", 23 | "JK_ALL_PROXY": "" 24 | }, 25 | "deduplication": 0, 26 | "json": "data/", 27 | "log": "log/ql.log", 28 | "repeat": "repeat.sqlite", 29 | "kill": [ 30 | "kill -9 $(netstat -nlp | grep fsbot | awk '{print $7}' | awk -F'/' '{ print $1 }')", 31 | "kill -9 $(netstat -nlp | grep :5008 | awk '{print $7}' | awk -F'/' '{ print $1 }')" 32 | ], 33 | "Delay": 0 34 | } 35 | 36 | def creat_yml(self, file_name="") -> int: 37 | """ 38 | 创建yaml文件,并且补全丢失变量 39 | """ 40 | file_name = file_name if file_name else self.file_name 41 | _remake_flag = False 42 | # 检测文件是否存在 43 | if not os.path.exists(file_name): 44 | with open(file_name, mode='w+', encoding="utf-8") as file: 45 | yaml.dump(self.template, file) 46 | file.close() 47 | return 0 48 | else: 49 | try: 50 | with open(file_name, 'r', encoding='utf-8') as f: 51 | data = yaml.load(f) 52 | f.close() 53 | for _key in data: 54 | # 把多余的删除 55 | if _key not in self.template: 56 | data.pop(_key) 57 | _remake_flag = True 58 | # 获取第二阶级 59 | elif type(self.template.get(_key)) == dict: 60 | if self.template.get(_key).keys() == data.get(_key).keys(): 61 | # 第二层检测 62 | for j in data.get(_key).keys(): 63 | if j not in self.template.get(_key).keys(): 64 | data.get(_key).pop(j) 65 | _remake_flag = True 66 | 67 | for key in self.template: 68 | if key not in data: 69 | data.setdefault(key, self.template.get(key)) 70 | _remake_flag = True 71 | continue 72 | elif type(self.template.get(key)) == dict: 73 | if self.template.get(key).keys() == data.get(key).keys(): 74 | # 第二层检测 75 | for j in self.template.get(key).keys(): 76 | if j not in data.keys(): 77 | data.get(key).setdefault(j, self.template.get(key).get(j)) 78 | _remake_flag = True 79 | if _remake_flag: 80 | with open(file_name, 'w', encoding='utf-8') as f: 81 | yaml.dump(data, f) 82 | f.close() 83 | except TypeError as e: 84 | print(e) 85 | with open(file_name, mode='w', encoding="utf-8") as file: 86 | yaml.dump(self.template, file) 87 | file.close() 88 | return 0 89 | 90 | def read_yaml(self, file_name="") -> dict: 91 | """ 92 | 读取yaml文件 93 | :param file_name:默认读取./conn.yml 94 | :return data: {} 95 | """ 96 | try: 97 | file_name = file_name if file_name else self.file_name 98 | with open(file_name, 'r', encoding='utf-8') as f: 99 | data = yaml.load(f) 100 | # 关闭文件 101 | f.close() 102 | return data 103 | except Exception as e: 104 | return {} 105 | 106 | def revise_yml(self, data, path='./conn.yml') -> int: 107 | """ 108 | 修改yml配置文件 109 | :param data: 添加的值 110 | :param path: 添加的路径,默认./conn.yml 111 | :return: 正常返回 0 非正常 -1 112 | """ 113 | with open(path, mode='r', encoding="utf-8") as file: 114 | old_data = yaml.load(file) 115 | file.close() 116 | try: 117 | with open(path, mode='w', encoding="utf-8") as file: 118 | yaml.dump(data, file) 119 | file.close() 120 | return 0 121 | except Exception as e: 122 | file.close() 123 | print("异常问题回滚,revise_yaml:" + str(e)) 124 | with open(path, mode='w', encoding="utf-8") as file: 125 | yaml.dump(old_data, file) 126 | file.close() 127 | return -1 128 | 129 | def read_txt(self, file_name="") -> list: 130 | """ 131 | 读取文件内容 132 | :param file_name:文件路径默认目录./conn.yml 133 | :return: 返回文件数据,异常返回-1 134 | """ 135 | try: 136 | file_name = file_name if file_name else self.file_name 137 | with open(file_name, mode='r', encoding='utf-8') as f: 138 | tx = f.readlines() 139 | f.close() 140 | return tx 141 | except Exception as e: 142 | return [] 143 | 144 | def empty_txt(self, file_name="") -> int: 145 | """ 146 | 清空文件内容 147 | :param file_name:文件路径默认目录 148 | :return: 异常返回-1 149 | """ 150 | try: 151 | file_name = file_name if file_name else self.file_name 152 | with open(file_name, mode='w', encoding='utf-8') as f: 153 | f.write('') 154 | return 0 155 | except Exception as e: 156 | return -1 157 | 158 | def delete_first_lines(self, filename, count) -> int: 159 | """ 160 | 删除前多少行 161 | :param filename: 路径 162 | :param count: 行 163 | :return: 164 | """ 165 | try: 166 | with open(filename, 'r', encoding='utf-8') as f: 167 | a = f.readlines() 168 | with open(filename, 'w', encoding='utf-8') as fout: 169 | b = ''.join(a[count:]) 170 | fout.write(b) 171 | return 0 172 | except Exception as e: 173 | return -1 174 | finally: 175 | f.close() 176 | fout.close() 177 | -------------------------------------------------------------------------------- /conn/mission/core.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from conn.ql.ql import QL 4 | from conn.tools.log import LoggerClass 5 | from conn.tools.sql import Sql 6 | from .sundries import Sundries 7 | 8 | 9 | class Main_core: 10 | """ 11 | 执行类 12 | """ 13 | 14 | def __init__(self): 15 | self.ql_js = 'qlva.sh' 16 | self.ql_cks = [] 17 | # 添加配置文件的内容 18 | self.Mark = {} 19 | self.conn = Sql() 20 | self.ql = QL() 21 | self.logger = LoggerClass() 22 | self.sundries = Sundries() 23 | 24 | def main_while(self): 25 | while True: 26 | data = self.sundries.q.get() 27 | self.sundries.marking_time() 28 | # 检测是否需要跳过 29 | team = self.Team(data) 30 | if not team: 31 | self.sundries.q.task_done() 32 | continue 33 | 34 | # 检测是否被执行过 35 | ctr = self.sundries.contrast(data) 36 | # 执行过返回-1结束 37 | if ctr[0] == -1: 38 | self.sundries.q.task_done() 39 | self.logger.write_log(f"识别关键字异常: {data['activities']} 后面还有排队数量 {self.sundries.q.qsize()}") 40 | time.sleep(2) 41 | continue 42 | elif ctr[0] == 3: 43 | self.logger.write_log(f"没有识别到关键字: {data['activities']} 后面还有排队数量 {self.sundries.q.qsize()}") 44 | self.sundries.q.task_done() 45 | time.sleep(2) 46 | continue 47 | elif ctr[0] == 1: 48 | self.logger.write_log(f"识别到关键字已经执行过了, 关键字: {ctr[1]} 后面还有排队数量 {self.sundries.q.qsize()}") 49 | self.sundries.q.task_done() 50 | time.sleep(2) 51 | continue 52 | 53 | # 转发 54 | self.sundries.interaction.for_message(data['activities']) 55 | 56 | self.ql_cks = self.conn.selectAll(table=self.conn.surface[3], where="state=0") 57 | if not self.ql_cks: 58 | self.sundries.q.task_done() 59 | self.logger.write_log("主人你好像没有对接青龙或者没有给我发送 /start") 60 | time.sleep(15) 61 | continue 62 | 63 | # 加入数组伪装队列 64 | data['time'] = int(time.time()) + int(data['interval']) 65 | self.Mark.setdefault(data['jd_js'], data) 66 | 67 | self.execution_ql(data, ctr) 68 | self.sundries.q.task_done() 69 | 70 | def Team(self, data): 71 | """ 72 | 伪造队列,如果不在规定时间会让任务重新排队 73 | :return: 74 | """ 75 | # 检测是否值1小黑屋 76 | if data['jd_js'] in self.sundries.AdReg.get('prohibit'): 77 | self.logger.write_log(f'脚本 {data["jd_js"]} 被你的主人狠心的拖进小黑屋关了永久禁闭') 78 | return False 79 | 80 | # 如果在任务里边 81 | if data['jd_js'] in self.Mark: 82 | if int(self.Mark[data['jd_js']]['time']) < int(time.time()): 83 | # 删除这个值 84 | self.Mark.pop(data['jd_js']) 85 | self.logger.write_log(f"脚本 {data['jd_js']} 的时间到了出去玩耍吧, 后面排队的还有 {self.sundries.q.qsize()}") 86 | return True 87 | else: 88 | self.sundries.q.put(self.Mark[data['jd_js']]) 89 | self.logger.write_log(f"脚本 {data['jd_js']} 刚刚才出去被扔到后面排队了 号码为 {self.sundries.q.qsize()}") 90 | # 根据队列执行不同的时间 91 | sun = self.sundries.q.qsize() 92 | if sun < 5: 93 | time.sleep(int(data['interval']) / 2) 94 | elif sun < 10: 95 | time.sleep(int(data['interval']) / 4) 96 | elif sun < 20: 97 | time.sleep(3) 98 | else: 99 | time.sleep(1) 100 | return False 101 | 102 | return True 103 | 104 | def execution_ql(self, data, ctr): 105 | """ 106 | 执行青龙任务 107 | :return: 108 | """ 109 | # 遍历青龙容器 110 | for j in range(len(self.ql_cks)): 111 | 112 | # 传入脚本名称返回任务ID 113 | ids = self.sundries.ql_compared(data["jd_js"], self.ql_cks[j]) 114 | # 判断是否有脚本 115 | if ids[0] == -1: 116 | # 没有匹配到进入匹配 117 | self.logger.write_log( 118 | f"脚本 {data['jd_js']} 活动参数 {data['activities']} 没有找到, 开始进行系列脚本匹配") 119 | # 如果没有这个任务就去转换多适配 120 | # 分链接和参数转换 121 | if "https://" not in data["activities"]: 122 | url = self.sundries.turn_url(data["activities"]) 123 | # 没有获取到 124 | if not url: 125 | self.logger.write_log(f" {data['activities']} 没有找到匹配出来的脚本跳过本次活动执行,请拉取脚本 {data['jd_js']} 执行") 126 | continue 127 | url = self.sundries.https_txt(url[0]) 128 | else: 129 | url = self.sundries.https_txt(data["activities"]) 130 | # 没有获取到 131 | if not url: 132 | self.logger.write_log( 133 | f" {data['activities']} 没有找到匹配出来的脚本跳过本次活动执行,请拉取脚本 {data['jd_js']} 执行") 134 | continue 135 | # 记录是否被执行 136 | for va in url: 137 | # 把原来的data部分内容替换 138 | data["jd_js"] = va[1].jd_js 139 | data['activities'] = va[0] 140 | ids = self.sundries.ql_compared(data["jd_js"], self.ql_cks[j]) 141 | if ids[0] == -1: 142 | continue 143 | self.for_ql(j, data, ctr, ids) 144 | break 145 | else: 146 | # 如果有这个任务就执行 147 | self.for_ql(j, data, ctr, ids) 148 | # 脚本结束自定义延迟时间 149 | time.sleep(self.sundries.AdReg.get('Delay')) 150 | 151 | def for_ql(self, j, data, ctr, ids) -> bool: 152 | """ 153 | 对execution_ql方法进行拆分出来的后半部分 154 | :return: 155 | """ 156 | if j == 0: 157 | # 把关键字添加到数据库 158 | self.sundries.ql_write(data, ctr) 159 | # 向青龙配置文件添加活动 160 | revise = self.ql.configs_revise(self.ql_js, data["activities"], self.ql_cks[j]) 161 | 162 | # 表示添加活动成功 163 | if revise["code"] == 200: 164 | # 根据脚本id,执行脚本 165 | qid = self.ql.ql_run(ids, self.ql_cks[j]) 166 | if qid == 0: 167 | self.logger.write_log( 168 | f"已经帮主人你把 {self.ql_cks[j][0]} 的小金库填充 {data['jd_js']} 脚本成功 ID {ids[0]} 执行参数: {data['activities']}") 169 | 170 | time.sleep(2) 171 | self.ql.configs_revise(self.ql_js, '', self.ql_cks[j]) 172 | return True 173 | else: 174 | self.logger.write_log( 175 | f"{self.ql_cks[j][0]}异常问题,请主人给我进入你小金库的权限我需要 的权限有 定时任务权限 和 配置文件权限, 否则无法吧金克拉放入主人小金库", 176 | level='error') 177 | return False 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### 根据变量运行自动运行对应任务 2 | 没有代理的填写下面反代 3 | # 本项目由于逻辑混乱和bug太多停止维护 4 | 请跳转新的监控机器人 [aigramBot](https://github.com/XgzK/aigramBot) 5 | ## 特别声明 6 | ```text 7 | 有问题可以加InteIJ外部群 https://t.me/InteIJ (已经在封群的InteIJ的请不要加入,外部群只能使用活动参数的权限) 8 | 需要其他功能的可以反馈添加,或者反馈脚本BUG问题 9 | 有如果有其他获取参数的可以反馈给我添加 10 | 本项目所有活动来自TG各大频道,本项目偷免单助力介意勿用 11 | 害怕偷CK的勿用,不接受任何形式甩锅,不对任何行为负责 12 | 本脚本优先适配船长和M系列脚本,如果有其他相同脚本但是参数不同将优先适配为准 13 | ``` 14 | 15 | ### 容器构建命令 16 | ```shell 17 | docker run -dit \ 18 | -p 5008:5008 \ 19 | -e TZ=Asia/Shanghai \ 20 | --name qlva \ 21 | --restart unless-stopped \ 22 | xgzk/qlvariable:latest 23 | ``` 24 | ```http 25 | http://IP:5008/ 26 | ``` 27 | 需要 定时任务 配置文件 权限 28 | 容器里面没有代码需要等待1-4分钟让程序跑起来 29 | 获取最新线报请重启项目 30 | #### 青龙那边操作 31 | ```text 32 | 进入青龙容器 33 | 青龙10版本执行 34 | touch /ql/config/qlva.sh 35 | 青龙11以后执行 36 | touch /ql/data/config/qlva.sh 37 | 38 | 青龙面板 修改配置文件 config.sh 39 | 10添加 source /ql/config/qlva.sh 40 | 11以后添加 source /ql/data/config/qlva.sh 41 | 可以在配置文件的文件看到qlva.sh文件 42 | ``` 43 | #### 机器人指令 44 | ```text 45 | 机器人指令 46 | 机器人所在群组发送 /id 机器人会返回群组ID (转发线报的机器人别拉自己群会循环发送) 47 | 频道消息转发给机器人会返回 频道信息和个人信息 48 | 下面是私聊消息指令 49 | /forward ID 会把东西转发到这个频道或者群组 暂时只能使用一个ID 50 | /prohibit 名称 脚本加入黑名单会不执行 51 | /quit 频道ID或者@唯一名称 退出群聊或频道 52 | /putk 别名@青龙URL@Client_ID@Client_Secret 提交青龙相关执行参数 别名不能相同否则无法提交成功(提交的无法被执行) 53 | /start 启动提交的青龙,如果任务异常会被删除,也可以同步青龙任务 54 | 机器人交互设置 55 | /delay 秒 脚本执行结束多少秒执行下一个脚本(23/2/1 18/00版本添加指令) 56 | 找 https://t.me/BotFather 发送 /setprivacy 选择自己使用的机器人名称 选择D开头的 57 | 怎么申请机器人自己百度 58 | 没有代理的把下面连接填写反代里 (反代不能转发,会乱码) 59 | https://thingproxy.freeboard.io/fetch/https://api.telegram.org 60 | ``` 61 | ### 对一些链接黑处理机制 62 | ```text 63 | https://cjhydz-isv.isvjcloud.com 提取的是cj 64 | lz cj ji pr sh tx wq 对非链接类型统一使用 no 65 | export NOT_TYPE="lz"; 66 | 别的怎么根据链接筛掉黑号自己解决 67 | 上面的会被自动当成链接变量添加到参数中 68 | ``` 69 | 70 | ### 非adm64系统的问题 71 | ```text 72 | 因为不能测试adm64外的版本不清楚其他版本是否正常 73 | 如果拉取三次容器都显示相同错误的 74 | 请手动构架 75 | 第一步 下载docker目录下的所有文件 76 | 全部上传到Linux系统进入上传文件的目录执行 77 | docker build -t xgzk/qlvariable:latest . 78 | 就行,然后重新执行容器构建命令 79 | ``` 80 | 81 | ## 更新说明 82 | 83 | ```text 84 | 版本1.1 85 | > 修复不同版本数据库差异问题 86 | > 添加去重功能 87 | 版本1.1.1 88 | > 修复重复提示不清楚问题 89 | > 增加请求次数,由原来一次请求失败,现在可以最多请求三次,只要成功一次,就不再请求了 90 | > 优化活动参数重复提醒 91 | 版本1.1.2 92 | > 适配了特别10.2版本,把10.2之前包括10.2定义为9版本 93 | 版本1.2 94 | > 更新可以保留conn.yml文件 95 | > 对一些获取进行不去重处理 96 | > 建议之前版本拉取最新脚本 97 | 版本1.3 98 | > 添加了配置文件检测 99 | > 修补了缺少的文件 100 | > 添加了10版本以上数据库表的检测 101 | 版本2 102 | > 正式启用容器版本 103 | > 取消了复杂的配置,改用程序自动适配 104 | > 有了自动更新省去了更新繁琐的步骤 105 | 版本2.1 106 | > 添加了库优先级,可以指定所有活动脚本走特定库,当库没有才走ID前面的脚本 107 | > 添加禁用活动脚本 108 | > 添加对相同活动去重复功能,只要其他脚本执行过将不再执行 109 | 版本2.2 110 | > 对页面进行美化 111 | 版本3.0 112 | > 使用tg官方机器人监控进行监控群组 113 | > 支持使用反代域名 114 | > 完美与爬虫端融合 115 | > 修改了对比去重复的标记物问题 116 | > 优化了对比数据执行时间缓慢问题 117 | > 不需要科学环境的正在开发还不支持使用(因公益服务器被攻击暂停开发这个部分) 118 | > 2022-11-1 修复当前版本出现BUG问题 119 | > 取消了官方TG库改成统一长连接请求 120 | > 2022-11-6 添加转发消息功能正式版本即将开始发布使用 121 | > 频道消息转发给机器人返回频道ID 群组发送 /id 机器人发给用户频道ID 122 | > 2022-11-7 修复多个参数漏掉问题 123 | > 支持获取链接变量类型 export NOT_TYPE 用户可以自己更改后筛掉黑CK 124 | > 2022-11-8 125 | > 修复没有过滤自己频道线报问题 126 | > 频道消息转发给机器人异常问题 127 | > 转发失败没有提示问题 128 | > 超时线报没有清理问题 129 | > 2022-11-9 13:07 130 | > 超时线报没有清理问题 131 | > 支持单参数活动变量转成伪活动链接(不清楚有没有问题) 132 | > 重复线报不再提示 133 | > 2022-11-9 17:21 134 | > 修复匹配船长库中 jd_wxCompleteInfo.py jd_joinCommon_opencard.py 的活动链接参数缺少问题 135 | > 2022-11-10 19:03 136 | > 添加脚本黑名单 /prohibit 名称 137 | > 修复链接转换参数 https_txt,异常问题: missing ), unterminated subpattern at position 0 报错 138 | > 2022-11-11 16:46 139 | > 修复重复参数标记物和线报出现 https://cjhydz-isv.isvjcloud.com&a7de573f565848dab15be18bae764aedexport 问题 140 | 版本3.1 141 | > 取消自动适配改用对任务列表解包统一json文件格式 142 | > 减少循环次数,优化了程序执行所需要的时间损耗 143 | > 同步脚本更改每12个小时同步一次 144 | > 清理重复参数更改12个小时清理一次 145 | > 合并清理和获取为一个函数 146 | > 2022-11-16 18:00 147 | > 修复禁用活动任务 148 | > 2022-11-16 21:00 149 | > 添加禁用重复任务 150 | > 修补 task 脚本 这种没有库的无法匹配问题 151 | > 不支持中文(此问题后期不会修复) 152 | > 2022-11-16 22:30 153 | > 修复NOT开头重复执行参数不执行问题 154 | > Administrator 用户ID正式启用 自己去配置文件填写,填写Administrator 的用户需要重启容器,暂时不能动态获取Administrator的值 155 | > 添加 Administrator 的用户遇到 NOT重复执行参数将会发送TG消息通知,一般七日签到等长期活动 156 | > 2022-11-16 23:10 157 | > 修复去重复关键字为空问题 158 | > 2022-11-17 12:00 159 | > 填充活动参数反转链接的数据库支持数量 160 | > 2022-11-18 11:00 161 | > 添加机器人退出群聊 162 | > 所有交互命令全部在设置Administrator的前提下触发 163 | > 2022-11-18 18:30 164 | > 修改数据库表 165 | > 优化了之前无脑使用查询sql 166 | > 2022-11-18 21:00 167 | > 修补export yhyauthorCode 转换链接引起的https_txt,异常问题 168 | > 2022-11-18 21:40 169 | > 新增加对 jd_lzkj_loreal_invite.js == 邀请入会有礼(lzkj_loreal)和 jd_jinggeng_showInviteJoin.js == 邀请入会赢好礼(京耕)脚本支持 170 | > 2022-11-19 21:00 171 | > 因多任务并发出现493问题暂时在零点设置延迟90秒(后面会优化) 172 | > 添加管理员权限请出群聊(未启用,只是开放了接口) 173 | > 2022-11-20 13:30 174 | > 弃用judge 175 | > 修改获取脚本的sql执行逻辑 176 | > 不清楚什么原因造成卡任务添加无关紧要输出 177 | > 卡任务未知 178 | > 2022-11-20 19:30 179 | > main_core方法使用多线程,不阻塞tg机器人交互 180 | > 2022-11-21 10:30 181 | > 对长连接以知异常明细划分 [Errno -3] Try again 异常不会再暂停10s 182 | 3.2版本(重新拉镜像) 183 | > 支持多容器 184 | > 修改青龙存储密钥方式 185 | > 前端页面提交修改 186 | > 添加提交青龙指令 187 | > 添加自动删除异常青龙功能 188 | > 登陆页面(暂时借用代理的登陆页面) 189 | > 更换容器和启动文件名称容器自动编译 190 | > 修复log显示不出来问题和js和css部分404问题 191 | > 493问题暂时没有修复 192 | > 修复在任务不执行而标记物添加问题 193 | 2022-11-24 20:30 194 | > 修复群聊下非Administrator用户发送/id触发异常问题 195 | > 零点延迟 23秒 平常延迟 3秒 196 | 2022-12-01 03:40(不保留文件更新) 197 | > 添加动态日志 198 | > 修复一些BUG 199 | > 检测用户提交的是否为ID 200 | > 更新数据库内容 201 | 2022-12-01 14:00 202 | > 对 jd_wdz.js jd_wdzfd.js jd_wdz.py 进行不去重复处理 203 | > 修复线报jd_wdzfd.js中掺杂export问题 204 | 2022-12-01 16:00(不保留文件更新) 205 | > 适配保护环境库脚本 206 | 2022-12-02 20:00 207 | > 添加云端数据库,本次重启项目都会获取新的数据库 208 | > 修复日志500错误 209 | 2022-12-07 18:30 210 | > 支持微定制转换URL 211 | 2022-12-08 00:30 212 | > 修复微定制转换URL 213 | 2022-12-08 02:00 214 | > 对多个相同参数值同一行只能识别一个问题 215 | 2022-12-08 03:00 216 | > 修复sh类型链接跳过问题 217 | 2022-12-10 02:00 218 | > 重启后保留1200秒之前的线报 219 | 2022-12-20 00:40 220 | > 修补数据库和转换链接和船长脚本店铺抽豆 221 | > 转发线报的保留更新非转发的重启就行 222 | 2022-12-20 21:21 223 | > 对接 jd_convert_json.py 店铺签到 https://github.com/XgzK/JD_annex/blob/master/jd_convert_json.py 224 | > 12个小时自动清理一次日志 225 | > 2022-12-30 03:51 226 | > 更换容器使用 python10 + nodejs 227 | > 使用 nuitka3 编译加密 228 | > 2023-1-4 1:09 229 | > 取消添加参数重启无法同步问题 230 | > 前端代码分离出来单独Web文件夹 231 | 3.3版本(不保留配置文件更新) 232 | 解决493问题 233 | 对同一脚本增加延迟时间 234 | 不再直接修改配置文件,改成引入qlva.sh文件 235 | 对 https://shop.m.jd.com/shop/lottery?shopId=585437 自动获取 venderId值 236 | 23/1/12 10:08 237 | 尝试修补队列任务不释放问题 238 | 23/1/12 12:08 239 | 修补wdz匹配问题 240 | 23/1/13 13:00 241 | 修补转链接BUG 242 | 优化用一脚本执行速度 243 | 23/1/13 21:40 244 | 修复一些小问题 245 | 23/1/15 18:00 246 | 优化去重复关键字提取问题 247 | 优化去重复关键字垃圾代码可读性优化 248 | 23/1/25 15/00 249 | 对链接前面添加NOT或者RUN都会跳过重复执行脚本 250 | RUNhttps://shop.m.jd.com/shop/lottery?shopId=645139&venderId=648822&channel=406&venderType=0 251 | RUNexport jd_zdjr_activityId="90552d49457d4572a1d66fd3b04b9150" 252 | 3.4重写版本 253 | 对代码进行逻辑方面重写,修补上面版本各种BUG问题 254 | 对垃圾代码进行逻辑的升华 255 | 减少时间上执行上损耗 256 | 23/1/30 12/00 257 | 尝试修复转发问题429问题 258 | 23/1/30 19/00(稳定版本) 259 | HTTPSConnectionPool异常问题修复 260 | 对转发进行去重复进行调整 261 | 23/2/1 18/00 262 | 添加delay指令脚本执行后延迟秒数 263 | /delay 秒 脚本执行结束多少秒执行下一个脚本(23/2/1 18/00版本添加指令) 264 | 23/2/2 19/00 265 | 修补非文本消息异常问题 266 | 23/2/19 14/40 267 | 对一个小时前旧消息进行过滤 268 | 对相同活动键进行多个返回 269 | 23/4/3 13/00 270 | 对部分采用异步方法 271 | 修复一些小问题 272 | ``` 273 | ### 插件 274 | ```text 275 | 添加解析 店铺抽豆 解析插件 com.Plugin.lottery 276 | ``` 277 | 278 | ### 活动逻辑改变 279 | ```text 280 | 把活动分成纯链接和活动变量 281 | 获取任务的脚本 282 | 传递去队列 283 | 如果有 284 | 执行 285 | 如果无 286 | 转换成链接 287 | 再把链接转换成多变量 288 | 一个个尝试如果有立刻停止 289 | ``` 290 | -------------------------------------------------------------------------------- /conn/Plugin/lottery.py: -------------------------------------------------------------------------------- 1 | import re 2 | import time 3 | 4 | import requests 5 | 6 | 7 | class Lottery: 8 | def __init__(self): 9 | self.headers = [ 10 | { 11 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8," 12 | "application/signed-exchange;v=b3;q=0.9", 13 | "Accept-Encoding": "gzip, deflate, br", 14 | "Accept-Language": "zh-CN,zh;q=0.9", 15 | "Connection": "keep-alive", 16 | "Cookie": "unionuuid=V2_ZgUbCkdeRBQnCkQBfhpcUW4HRV1GVUYccF1CUnwRXgIIABNeQVdDFn0IQ1d6G1xqZwoRQkJXSgp2CkVLexhZ; " 17 | "language=zh_CN; __jda=123.16697028320441614753532.1669702832.1669702832.1669702832.2; __jdc=123; " 18 | "mba_muid=16697028320441614753532; share_cpin=; share_open_id=; share_gpin=; channel=; source_module=; " 19 | "erp=; visitkey=86254801803499; " 20 | "CSID=HT06HnkCWFpTT1ZeWRReT1A0IH10KAUKEApVA0wJDFZqeHZ4dHx3chxTUi1TWlRXWnZnYA5fRBRqZBhxXFtZOkdbXENHX09GZ3V6antWWg%3d%3d; unpl=JF8EAK9jNSttWx8BV0lXHkEXHFUGW10KSh8LbWcGUQoIGVcMGwJMEhR7XlVdXhRLFx9sbxRUWFNIXQ4fASsSEHteVVxcD0sUA2tgNVJdX0xXBxgyGxMRSlVXWlwIQicAb1cEZF1fTFwDGgESEBNKWFFeWghOEQdtYwdXbVl7VA0cMisiEXtdV1tYCXsWMy4JBVdbWU9QBxNPGxUXQ1tVXVQKSBYGamcCVFheT1YBGQErEyBI; CCC_SE=ADC_5n%2bm8fzTbtjP5EQGzzCLf1tPCxuHgOnfLUTk5TYAuKgeAoVQnchfuynZh7ySu8G%2f2t%2flp8CEEs%2fPdD4uET8DHxc8Zy4ic5n6zs5ZMl3zDVrXjC%2bsJySLO7M0Rc%2fUrHuroduCAoOzNka6wfkOMLAFQtZyoS0NmnH785mdxDIQgYSAp6KNUOsKlpC4IASx2VLYie%2f50ZbHZkrdTcOU50yeOJpAuQHqhjbZOSg4JQDP9dlwIbRnhKm%2bLDG1Hhvj%2bo2aO62iUjfonivy6yY%2bPtUuUSbZD9Bl%2fGzX5YBQAJj831npTmd224SWLbpqaWN8A%2fXRcGBdscyWjpHR0sCttBET6HOrB52ugRJ7Qy11wXNrflDP8724dYIramxnlPrg2dXAvLIJ1CmxxHHznB00CY5hhaP2fhAXHr6wMKAoLk2tq2SOslF5v39B3N5nwtO%2fhJLRMcBABrjcqdEMycbCShYSZ2DtKBOlAG8lK6OXoU5OlzOfA9Bbras5jRaZposGFIubSRwy4FiJYftFSIKenQ3Yfw%3d%3d; __jdb=123.4.16697028320441614753532|2.1669702832; __jdv=123%7Ckong%7Ct_2030612156_7166232%7Cjingfen%7C2edccd4b6f9c4130993124fac2811f15%7C1669703333218; PPRD_P=LOGID.1669703333358.69622262; mba_sid=16697028329705207265371209400.4; __jd_ref_cls=MDownLoadFloat_TopOldExpo", 21 | "sec-ch-ua": '"Microsoft Edge";v="107", "Chromium";v="107", "Not=A?Brand";v="24"', 22 | "sec-ch-ua-mobile": "?0", 23 | "sec-ch-ua-platform": "Windows", 24 | "Sec-Fetch-Dest": "document", 25 | "Sec-Fetch-Mode": "navigate", 26 | "Sec-Fetch-Site": "none", 27 | "Sec-Fetch-User": "?1", 28 | "Upgrade-Insecure-Requests": "1", 29 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 " 30 | "Safari/537.36 Edg/107.0.1418.56 " 31 | }, 32 | { 33 | "accept": "*/*", 34 | "accept-encoding": "gzip, deflate, br", 35 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 36 | "Cookie": "unionuuid=V2_ZgUbCkdeRBQnCkQBfhpcUW4HRV1GVUYccF1CUnwRXgIIABNeQVdDFn0IQ1d6G1xqZwoRQkJXSgp2CkVLexhZ; language=zh_CN; __jda=123.16697028320441614753532.1669702832.1669702832.1669702832.2; __jdc=123; mba_muid=16697028320441614753532; share_cpin=; share_open_id=; share_gpin=; channel=; source_module=; erp=; visitkey=86254801803499; CSID=HT06HnkCWFpTT1ZeWRReT1A0IH10KAUKEApVA0wJDFZqeHZ4dHx3chxTUi1TWlRXWnZnYA5fRBRqZBhxXFtZOkdbXENHX09GZ3V6antWWg%3d%3d; unpl=JF8EAK9jNSttWx8BV0lXHkEXHFUGW10KSh8LbWcGUQoIGVcMGwJMEhR7XlVdXhRLFx9sbxRUWFNIXQ4fASsSEHteVVxcD0sUA2tgNVJdX0xXBxgyGxMRSlVXWlwIQicAb1cEZF1fTFwDGgESEBNKWFFeWghOEQdtYwdXbVl7VA0cMisiEXtdV1tYCXsWMy4JBVdbWU9QBxNPGxUXQ1tVXVQKSBYGamcCVFheT1YBGQErEyBI; CCC_SE=ADC_5n%2bm8fzTbtjP5EQGzzCLf1tPCxuHgOnfLUTk5TYAuKgeAoVQnchfuynZh7ySu8G%2f2t%2flp8CEEs%2fPdD4uET8DHxc8Zy4ic5n6zs5ZMl3zDVrXjC%2bsJySLO7M0Rc%2fUrHuroduCAoOzNka6wfkOMLAFQtZyoS0NmnH785mdxDIQgYSAp6KNUOsKlpC4IASx2VLYie%2f50ZbHZkrdTcOU50yeOJpAuQHqhjbZOSg4JQDP9dlwIbRnhKm%2bLDG1Hhvj%2bo2aO62iUjfonivy6yY%2bPtUuUSbZD9Bl%2fGzX5YBQAJj831npTmd224SWLbpqaWN8A%2fXRcGBdscyWjpHR0sCttBET6HOrB52ugRJ7Qy11wXNrflDP8724dYIramxnlPrg2dXAvLIJ1CmxxHHznB00CY5hhaP2fhAXHr6wMKAoLk2tq2SOslF5v39B3N5nwtO%2fhJLRMcBABrjcqdEMycbCShYSZ2DtKBOlAG8lK6OXoU5OlzOfA9Bbras5jRaZposGFIubSRwy4FiJYftFSIKenQ3Yfw%3d%3d; __jdb=123.4.16697028320441614753532|2.1669702832; __jdv=123%7Ckong%7Ct_2030612156_7166232%7Cjingfen%7C2edccd4b6f9c4130993124fac2811f15%7C1669703333218; PPRD_P=LOGID.1669703333358.69622262; mba_sid=16697028329705207265371209400.4; __jd_ref_cls=MDownLoadFloat_TopOldExpo", 37 | "dnt": "1", 38 | "origin": "https://shop.m.jd.com", 39 | "referer": "https://shop.m.jd.com/", 40 | "sec-ch-ua-mobile": "?0", 41 | "sec-ch-ua-platform": "Windows", 42 | "sec-fetch-dest": "empty", 43 | "sec-fetch-mode": "cors", 44 | "sec-fetch-site": "same-site", 45 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" 46 | } 47 | ] 48 | 49 | def urlJump(self, url) -> list: 50 | """ 51 | 短URL获取跳转后的长URL 52 | :param url: https://u.jd.com/odnXBMw 53 | :return: URL 54 | """ 55 | try: 56 | jump = requests.get(url=url, headers=self.headers[0], allow_redirects=False) 57 | jump.close() 58 | if jump.status_code == 200: 59 | return re.findall("hrl='(.*?)'", jump.text) 60 | else: 61 | return [] 62 | except Exception as e: 63 | print(f"短URL获取跳转后的长URL异常 {e}") 64 | return [] 65 | 66 | def url302(self, url) -> list: 67 | """ 68 | 302跳转获取店铺 shopId 69 | :param url: 70 | :return: [json] [] 71 | """ 72 | try: 73 | getId = requests.get(url=url, headers=self.headers[0], allow_redirects=False) 74 | if getId.status_code == 302: 75 | location = getId.headers['location'].replace("https://shop.m.jd.com/?", "") 76 | if location: 77 | cc = {} 78 | for i in location.split('&'): 79 | a = i.split('=') 80 | cc.setdefault(a[0], a[1]) 81 | return [cc] 82 | else: 83 | return [] 84 | except Exception as e: 85 | print(f"短URL获取跳转后的长URL异常 {e}") 86 | return [] 87 | 88 | def getvenderId(self, js) -> str: 89 | """ 90 | 获取店铺的 venderId 91 | :param js: URL中部分参数 92 | :return: 93 | """ 94 | try: 95 | url = 'https://api.m.jd.com/client.action?functionId=whx_getMShopOutlineInfo&body={"cu":"true",' + f'"shopId":"{js["shopId"]}","utm_campaign":"{js["utm_campaign"]}","utm_medium":"{js["utm_medium"]}","utm_source":"{js["utm_source"]}","utm_term":"{js["utm_term"]}","source":"m-shop"' + '}&' + f't={int(time.time())}337&appid=shop_view&clientVersion=11.0.0&client=wh5&area=1_72_2799_0&uuid="{int(time.time()) - 12121355344}360960026"' 96 | vender = requests.get(url, headers=self.headers[1], allow_redirects=False) 97 | if vender.status_code == 200: 98 | venderId = vender.json()['data']['shopInfo']['venderId'] 99 | shopId = vender.json()['data']['shopInfo']['shopId'] 100 | return f"https://shop.m.jd.com/shop/lottery?shopId={shopId}&venderId={venderId}" 101 | else: 102 | return '' 103 | except Exception as e: 104 | print(f"获取店铺的 venderId 异常 {e}") 105 | return "" 106 | 107 | def main_lottery(self, url) -> str: 108 | """ 109 | 执行这个类的方法 110 | :param url: https://u.jd.com/odnXBMw 111 | :return: https://shop.m.jd.com/shop/lottery?shopId=10183543&venderId=10327499 112 | """ 113 | jump = self.urlJump(url) 114 | if not jump: 115 | return '' 116 | ur302 = self.url302(jump[0]) 117 | if not ur302: 118 | return '' 119 | return self.getvenderId(ur302[0]) 120 | 121 | def get_venderId(self, shopId) -> str: 122 | """ 123 | 获取店铺的 venderId 124 | :param shopId: URL中部分参数 125 | :return: 126 | """ 127 | try: 128 | url = 'https://api.m.jd.com/client.action?functionId=whx_getMShopOutlineInfo&body={"cu":"true",' + f'"shopId":"{shopId}","source":"m-shop"' + '}&' + f't={int(time.time())}337&appid=shop_view&clientVersion=11.0.0&client=wh5&area=1_72_2799_0&uuid="{int(time.time()) - 12121355344}360960026"' 129 | vender = requests.get(url, headers=self.headers[1], allow_redirects=False) 130 | if vender.status_code == 200: 131 | venderId = vender.json()['data']['shopInfo']['venderId'] 132 | return f"&venderId={venderId}" 133 | else: 134 | return "" 135 | except Exception as e: 136 | print(f"获取店铺的 venderId 异常 {e}") 137 | return "" -------------------------------------------------------------------------------- /conn/bots/getUpdate.py: -------------------------------------------------------------------------------- 1 | """ 2 | 长链接请求,后期处理 3 | """ 4 | import json 5 | import re 6 | 7 | import requests 8 | from requests import ConnectTimeout 9 | 10 | from conn.Template.ancestors import Father 11 | 12 | 13 | class GetUpdate(Father): 14 | def __init__(self): 15 | super().__init__() 16 | self.flash_Config() 17 | self.url = self.AdReg.get('Proxy')["TG_API_HOST"] 18 | self.Token = "/bot" + self.AdReg.get('Token') 19 | self.headers = {"Content-Type": "application/json", 20 | "Connection": "close", 21 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 22 | } 23 | self.data = { 24 | "offset": 0, 25 | "timeout": 100 26 | } 27 | self.proxies = self.AdReg.get('Proxy')['Proxy'] if self.AdReg.get('Proxy')['Proxy'] else None 28 | self.offset = None 29 | self.status = ["left", "member", "administrator", "creator"] 30 | 31 | def http_post(self, url, data): 32 | """ 33 | 发送请求 34 | :param url: /xxx 35 | :param data: 36 | :return: 37 | """ 38 | try: 39 | if not re.findall('(/bot\w+)', self.Token): 40 | self.Update() 41 | resp = requests.post(url=self.url + self.Token + url, data=json.dumps(data), timeout=200, headers=self.headers, proxies={"https": self.proxies, "http": self.proxies}) 42 | code = resp.status_code 43 | # 502 和409表示没有消息 44 | if code in [502, 409]: 45 | return [200, {"ok": True, "result": []}] 46 | elif code == 404: 47 | return [code, {"ok": False, "result": [f'404: {resp.text}']}] 48 | resp_js = resp.json() 49 | resp.close() 50 | if code == 200: 51 | return [code, resp_js] 52 | else: 53 | return [code, resp_js] 54 | except Exception as e: 55 | return [0, {'ok': False, 'result': [e]}] 56 | 57 | async def get_long_link(self, offset: int = None, limit: int = 100, timeout: int = 0, 58 | allowed_updates: list = None): 59 | """ 60 | 长链接 61 | :param offset: Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. All previous updates will forgotten. 62 | :param limit: Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100. 63 | :param timeout: Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, short polling should be used for testing purposes only. 64 | :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. 65 | 66 | Please note that this parameter doesn't affect updates created before the call to the getUpdates, so unwanted updates may be received for a short period of time. 67 | :return: 68 | """ 69 | try: 70 | data = { 71 | 'offset': self.offset, 72 | 'limit': limit, 73 | 'timeout': timeout, 74 | 'allowed_updates': allowed_updates 75 | } 76 | resp = requests.post(url=self.url + self.Token + '/getUpdates', data=json.dumps(data), 77 | proxies={"https": self.proxies, "http": self.proxies}, headers=self.headers) 78 | code = resp.status_code 79 | # 502 和409表示没有消息 80 | if code in [502, 409]: 81 | return [200, {"ok": True, "result": []}] 82 | elif code == 404: 83 | return [code, {"ok": False, "result": [f'404: {resp.text}']}] 84 | resp_js = resp.json() 85 | resp.close() 86 | if code == 200: 87 | # return [code, resp_js] 88 | if resp_js['ok']: 89 | if len(resp_js['result']) > 0: 90 | self.offset = resp_js['result'][len(resp_js['result']) - 1]['update_id'] + 1 91 | return resp_js['result'] 92 | else: 93 | self.log_write( 94 | f'conn.bots.getUpdate.GetUpdate.get_long_link状态码: {code} 发生异常事件: {resp_js["result"][0]}', 95 | level='error') 96 | return [] 97 | else: 98 | return [] 99 | except ConnectTimeout as e: 100 | self.log_write( 101 | f"conn.bots.getUpdate.GetUpdate.get_long_link 异常: {e}, 连接超时请确保能使用代理或者反代或直连 进行使用", 102 | level='error') 103 | return [] 104 | except Exception as e: 105 | self.log_write(f"conn.bots.getUpdate.GetUpdate.get_long_link 异常: {e}", level='error') 106 | return [] 107 | 108 | def send_message(self, chat_id: str, text: str): 109 | """ 110 | 发送消息 111 | :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) 112 | :param text: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only 113 | :return: 114 | """ 115 | try: 116 | send = self.http_post('/sendMessage', {"chat_id": chat_id, "text": text}) 117 | if send[0] == 200: 118 | return send[0] 119 | elif send[0] == 403: 120 | self.log_write( 121 | f"conn.bots.getUpdate.GetUpdate.send_message转发消息失败,机器人不在你转发的频道或者群组\n状态码{send[0]}\n失败原因{send[1]}", 122 | level='error') 123 | return send[0] 124 | elif send[0] == 400: 125 | self.log_write( 126 | f"conn.bots.getUpdate.GetUpdate.send_message转发消息失败,可能问题权限不足\n状态码{send[0]}\n失败原因{send[1]}", 127 | level='error') 128 | return send[0] 129 | else: 130 | self.log_write( 131 | f"conn.bots.getUpdate.GetUpdate.send_message转发消息失败\n状态码{send[0]}\n失败原因{send[1]}", 132 | level='error') 133 | return send[0] 134 | except Exception as e: 135 | self.log_write(f"conn.bots.getUpdate.GetUpdate.send_message发送消息异常: {e}", level='error') 136 | return [0] 137 | 138 | def banChatMember(self, chat_id, user_id): 139 | """ 140 | 踢出群聊 141 | :param chat_id: 群标识 142 | :param user_id: 踢出标识 143 | :return: 144 | """ 145 | try: 146 | send = self.http_post('/banChatMember', {"chat_id": chat_id, "user_id": user_id}) 147 | if send[0] == 200: 148 | return 0 149 | elif send[0] == 403: 150 | self.log_write(f"踢出群聊{send[0]}", level='error') 151 | elif send[0] == 400: 152 | self.log_write(f"踢出群聊失败,可能问题权限不足\n状态码{send[0]}\n失败原因{send[1]}", level='error') 153 | else: 154 | self.log_write(f"踢出群聊失败\n状态码{send[0]}\n失败原因{send[1]}", level='error') 155 | return [] 156 | except Exception as e: 157 | self.log_write(f"踢出群聊异常: {e}", level='error') 158 | 159 | def getChatMember(self, chat_id, user_id): 160 | """ 161 | 获取用户是否再群组 162 | :param chat_id: 群标识 163 | :param user_id: 成员 164 | :return: json 165 | """ 166 | try: 167 | send = self.http_post('/getChatMember', {"chat_id": chat_id, "user_id": user_id}) 168 | if send[0] == 200: 169 | return send[1]['result'] 170 | elif send[0] == 403: 171 | self.log_write(f"获取用户是否再群组 {send[0]} ", level='error') 172 | elif send[0] == 400: 173 | self.log_write(f"获取ID {user_id} 失败 状态码 {send[0]} \n失败原因 {send[1]}", level='error') 174 | else: 175 | self.log_write(f"获取ID {user_id} 失败 状态码 {send[0]} \n失败原因 {send[1]}", level='error') 176 | return [] 177 | except Exception as e: 178 | self.log_write(f"获取用户是否再群组异常: {e}", level='error') 179 | 180 | def leaveChat(self, chat_id): 181 | """ 182 | 使用此方法让您的机器人离开组、超级组或频道。成功返回True 183 | :return: 184 | """ 185 | try: 186 | ur = self.http_post(url='/leaveChat', data={"chat_id": chat_id}) 187 | if ur[0] == 200: 188 | self.send_message(f"退出 {chat_id} 群聊成功", self.AdReg.get('Administrator')) 189 | return 0 190 | else: 191 | self.send_message(f"退出 {chat_id} 失败 失败原因 {ur[1]}", self.AdReg.get('Administrator')) 192 | return 400 193 | except Exception as e: 194 | return -1 195 | 196 | def getChat(self, chat_id) -> dict: 197 | """ 198 | 使用此方法根据ID获取群信息 199 | :return: 200 | """ 201 | try: 202 | ur = self.http_post(url='/getChat', data={"chat_id": chat_id}) 203 | if ur[0] == 200 and ur[0]['ok']: 204 | return ur[0] 205 | else: 206 | return ur[0] 207 | except Exception as e: 208 | return { 209 | "ok": False, 210 | "error_code": 500, 211 | "description": e 212 | } 213 | 214 | def Update(self): 215 | """ 216 | 更新GetUpdate和父类 217 | :return: 218 | """ 219 | self.flash_Config() 220 | self.url = self.AdReg.get('Proxy')["TG_API_HOST"] 221 | self.Token = "/bot" + self.AdReg.get('Token') 222 | self.proxies = self.AdReg.get('Proxy')['Proxy'] if self.AdReg.get('Proxy')['Proxy'] else None 223 | -------------------------------------------------------------------------------- /conn/mission/sundries.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import re 3 | from queue import Queue 4 | from typing import Any 5 | 6 | from conn.Plugin.lottery import Lottery 7 | from conn.Template.ancestors import Father 8 | from conn.Template.jdql import JdQl 9 | from conn.bots.interaction import Interaction 10 | from conn.tools.log import LoggerClass 11 | from conn.tools.sql import Sql 12 | 13 | q = Queue() 14 | 15 | 16 | class Sundries(Father): 17 | 18 | def __init__(self): 19 | """ 20 | 处理杂物的类 21 | """ 22 | super().__init__() 23 | self.q = q 24 | self.flash_Config() 25 | self.sql = Sql() 26 | self.lottery = Lottery() 27 | self.logger = LoggerClass() 28 | self.li = ['jd', 'pr', 'co', 'ji', 'sh', 'tx', 'wq'] 29 | self.Markings = ["RUN", "NOT"] 30 | self.interaction = Interaction() 31 | 32 | def looking(self, text_str: str) -> list[JdQl] | list[Any]: 33 | """ 34 | 用户传入变量返回脚本库 35 | :param text_str: 36 | :return: JdQl类或None 37 | """ 38 | value1 = self.sql.selectAll(table=self.sql.surface[0], 39 | where=f'jd_value1="NOT{text_str}" or jd_value1="{text_str}" ' 40 | f'or jd_value2="{text_str}" ' 41 | f'or jd_value3="{text_str}"') 42 | if type(value1) == list: 43 | vale_list = [] 44 | for vl in value1: 45 | vale_list.append(JdQl(vl)) 46 | return vale_list 47 | else: 48 | return [] 49 | 50 | def https_txt(self, http) -> list[[str, JdQl]]: 51 | """ 52 | 处理链接类型转成活动 53 | :param http: 待处理的数据 54 | :return: [] 55 | """ 56 | try: 57 | lis = [] 58 | http = self.sh_venderId(http.replace('"', "")) 59 | 60 | # 先查询是否存有这个链接 61 | li = self.fuzzy_query(http) 62 | if not li: 63 | self.logger.write_log( 64 | f"conn.mission.sundries.Sundries.https_txt,没有查询到的链接是: {http}") 65 | return [] 66 | 67 | # 遍历数组 68 | for ink in li: 69 | tx = re.findall(f'{ink.jd_re}', http) 70 | if not tx: 71 | self.logger.write_log( 72 | f"conn.mission.sundries.Sundries.https_txt,匹配不到内容: {ink.jd_re} 链接是: {http}") 73 | continue 74 | st2 = '' 75 | # 往后推 76 | sun = 0 77 | # 拼接数组 78 | for i in tx[0] if type(tx[0]) == tuple else tx: 79 | if type(list) and len(i) == 2 and ink.jd_value2 is None: 80 | st2 += ink.Change[sun] + "=" + f'\"{i[0]}{ink.partition if ink.partition else ""}{i[1]}\";' 81 | sun += 1 82 | else: 83 | if ink.Change[sun] is None: 84 | st2 = st2.replace('";', '') 85 | st2 += ink.partition + str(i) + '";' 86 | else: 87 | st2 += ink.Change[sun] + "=" + f'"{i}";' 88 | sun += 1 89 | if st2: 90 | TYPE = re.findall("https://(\w{2})", http)[0] 91 | st2 += f'export NOT_TYPE="{TYPE}";' 92 | lis.append([st2, ink]) 93 | return lis 94 | except Exception as e: 95 | self.logger.write_log(f"conn.mission.sundries.Sundries.https_txt,异常问题: {e} 活动链接 {http}") 96 | return [] 97 | 98 | def sh_venderId(self, url=None): 99 | """ 100 | 检测是否需要获取 venderId 101 | :param url: 102 | :return: 103 | """ 104 | try: 105 | shven = re.findall("https://shop\.m\.jd\.com/shop/lottery.*?shopId=(\d+)$", url) 106 | if shven: 107 | return url + self.lottery.get_venderId(shven[0]) 108 | else: 109 | return url 110 | except Exception as e: 111 | self.logger.write_log(f"conn.mission.sundries.Sundries.sh_venderId异常 {e}") 112 | return url 113 | 114 | def fuzzy_query(self, url: str) -> list[JdQl]: 115 | """ 116 | 模糊查询,没有就打印日志让管理者添加 117 | :param url: 查询的url带问号后面的内容,如果没有就传None 118 | :return: 返回数据库 or [] 119 | """ 120 | try: 121 | li1s = [] 122 | TYPE = re.findall("https://(.{2})", url)[0] 123 | # 读取数据库中活动全部链接的数据 124 | lines = self.sql.selectAll(table=self.sql.surface[0], 125 | where=f'jd_type == "{TYPE}"') if TYPE in self.li else self.sql.selectAll( 126 | table=self.sql.surface[0], where=f'jd_type == "{TYPE}" or jd_type == "cl"', order="id DESC") 127 | # 遍历数据库正则表达式非空 128 | if type(lines) == list: 129 | for i in lines: 130 | i = JdQl(i) 131 | try: 132 | zbds = re.findall(i.jd_url, url) 133 | if zbds: 134 | li1s.append(i) 135 | else: 136 | # 如果没有就清理 137 | del i 138 | except Exception as e: 139 | self.logger.write_log( 140 | f"conn.mission.sundries.Sundries.fuzzy_query 在对比数据库中出现异常: {e} 触发异常的值是 {url} 数据库值的脚本名称是 {i[2]}") 141 | if li1s: 142 | return li1s 143 | self.logger.write_log("模糊查询中: " + str(url) + " 没有找到,请添加") 144 | return li1s 145 | except Exception as e: 146 | self.logger.write_log( 147 | "conn.mission.sundries.Sundries.fuzzy_query,异常问题: " + str(e) + "异常的值是: " + url) 148 | return [] 149 | 150 | def turn_url(self, export: str): 151 | """ 152 | 参数转连接 153 | :param export: 活动参数 154 | :return: 155 | """ 156 | export = re.sub("[()'`;\"*]+(?:export NOT_TYPE=\".*?\";)", "", export) 157 | ex_sun = re.findall('(export \w+)=', export) 158 | 159 | jsva = ''.join( 160 | f"{'{0}{1}' + str(ex_sun[i]) + '{1} or ' if i != len(ex_sun) - 1 else '{0}{1}' + str(ex_sun[i]) + '{1}'} " 161 | for i 162 | in 163 | range(len(ex_sun))).format('export1=', '"') 164 | 165 | sq = self.sql.selectAll(table=self.sql.surface[2], where=f"{jsva}") 166 | 167 | # 返回的有数组 168 | if sq: 169 | for jd_va in sq: 170 | 171 | if jd_va[2] and len(ex_sun) == 2: 172 | try: 173 | return [ 174 | str(jd_va[0]).replace('#0', re.findall('activityUrl="([A-Za-z0-9&_/:.-]{5,})"', export)[-1]) 175 | .replace('#1', re.findall('activityId="(\w+)"', export)[-1])] 176 | except: 177 | pass 178 | # 参数2没有 179 | elif not jd_va[2] and len(ex_sun) == 1: 180 | lis = [] 181 | sun = 0 182 | st = "" 183 | ex_tx = export.split('=') 184 | # 进入这里表示只需要一个值 185 | points = ex_tx[1].split(jd_va[4]) if jd_va[4] else [ex_tx[1]] 186 | su = len(jd_va[0].split('#')) 187 | for son in set(points): 188 | if su > 2: 189 | st += str(jd_va[0]).replace(f"#{sun}", re.findall('(\w+)', son)[-1]) 190 | if sun == su - 1: 191 | lis.append(st) 192 | else: 193 | lis.append(str(jd_va[0]).replace("#0", re.findall('(\w+)', son)[-1])) 194 | sun += 1 195 | return lis 196 | return [] 197 | else: 198 | # 没有返回空 199 | return [] 200 | 201 | def contrast(self, str12: dict): 202 | """ 203 | 去除掉相同脚本参数,如果脚本相同只执行一次 204 | :param str12: 活动参数 205 | :return: NOT关键字返回 [0] 执行过返回 [1, 关键字] 没有执行过 [2, 关键字] 没有识别 [3] 异常 [-1] 执行 206 | """ 207 | try: 208 | if str12["marking"] in self.Markings: 209 | return [0] 210 | 211 | # 提取链接类型关键字 212 | keywords_url1 = re.findall("(?:activityId|configCode|actId|user_id|shopId|a|token)=\"?(\w+)", 213 | str12["activities"], re.S) 214 | if keywords_url1: 215 | inquire = 1 if self.sql.selectTopone(table=self.sql.surface[1], 216 | where=f"jd_value1='{keywords_url1[0]}'") else 2 217 | return [inquire, keywords_url1[0]] 218 | 219 | # 提取特殊链接类型 220 | keywords_url2 = re.findall("(?:id|code|Id|activityUrl)=\"?(\w+)", str12["activities"], re.S) 221 | if keywords_url2: 222 | inquire = 1 if self.sql.selectTopone(table=self.sql.surface[1], 223 | where=f"jd_value1='{keywords_url2[0]}'") else 2 224 | return [inquire, keywords_url2[0]] 225 | 226 | # 提取变量非链接类型 227 | keywords_url3 = re.findall("=\"([a-zA-Z0-9&]+)", str12["activities"], re.S) 228 | if keywords_url3: 229 | inquire = 1 if self.sql.selectTopone(table=self.sql.surface[1], 230 | where=f"jd_value1='{keywords_url3[0]}'") else 2 231 | return [inquire, keywords_url3[0]] 232 | return [3] 233 | except Exception as e: 234 | self.logger.write_log('去掉相同活动异常: ', e) 235 | return [-1] 236 | 237 | def ql_write(self, data: dict, essential: list): 238 | """ 239 | 写入青龙任务配置文件 240 | :param data: 传入内容 241 | :param essential: 添加进重复数据库的关键字 242 | :return: 如果没有执行过返回0,如果执行过返回-1 243 | """ 244 | try: 245 | if self.AdReg.get('deduplication') == 1: 246 | return 0 247 | # 0表示不去重复 248 | elif data["marking"] == "NOT": 249 | self.interaction.for_message(f"NOT表示属于不去重复关键字(未开发功能): \n{data['activities']}", False) 250 | return 0 251 | elif data["marking"] == "RUN": 252 | return 0 253 | elif essential[0] == 2: 254 | self.sql.insert(table=self.sql.surface[1], jd_value1=f"{essential[1]}", 255 | jd_data=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 256 | return 0 257 | except Exception as e: 258 | self.logger.write_log("ql_write,异常信息:" + str(e)) 259 | return -1 260 | 261 | def ql_compared(self, jst: str, ql_ck: tuple) -> list: 262 | """ 263 | 遍历青龙任务来对比,获取任务ID 264 | :param jst: 脚本名称 265 | :param ql_ck: 青龙数据库 266 | :return: ID or -1 267 | """ 268 | try: 269 | jstx = self.read(ql_ck[5]) 270 | # 判断脚本时否存在,不存在直接返回 271 | if not (jst in jstx): 272 | return [-1] 273 | va1 = jstx[jst] 274 | # 判断用户时否需要优先执行特定库 task 库/脚本.js 275 | lis = list(va1.keys()) 276 | return [va1[lis[0]]['id']] 277 | except Exception as e: 278 | self.logger.write_log(f'查询任务异常信息: {e}') 279 | return [-1] 280 | 281 | def tx_compared(self, value1: list): 282 | """ 283 | 用于对比数据,由TG获取的文本对比数据库中的数据 284 | :return: 返回数组的脚本名称[0]和变量[1],异常返回-1 285 | """ 286 | try: 287 | # value1[1].toString() 288 | # [脚本, 活动, 时间, 关键字] 289 | self.q.put({ 290 | "jd_js": value1[1].jd_js, 291 | "activities": value1[2], 292 | "interval": value1[1].interval, 293 | "marking": value1[0] 294 | }) 295 | self.logger.write_log( 296 | f"脚本名称 {value1[1].jd_js} 脚本 {value1[1].jd_name} 加入队列任务, 当前队列任务剩余 {q.qsize()} 个") 297 | return 298 | except Exception as e: 299 | self.logger.write_log(f"conn.mission.sorting.Sorting.tx_compared 异常对比脚本异常信息信息: {e}") 300 | -------------------------------------------------------------------------------- /Web/static/js/socket.io.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Socket.IO v4.5.2 3 | * (c) 2014-2022 Guillermo Rauch 4 | * Released under the MIT License. 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).io=e()}(this,(function(){"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t,e){for(var n=0;nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,a=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return s=t.done,t},e:function(t){a=!0,o=t},f:function(){try{s||null==n.return||n.return()}finally{if(a)throw o}}}}var m=Object.create(null);m.open="0",m.close="1",m.ping="2",m.pong="3",m.message="4",m.upgrade="5",m.noop="6";var b=Object.create(null);Object.keys(m).forEach((function(t){b[m[t]]=t}));for(var k={type:"error",data:"parser error"},w="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),_="function"==typeof ArrayBuffer,O=function(t,e,n){var r,i=t.type,o=t.data;return w&&o instanceof Blob?e?n(o):A(o,n):_&&(o instanceof ArrayBuffer||(r=o,"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(r):r&&r.buffer instanceof ArrayBuffer))?e?n(o):A(new Blob([o]),n):n(m[i]+(o||""))},A=function(t,e){var n=new FileReader;return n.onload=function(){var t=n.result.split(",")[1];e("b"+t)},n.readAsDataURL(t)},E="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",R="undefined"==typeof Uint8Array?[]:new Uint8Array(256),T=0;T1?{type:b[n],data:t.substring(1)}:{type:b[n]}:k},S=function(t,e){if(C){var n=function(t){var e,n,r,i,o,s=.75*t.length,a=t.length,c=0;"="===t[t.length-1]&&(s--,"="===t[t.length-2]&&s--);var u=new ArrayBuffer(s),h=new Uint8Array(u);for(e=0;e>4,h[c++]=(15&r)<<4|i>>2,h[c++]=(3&i)<<6|63&o;return u}(t);return N(n,e)}return{base64:!0,data:t}},N=function(t,e){return"blob"===e&&t instanceof ArrayBuffer?new Blob([t]):t},x=String.fromCharCode(30);function L(t){if(t)return function(t){for(var e in L.prototype)t[e]=L.prototype[e];return t}(t)}L.prototype.on=L.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},L.prototype.once=function(t,e){function n(){this.off(t,n),e.apply(this,arguments)}return n.fn=e,this.on(t,n),this},L.prototype.off=L.prototype.removeListener=L.prototype.removeAllListeners=L.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n,r=this._callbacks["$"+t];if(!r)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var i=0;i1?e-1:0),r=1;r0);return e}function W(){var t=z(+new Date);return t!==F?(K=0,F=t):t+"."+z(K++)}for(;Y<64;Y++)H[V[Y]]=Y;function $(t){var e="";for(var n in t)t.hasOwnProperty(n)&&(e.length&&(e+="&"),e+=encodeURIComponent(n)+"="+encodeURIComponent(t[n]));return e}function J(t){for(var e={},n=t.split("&"),r=0,i=n.length;r0&&void 0!==arguments[0]?arguments[0]:{};return i(t,{xd:this.xd,xs:this.xs},this.opts),new nt(this.uri(),t)}},{key:"doWrite",value:function(t,e){var n=this,r=this.request({method:"POST",data:t});r.on("success",e),r.on("error",(function(t,e){n.onError("xhr post error",t,e)}))}},{key:"doPoll",value:function(){var t=this,e=this.request();e.on("data",this.onData.bind(this)),e.on("error",(function(e,n){t.onError("xhr poll error",e,n)})),this.pollXhr=e}}]),s}(U),nt=function(t){o(i,t);var n=p(i);function i(t,r){var o;return e(this,i),D(f(o=n.call(this)),r),o.opts=r,o.method=r.method||"GET",o.uri=t,o.async=!1!==r.async,o.data=void 0!==r.data?r.data:null,o.create(),o}return r(i,[{key:"create",value:function(){var t=this,e=j(this.opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");e.xdomain=!!this.opts.xd,e.xscheme=!!this.opts.xs;var n=this.xhr=new Q(e);try{n.open(this.method,this.uri,this.async);try{if(this.opts.extraHeaders)for(var r in n.setDisableHeaderCheck&&n.setDisableHeaderCheck(!0),this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(r)&&n.setRequestHeader(r,this.opts.extraHeaders[r])}catch(t){}if("POST"===this.method)try{n.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(t){}try{n.setRequestHeader("Accept","*/*")}catch(t){}"withCredentials"in n&&(n.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(n.timeout=this.opts.requestTimeout),n.onreadystatechange=function(){4===n.readyState&&(200===n.status||1223===n.status?t.onLoad():t.setTimeoutFn((function(){t.onError("number"==typeof n.status?n.status:0)}),0))},n.send(this.data)}catch(e){return void this.setTimeoutFn((function(){t.onError(e)}),0)}"undefined"!=typeof document&&(this.index=i.requestsCount++,i.requests[this.index]=this)}},{key:"onError",value:function(t){this.emitReserved("error",t,this.xhr),this.cleanup(!0)}},{key:"cleanup",value:function(t){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=Z,t)try{this.xhr.abort()}catch(t){}"undefined"!=typeof document&&delete i.requests[this.index],this.xhr=null}}},{key:"onLoad",value:function(){var t=this.xhr.responseText;null!==t&&(this.emitReserved("data",t),this.emitReserved("success"),this.cleanup())}},{key:"abort",value:function(){this.cleanup()}}]),i}(L);if(nt.requestsCount=0,nt.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",rt);else if("function"==typeof addEventListener){addEventListener("onpagehide"in P?"pagehide":"unload",rt,!1)}function rt(){for(var t in nt.requests)nt.requests.hasOwnProperty(t)&&nt.requests[t].abort()}var it="function"==typeof Promise&&"function"==typeof Promise.resolve?function(t){return Promise.resolve().then(t)}:function(t,e){return e(t,0)},ot=P.WebSocket||P.MozWebSocket,st="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase(),at=function(t){o(i,t);var n=p(i);function i(t){var r;return e(this,i),(r=n.call(this,t)).supportsBinary=!t.forceBase64,r}return r(i,[{key:"name",get:function(){return"websocket"}},{key:"doOpen",value:function(){if(this.check()){var t=this.uri(),e=this.opts.protocols,n=st?{}:j(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(n.headers=this.opts.extraHeaders);try{this.ws=st?new ot(t,e,n):e?new ot(t,e):new ot(t)}catch(t){return this.emitReserved("error",t)}this.ws.binaryType=this.socket.binaryType||"arraybuffer",this.addEventListeners()}}},{key:"addEventListeners",value:function(){var t=this;this.ws.onopen=function(){t.opts.autoUnref&&t.ws._socket.unref(),t.onOpen()},this.ws.onclose=function(e){return t.onClose({description:"websocket connection closed",context:e})},this.ws.onmessage=function(e){return t.onData(e.data)},this.ws.onerror=function(e){return t.onError("websocket error",e)}}},{key:"write",value:function(t){var e=this;this.writable=!1;for(var n=function(n){var r=t[n],i=n===t.length-1;O(r,e.supportsBinary,(function(t){try{e.ws.send(t)}catch(t){}i&&it((function(){e.writable=!0,e.emitReserved("drain")}),e.setTimeoutFn)}))},r=0;r1&&void 0!==arguments[1]?arguments[1]:{};return e(this,a),r=s.call(this),n&&"object"===t(n)&&(o=n,n=null),n?(n=ft(n),o.hostname=n.host,o.secure="https"===n.protocol||"wss"===n.protocol,o.port=n.port,n.query&&(o.query=n.query)):o.host&&(o.hostname=ft(o.host).host),D(f(r),o),r.secure=null!=o.secure?o.secure:"undefined"!=typeof location&&"https:"===location.protocol,o.hostname&&!o.port&&(o.port=r.secure?"443":"80"),r.hostname=o.hostname||("undefined"!=typeof location?location.hostname:"localhost"),r.port=o.port||("undefined"!=typeof location&&location.port?location.port:r.secure?"443":"80"),r.transports=o.transports||["polling","websocket"],r.readyState="",r.writeBuffer=[],r.prevBufferLen=0,r.opts=i({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!0},o),r.opts.path=r.opts.path.replace(/\/$/,"")+"/","string"==typeof r.opts.query&&(r.opts.query=J(r.opts.query)),r.id=null,r.upgrades=null,r.pingInterval=null,r.pingTimeout=null,r.pingTimeoutTimer=null,"function"==typeof addEventListener&&(r.opts.closeOnBeforeunload&&addEventListener("beforeunload",(function(){r.transport&&(r.transport.removeAllListeners(),r.transport.close())}),!1),"localhost"!==r.hostname&&(r.offlineEventListener=function(){r.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",r.offlineEventListener,!1))),r.open(),r}return r(a,[{key:"createTransport",value:function(t){var e=i({},this.opts.query);e.EIO=4,e.transport=t,this.id&&(e.sid=this.id);var n=i({},this.opts.transportOptions[t],this.opts,{query:e,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return new ct[t](n)}},{key:"open",value:function(){var t,e=this;if(this.opts.rememberUpgrade&&a.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))t="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((function(){e.emitReserved("error","No transports available")}),0);t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)}},{key:"setTransport",value:function(t){var e=this;this.transport&&this.transport.removeAllListeners(),this.transport=t,t.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(function(t){return e.onClose("transport close",t)}))}},{key:"probe",value:function(t){var e=this,n=this.createTransport(t),r=!1;a.priorWebsocketSuccess=!1;var i=function(){r||(n.send([{type:"ping",data:"probe"}]),n.once("packet",(function(t){if(!r)if("pong"===t.type&&"probe"===t.data){if(e.upgrading=!0,e.emitReserved("upgrading",n),!n)return;a.priorWebsocketSuccess="websocket"===n.name,e.transport.pause((function(){r||"closed"!==e.readyState&&(f(),e.setTransport(n),n.send([{type:"upgrade"}]),e.emitReserved("upgrade",n),n=null,e.upgrading=!1,e.flush())}))}else{var i=new Error("probe error");i.transport=n.name,e.emitReserved("upgradeError",i)}})))};function o(){r||(r=!0,f(),n.close(),n=null)}var s=function(t){var r=new Error("probe error: "+t);r.transport=n.name,o(),e.emitReserved("upgradeError",r)};function c(){s("transport closed")}function u(){s("socket closed")}function h(t){n&&t.name!==n.name&&o()}var f=function(){n.removeListener("open",i),n.removeListener("error",s),n.removeListener("close",c),e.off("close",u),e.off("upgrading",h)};n.once("open",i),n.once("error",s),n.once("close",c),this.once("close",u),this.once("upgrading",h),n.open()}},{key:"onOpen",value:function(){if(this.readyState="open",a.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade&&this.transport.pause)for(var t=0,e=this.upgrades.length;t1))return this.writeBuffer;for(var t,e=1,n=0;n=57344?n+=3:(r++,n+=4);return n}(t):Math.ceil(1.33*(t.byteLength||t.size))),n>0&&e>this.maxPayload)return this.writeBuffer.slice(0,n);e+=2}return this.writeBuffer}},{key:"write",value:function(t,e,n){return this.sendPacket("message",t,e,n),this}},{key:"send",value:function(t,e,n){return this.sendPacket("message",t,e,n),this}},{key:"sendPacket",value:function(t,e,n,r){if("function"==typeof e&&(r=e,e=void 0),"function"==typeof n&&(r=n,n=null),"closing"!==this.readyState&&"closed"!==this.readyState){(n=n||{}).compress=!1!==n.compress;var i={type:t,data:e,options:n};this.emitReserved("packetCreate",i),this.writeBuffer.push(i),r&&this.once("flush",r),this.flush()}}},{key:"close",value:function(){var t=this,e=function(){t.onClose("forced close"),t.transport.close()},n=function n(){t.off("upgrade",n),t.off("upgradeError",n),e()},r=function(){t.once("upgrade",n),t.once("upgradeError",n)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(function(){t.upgrading?r():e()})):this.upgrading?r():e()),this}},{key:"onError",value:function(t){a.priorWebsocketSuccess=!1,this.emitReserved("error",t),this.onClose("transport error",t)}},{key:"onClose",value:function(t,e){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&removeEventListener("offline",this.offlineEventListener,!1),this.readyState="closed",this.id=null,this.emitReserved("close",t,e),this.writeBuffer=[],this.prevBufferLen=0)}},{key:"filterUpgrades",value:function(t){for(var e=[],n=0,r=t.length;n0;case Ot.ACK:case Ot.BINARY_ACK:return Array.isArray(n)}}}]),a}(L),Rt=function(){function t(n){e(this,t),this.packet=n,this.buffers=[],this.reconPack=n}return r(t,[{key:"takeBinaryData",value:function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=wt(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}},{key:"finishedReconstruction",value:function(){this.reconPack=null,this.buffers=[]}}]),t}(),Tt=Object.freeze({__proto__:null,protocol:5,get PacketType(){return Ot},Encoder:At,Decoder:Et});function Ct(t,e,n){return t.on(e,n),function(){t.off(e,n)}}var Bt=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1}),St=function(t){o(i,t);var n=p(i);function i(t,r,o){var s;return e(this,i),(s=n.call(this)).connected=!1,s.receiveBuffer=[],s.sendBuffer=[],s.ids=0,s.acks={},s.flags={},s.io=t,s.nsp=r,o&&o.auth&&(s.auth=o.auth),s.io._autoConnect&&s.open(),s}return r(i,[{key:"disconnected",get:function(){return!this.connected}},{key:"subEvents",value:function(){if(!this.subs){var t=this.io;this.subs=[Ct(t,"open",this.onopen.bind(this)),Ct(t,"packet",this.onpacket.bind(this)),Ct(t,"error",this.onerror.bind(this)),Ct(t,"close",this.onclose.bind(this))]}}},{key:"active",get:function(){return!!this.subs}},{key:"connect",value:function(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}},{key:"open",value:function(){return this.connect()}},{key:"send",value:function(){for(var t=arguments.length,e=new Array(t),n=0;n1?e-1:0),r=1;r0&&t.jitter<=1?t.jitter:0,this.attempts=0}Nt.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},Nt.prototype.reset=function(){this.attempts=0},Nt.prototype.setMin=function(t){this.ms=t},Nt.prototype.setMax=function(t){this.max=t},Nt.prototype.setJitter=function(t){this.jitter=t};var xt=function(n){o(s,n);var i=p(s);function s(n,r){var o,a;e(this,s),(o=i.call(this)).nsps={},o.subs=[],n&&"object"===t(n)&&(r=n,n=void 0),(r=r||{}).path=r.path||"/socket.io",o.opts=r,D(f(o),r),o.reconnection(!1!==r.reconnection),o.reconnectionAttempts(r.reconnectionAttempts||1/0),o.reconnectionDelay(r.reconnectionDelay||1e3),o.reconnectionDelayMax(r.reconnectionDelayMax||5e3),o.randomizationFactor(null!==(a=r.randomizationFactor)&&void 0!==a?a:.5),o.backoff=new Nt({min:o.reconnectionDelay(),max:o.reconnectionDelayMax(),jitter:o.randomizationFactor()}),o.timeout(null==r.timeout?2e4:r.timeout),o._readyState="closed",o.uri=n;var c=r.parser||Tt;return o.encoder=new c.Encoder,o.decoder=new c.Decoder,o._autoConnect=!1!==r.autoConnect,o._autoConnect&&o.open(),o}return r(s,[{key:"reconnection",value:function(t){return arguments.length?(this._reconnection=!!t,this):this._reconnection}},{key:"reconnectionAttempts",value:function(t){return void 0===t?this._reconnectionAttempts:(this._reconnectionAttempts=t,this)}},{key:"reconnectionDelay",value:function(t){var e;return void 0===t?this._reconnectionDelay:(this._reconnectionDelay=t,null===(e=this.backoff)||void 0===e||e.setMin(t),this)}},{key:"randomizationFactor",value:function(t){var e;return void 0===t?this._randomizationFactor:(this._randomizationFactor=t,null===(e=this.backoff)||void 0===e||e.setJitter(t),this)}},{key:"reconnectionDelayMax",value:function(t){var e;return void 0===t?this._reconnectionDelayMax:(this._reconnectionDelayMax=t,null===(e=this.backoff)||void 0===e||e.setMax(t),this)}},{key:"timeout",value:function(t){return arguments.length?(this._timeout=t,this):this._timeout}},{key:"maybeReconnectOnOpen",value:function(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}},{key:"open",value:function(t){var e=this;if(~this._readyState.indexOf("open"))return this;this.engine=new lt(this.uri,this.opts);var n=this.engine,r=this;this._readyState="opening",this.skipReconnect=!1;var i=Ct(n,"open",(function(){r.onopen(),t&&t()})),o=Ct(n,"error",(function(n){r.cleanup(),r._readyState="closed",e.emitReserved("error",n),t?t(n):r.maybeReconnectOnOpen()}));if(!1!==this._timeout){var s=this._timeout;0===s&&i();var a=this.setTimeoutFn((function(){i(),n.close(),n.emit("error",new Error("timeout"))}),s);this.opts.autoUnref&&a.unref(),this.subs.push((function(){clearTimeout(a)}))}return this.subs.push(i),this.subs.push(o),this}},{key:"connect",value:function(t){return this.open(t)}},{key:"onopen",value:function(){this.cleanup(),this._readyState="open",this.emitReserved("open");var t=this.engine;this.subs.push(Ct(t,"ping",this.onping.bind(this)),Ct(t,"data",this.ondata.bind(this)),Ct(t,"error",this.onerror.bind(this)),Ct(t,"close",this.onclose.bind(this)),Ct(this.decoder,"decoded",this.ondecoded.bind(this)))}},{key:"onping",value:function(){this.emitReserved("ping")}},{key:"ondata",value:function(t){try{this.decoder.add(t)}catch(t){this.onclose("parse error")}}},{key:"ondecoded",value:function(t){this.emitReserved("packet",t)}},{key:"onerror",value:function(t){this.emitReserved("error",t)}},{key:"socket",value:function(t,e){var n=this.nsps[t];return n||(n=new St(this,t,e),this.nsps[t]=n),n}},{key:"_destroy",value:function(t){for(var e=0,n=Object.keys(this.nsps);e=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{var n=this.backoff.duration();this._reconnecting=!0;var r=this.setTimeoutFn((function(){e.skipReconnect||(t.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((function(n){n?(e._reconnecting=!1,e.reconnect(),t.emitReserved("reconnect_error",n)):e.onreconnect()})))}),n);this.opts.autoUnref&&r.unref(),this.subs.push((function(){clearTimeout(r)}))}}},{key:"onreconnect",value:function(){var t=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",t)}}]),s}(L),Lt={};function Pt(e,n){"object"===t(e)&&(n=e,e=void 0);var r,i=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,r=t;n=n||"undefined"!=typeof location&&location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(t=void 0!==n?n.protocol+"//"+t:"https://"+t),r=ft(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var i=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+i+":"+r.port+e,r.href=r.protocol+"://"+i+(n&&n.port===r.port?"":":"+r.port),r}(e,(n=n||{}).path||"/socket.io"),o=i.source,s=i.id,a=i.path,c=Lt[s]&&a in Lt[s].nsps;return n.forceNew||n["force new connection"]||!1===n.multiplex||c?r=new xt(o,n):(Lt[s]||(Lt[s]=new xt(o,n)),r=Lt[s]),i.query&&!n.query&&(n.query=i.queryKey),r.socket(i.path,n)}return i(Pt,{Manager:xt,Socket:St,io:Pt,connect:Pt}),Pt})); 7 | //# sourceMappingURL=socket.io.min.js.map --------------------------------------------------------------------------------