├── 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 | {{ se_repeat[0] }}
13 | {{ se_repeat[1] }}
14 |
15 | {% endfor %}
16 |
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 |
14 |
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 |
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 |
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 |
15 |
16 | {# #}
17 |
活动参数
18 |
20 |
21 |
22 |
38 |
39 |
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
--------------------------------------------------------------------------------