Wipe branch
This commit is contained in:
commit
553754cd77
|
@ -0,0 +1,162 @@
|
||||||
|
# ---> Python
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
BIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Bit.Corp
|
||||||
|
|
||||||
|
Permission is deny, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,3 @@
|
||||||
|
MAKE BACKUPS FOR LICENSE SERVER!!!
|
||||||
|
Enable LED when WARN or CRIT
|
||||||
|
Switch debug remotely
|
|
@ -0,0 +1,27 @@
|
||||||
|
from enum import Enum, auto
|
||||||
|
|
||||||
|
class ASICs(Enum):
|
||||||
|
# Avalon 10-13
|
||||||
|
Avalon1066 = auto()
|
||||||
|
Avalon1126 = auto()
|
||||||
|
|
||||||
|
# Antminer S
|
||||||
|
AntminerS19 = auto()
|
||||||
|
AntminerS19J = auto()
|
||||||
|
AntminerS19Pro = auto()
|
||||||
|
AntminerS19JPro = auto()
|
||||||
|
|
||||||
|
# Antminer L
|
||||||
|
AntminerL3plus = auto()
|
||||||
|
AntminerL7 = auto()
|
||||||
|
|
||||||
|
# Antminer T
|
||||||
|
AntminerT17 = auto()
|
||||||
|
AntminerT17plus = auto()
|
||||||
|
|
||||||
|
# WhatsMiner
|
||||||
|
WhatsMinerM20s = auto()
|
||||||
|
WhatsMinerM21s = auto()
|
||||||
|
WhatsMinerM30 = auto()
|
||||||
|
WhatsMinerM31 = auto()
|
||||||
|
WhatsMinerM50 = auto()
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pyarmor-7 pack --clean -e "--onefile " main.py
|
||||||
|
#pyarmor-7 pack --clean -e "--onefile " main.py asics.py checker.py colors.py config.py license.py macros.py telegrambot.py updater.py webserver.py
|
|
@ -0,0 +1,334 @@
|
||||||
|
from threading import Thread
|
||||||
|
from mysql.connector import connect, Error
|
||||||
|
from time import sleep, time
|
||||||
|
from asics import ASICs
|
||||||
|
from macros import *
|
||||||
|
import os, re, json, socket, requests
|
||||||
|
|
||||||
|
|
||||||
|
class CThread(Thread):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
def __init__(self, event):
|
||||||
|
super(CThread, self).__init__()
|
||||||
|
self.event = event
|
||||||
|
|
||||||
|
|
||||||
|
def getreg(self, reg, str):
|
||||||
|
res = re.findall(reg, str)
|
||||||
|
if res: return res[0][1]
|
||||||
|
else: return False
|
||||||
|
|
||||||
|
|
||||||
|
def getstat_AntminerL3(self, log):
|
||||||
|
status = 'ok'
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def getstat_Avalon1XXX(self, log, type):
|
||||||
|
status = 'ok'
|
||||||
|
|
||||||
|
rej = float(self.getreg('("Device Rejected%":([0-9.]+),)', log))
|
||||||
|
ghs = int(float(self.getreg('(GHSmm\[([0-9.:,]+)\])', log)))
|
||||||
|
avg = int(float(self.getreg('(GHSavg\[([0-9.: ]+)\])', log)))
|
||||||
|
# brd = self.getreg('(MGHS\[([0-9.:, ]+)\])', log)
|
||||||
|
tmp = int(self.getreg('(TMax\[([0-9]+)\])', log))
|
||||||
|
|
||||||
|
chash = CONF.get('asics', ASICs(type).name, 'crit', 'hash')
|
||||||
|
whash = CONF.get('asics', ASICs(type).name, 'warn', 'hash')
|
||||||
|
|
||||||
|
ctemp = CONF.get('asics', ASICs(type).name, 'crit', 'temp')
|
||||||
|
wtemp = CONF.get('asics', ASICs(type).name, 'warn', 'temp')
|
||||||
|
|
||||||
|
crej = CONF.get('asics', ASICs(type).name, 'crit', 'rej')
|
||||||
|
wrej = CONF.get('asics', ASICs(type).name, 'warn', 'rej')
|
||||||
|
|
||||||
|
if rej > crej or ghs < chash or avg < chash or tmp > ctemp: status = 'crit'
|
||||||
|
elif rej > wrej or ghs < whash or avg < whash or tmp > wtemp: status = 'warn'
|
||||||
|
|
||||||
|
DEBUG(f"{rej} | {ghs} | {avg} | {brd} | {tmp}")
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def getstat_AntminerS19(self, log):
|
||||||
|
status = 'ok'
|
||||||
|
|
||||||
|
rej = float(self.getreg('("Device Rejected%": ([0-9.]+),)', log))
|
||||||
|
ghs = int(float(self.getreg('("GHS 5s": ([0-9.]+),)', log)))
|
||||||
|
avg = int(float(self.getreg('("GHS av": ([0-9.]+),)', log)))
|
||||||
|
tmp = int(self.getreg('("TMax": ([0-9]+),)', log))
|
||||||
|
|
||||||
|
chash = CONF.get('asics', ASICs(type).name, 'crit', 'hash')
|
||||||
|
whash = CONF.get('asics', ASICs(type).name, 'warn', 'hash')
|
||||||
|
|
||||||
|
ctemp = CONF.get('asics', ASICs(type).name, 'crit', 'temp')
|
||||||
|
wtemp = CONF.get('asics', ASICs(type).name, 'warn', 'temp')
|
||||||
|
|
||||||
|
crej = CONF.get('asics', ASICs(type).name, 'crit', 'rej')
|
||||||
|
wrej = CONF.get('asics', ASICs(type).name, 'warn', 'rej')
|
||||||
|
|
||||||
|
if rej > crej or ghs < chash or avg < chash or tmp > ctemp: status = 'crit'
|
||||||
|
elif rej > wrej or ghs < whash or avg < whash or tmp > wtemp: status = 'warn'
|
||||||
|
|
||||||
|
DEBUG(f"{rej} | {ghs} | {avg} | {brd} | {tmp}")
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def gettype(self, ip):
|
||||||
|
info = self.rtcp(ip, 4028, '{"command": "version"}')
|
||||||
|
|
||||||
|
if not info:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if CONF.get('debug') and info:
|
||||||
|
info1 = self.rtcp(ip, 4028, '{"command": "stats"}')
|
||||||
|
info2 = self.rtcp(ip, 4028, '{"command": "version"}')
|
||||||
|
info3 = self.rtcp(ip, 4028, '{"command": "summary"}')
|
||||||
|
info4 = self.rtcp(ip, 4028, '{"command": "pools"}')
|
||||||
|
info5 = self.rtcp(ip, 4028, '{"command": "devdetails"}')
|
||||||
|
info6 = self.rtcp(ip, 4028, '{"command": "get_version"}')
|
||||||
|
info7 = self.rtcp(ip, 4028, '{"command": "status"}')
|
||||||
|
info8 = self.rtcp(ip, 4028, '{"command": "get_miner_info"}')
|
||||||
|
info9 = self.rtcp(ip, 4028, '{"command": "devs"}')
|
||||||
|
info10 = self.rtcp(ip, 4028, '{"command": "notify"}')
|
||||||
|
info11 = self.rtcp(ip, 4028, '{"command": "coin"}')
|
||||||
|
info12 = self.rtcp(ip, 4028, '{"command": "edevs"}')
|
||||||
|
info13 = self.rtcp(ip, 4028, '{"command": "estats"}')
|
||||||
|
|
||||||
|
DEBUG(f"[{ip}] {info1}")
|
||||||
|
DEBUG(f"[{ip}] {info2}")
|
||||||
|
DEBUG(f"[{ip}] {info3}")
|
||||||
|
DEBUG(f"[{ip}] {info4}")
|
||||||
|
DEBUG(f"[{ip}] {info5}")
|
||||||
|
DEBUG(f"[{ip}] {info6}")
|
||||||
|
DEBUG(f"[{ip}] {info7}")
|
||||||
|
DEBUG(f"[{ip}] {info8}")
|
||||||
|
DEBUG(f"[{ip}] {info9}")
|
||||||
|
DEBUG(f"[{ip}] {info10}")
|
||||||
|
DEBUG(f"[{ip}] {info11}")
|
||||||
|
DEBUG(f"[{ip}] {info12}")
|
||||||
|
DEBUG(f"[{ip}] {info13}")
|
||||||
|
|
||||||
|
info += self.rtcp(ip, 4028, '{"command": "devdetails"}')
|
||||||
|
|
||||||
|
# Bitmain
|
||||||
|
if "Antminer" in info:
|
||||||
|
if "S19J Pro" in info:
|
||||||
|
return ASICs.AntminerS19JPro.value
|
||||||
|
if "S19J" in info:
|
||||||
|
return ASICs.AntminerS19J.value
|
||||||
|
if "S19 Pro" in info:
|
||||||
|
return ASICs.AntminerS19Pro.value
|
||||||
|
if "S19" in info:
|
||||||
|
return ASICs.AntminerS19.value
|
||||||
|
|
||||||
|
if "L3+" in info:
|
||||||
|
return ASICs.AntminerL3plus.value
|
||||||
|
if "L7" in info:
|
||||||
|
return ASICs.AntminerL3plus.value
|
||||||
|
|
||||||
|
if "T17+" in info:
|
||||||
|
return ASICs.AntminerT17plus.value
|
||||||
|
if "T17" in info:
|
||||||
|
return ASICs.AntminerT17.value
|
||||||
|
|
||||||
|
# Avalon
|
||||||
|
if "Avalon" in info:
|
||||||
|
if "1066" in info:
|
||||||
|
return ASICs.Avalon1066.value
|
||||||
|
if "1126" in info:
|
||||||
|
return ASICs.Avalon1126.value
|
||||||
|
|
||||||
|
# WhatsMiner
|
||||||
|
if "bitmicro" in info:
|
||||||
|
if "M20s":
|
||||||
|
return ASICs.WhatsMinerM20s.value
|
||||||
|
if "M21s":
|
||||||
|
return ASICs.WhatsMinerM21s.value
|
||||||
|
if "M30":
|
||||||
|
return ASICs.WhatsMinerM30.value
|
||||||
|
if "M31":
|
||||||
|
return ASICs.WhatsMinerM31.value
|
||||||
|
if "M50":
|
||||||
|
return ASICs.WhatsMinerM50.value
|
||||||
|
|
||||||
|
# Innosilicon
|
||||||
|
if False:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def rtcp(self, ip, port, cmd):
|
||||||
|
cmd = cmd.encode('utf-8')
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
|
try:
|
||||||
|
s.connect((ip, port))
|
||||||
|
s.sendall(cmd)
|
||||||
|
tdata = []
|
||||||
|
while 1:
|
||||||
|
data = s.recv(1024*32)
|
||||||
|
if not data: break
|
||||||
|
tdata.append(data)
|
||||||
|
|
||||||
|
data = b''.join(tdata).decode("utf-8")
|
||||||
|
|
||||||
|
return data
|
||||||
|
except OSError as e:
|
||||||
|
# ERR(e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_AntminerL3(self, ip):
|
||||||
|
# f'http://{ip}/cgi-bin/get_miner_status.cgi'
|
||||||
|
# f'http://{ip}/cgi-bin/get_miner_conf.cgi'
|
||||||
|
summ = self.rtcp(ip, 4028, "summary")
|
||||||
|
stat = self.rtcp(ip, 4028, "estats")
|
||||||
|
info = self.rtcp(ip, 4028, "version")
|
||||||
|
|
||||||
|
if summ and stat:
|
||||||
|
mac = json.loads(requests.get(
|
||||||
|
f'http://{ip}/cgi-bin/get_system_info.cgi',
|
||||||
|
auth=requests.auth.HTTPDigestAuth('root', 'root')).text)['macaddr'].lower()
|
||||||
|
|
||||||
|
log = f"{info} ||| {summ} ||| {stat}"
|
||||||
|
|
||||||
|
return log, mac
|
||||||
|
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
|
||||||
|
def check_AntminerS19(self, ip):
|
||||||
|
# f'http://{ip}/cgi-bin/get_miner_status.cgi'
|
||||||
|
# f'http://{ip}/cgi-bin/get_miner_conf.cgi'
|
||||||
|
summ = self.rtcp(ip, 4028, "summary")
|
||||||
|
stat = self.rtcp(ip, 4028, "stats")
|
||||||
|
info = self.rtcp(ip, 4028, "version")
|
||||||
|
|
||||||
|
if summ and stat:
|
||||||
|
mac = json.loads(requests.get(
|
||||||
|
f'http://{ip}/cgi-bin/get_network_info.cgi',
|
||||||
|
auth=requests.auth.HTTPDigestAuth('root', 'root')).text)['macaddr'].lower()
|
||||||
|
|
||||||
|
log = f"{info} ||| {summ} ||| {stat}"
|
||||||
|
|
||||||
|
return log, mac
|
||||||
|
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
|
||||||
|
def check_Avalon1XXX(self, ip):
|
||||||
|
summ = self.rtcp(ip, 4028, "summary")
|
||||||
|
stat = self.rtcp(ip, 4028, "estats")
|
||||||
|
info = self.rtcp(ip, 4028, "version")
|
||||||
|
|
||||||
|
if summ and stat:
|
||||||
|
mac = self.getreg(r'(MAC=([A-z0-9]+))', info)
|
||||||
|
if mac: mac = ':'.join(mac[i:i+2] for i in range(0,12,2))
|
||||||
|
else: mac = ':'
|
||||||
|
|
||||||
|
log = f"{info} ||| {summ} ||| {stat}"
|
||||||
|
|
||||||
|
return log, mac
|
||||||
|
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
|
||||||
|
def check(self, ip):
|
||||||
|
try:
|
||||||
|
type = self.gettype(ip)
|
||||||
|
|
||||||
|
if not type:
|
||||||
|
return (False, False, False, False, False), False
|
||||||
|
|
||||||
|
if type in [ASICs.AntminerL3plus.value]:
|
||||||
|
log, mac = self.check_AntminerL3(ip)
|
||||||
|
status = self.getstat_AntminerL3(log, type)
|
||||||
|
elif type in [ASICs.Avalon1066.value, ASICs.Avalon1126.value]:
|
||||||
|
log, mac = self.check_Avalon1XXX(ip)
|
||||||
|
status = self.getstat_Avalon1XXX(log, type)
|
||||||
|
elif type in [ASICs.AntminerS19.value, ASICs.AntminerS19J.value, ASICs.AntminerS19Pro.value, ASICs.AntminerS19JPro.value]:
|
||||||
|
log, mac = self.check_AntminerS19(ip)
|
||||||
|
status = self.getstat_AntminerS19(log, type)
|
||||||
|
|
||||||
|
return (ip, mac, type, int(time()), log), status
|
||||||
|
except Exception as e:
|
||||||
|
WARN('Minor error in <lambda>Thread: ' + str(e))
|
||||||
|
return (False, False, False, False, False), False
|
||||||
|
|
||||||
|
|
||||||
|
def checkall(self):
|
||||||
|
ips = []
|
||||||
|
|
||||||
|
with self.conn.cursor(dictionary=True) as c:
|
||||||
|
c.execute("SELECT * FROM `ips` WHERE `location` = %s", (CONF.get('location'),))
|
||||||
|
|
||||||
|
for ip in c.fetchall():
|
||||||
|
ip = ip['ip']
|
||||||
|
if ip.isalpha() or not ip.find('-'):
|
||||||
|
ips.append(ip)
|
||||||
|
else:
|
||||||
|
i_p = ip.split('.')
|
||||||
|
i_p_new = []
|
||||||
|
for i in i_p:
|
||||||
|
i_p_new.append(i.split('-'))
|
||||||
|
i_p_new[-1][0] = int(i_p_new[-1][0])
|
||||||
|
i_p_new[-1][-1] = int(i_p_new[-1][-1])
|
||||||
|
|
||||||
|
for i1 in range(i_p_new[0][0], i_p_new[0][-1] + 1):
|
||||||
|
for i2 in range(i_p_new[1][0], i_p_new[1][-1] + 1):
|
||||||
|
for i3 in range(i_p_new[2][0], i_p_new[2][-1] + 1):
|
||||||
|
for i4 in range(i_p_new[3][0], i_p_new[3][-1] + 1):
|
||||||
|
ips.append(f"{i1}.{i2}.{i3}.{i4}")
|
||||||
|
|
||||||
|
Tca = []
|
||||||
|
ca = []
|
||||||
|
for ip in ips:
|
||||||
|
T = Thread(target=lambda x: ca.append(self.check(x)), args=(ip,))
|
||||||
|
Tca.append(T)
|
||||||
|
for i in range(len(Tca)):
|
||||||
|
Tca[i].start()
|
||||||
|
for i in range(len(Tca)):
|
||||||
|
Tca[i].join()
|
||||||
|
|
||||||
|
with self.conn.cursor() as c:
|
||||||
|
c.execute(f"DELETE FROM `laststate` WHERE `location` = %s", (CONF.get('location'),))
|
||||||
|
|
||||||
|
for i in ca:
|
||||||
|
i, status = i
|
||||||
|
if i[4]:
|
||||||
|
c.execute(f"INSERT INTO `asiclogs` (`ip`, `mac`, `type`, `time`, `log`) VALUES (%s, %s, %s, %s, %s)", i)
|
||||||
|
|
||||||
|
if i[1] == ':':
|
||||||
|
continue
|
||||||
|
c.execute(
|
||||||
|
f"INSERT INTO `laststate` (`ip`, `mac`, `type`, `location`, `status`, `time`) VALUES (%s, %s, %s, %s, %s, %s)",
|
||||||
|
(i[0], i[1], i[2], CONF.get('location'), status, int(time())))
|
||||||
|
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
SUCC("Checker started!")
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
self.conn = connect(
|
||||||
|
host=CONF.get('db', 'host'),
|
||||||
|
user=CONF.get('db', 'user'),
|
||||||
|
password=CONF.get('db', 'password'),
|
||||||
|
database=CONF.get('db', 'name'))
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
if self.event.is_set():
|
||||||
|
SUCC("Checker stopped!")
|
||||||
|
break
|
||||||
|
|
||||||
|
self.checkall()
|
||||||
|
sleep(10)
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
os._exit(1)
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
watch -n 0.1 'ps -o vsize,command,pcpu ax | sort -b -k3 -r | grep -E -i -w "[0-9]+ python"'
|
|
@ -0,0 +1,43 @@
|
||||||
|
from threading import Thread
|
||||||
|
from mysql.connector import connect, Error
|
||||||
|
from time import sleep, time
|
||||||
|
from macros import *
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class NThread(Thread):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
def __init__(self, event):
|
||||||
|
super(NThread, self).__init__()
|
||||||
|
self.event = event
|
||||||
|
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
with self.conn.cursor() as c:
|
||||||
|
c.execute(f"DELETE FROM `asiclogs` WHERE `time` < UNIX_TIMESTAMP() - %s", (CONF.get('db-log-days'),))
|
||||||
|
c.execute(f"DELETE FROM `laststate` WHERE `time` < UNIX_TIMESTAMP() - 30")
|
||||||
|
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
SUCC("Cleaner started!")
|
||||||
|
|
||||||
|
self.conn = connect(
|
||||||
|
host=CONF.get('db', 'host'),
|
||||||
|
user=CONF.get('db', 'user'),
|
||||||
|
password=CONF.get('db', 'password'),
|
||||||
|
database=CONF.get('db', 'name'))
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
if self.event.is_set():
|
||||||
|
SUCC('Cleaner stopped!')
|
||||||
|
break
|
||||||
|
|
||||||
|
self.clean()
|
||||||
|
sleep(5)
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
os._exit(1)
|
|
@ -0,0 +1,20 @@
|
||||||
|
RESET = "\033[0m"
|
||||||
|
BOLD = "\033[1m"
|
||||||
|
|
||||||
|
BLACK = "\033[30m"
|
||||||
|
RED = "\033[31m"
|
||||||
|
GREEN = "\033[32m"
|
||||||
|
YELLOW = "\033[33m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
MAGENTA = "\033[35m"
|
||||||
|
CYAN = "\033[36m"
|
||||||
|
WHITE = "\033[37m"
|
||||||
|
|
||||||
|
BGBLACK = "\033[40m"
|
||||||
|
BGRED = "\033[41m"
|
||||||
|
BGGREEN = "\033[42m"
|
||||||
|
BGYELLOW = "\033[43m"
|
||||||
|
BGBLUE = "\033[44m"
|
||||||
|
BGMAGENTA = "\033[45m"
|
||||||
|
BGCYAN = "\033[46m"
|
||||||
|
BGWHITE = "\033[47m"
|
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"license": "00001111-2222-3333-4444-555566667777",
|
||||||
|
"location": "Underground",
|
||||||
|
"logging": true,
|
||||||
|
"db-log-days": 7,
|
||||||
|
"debug": true,
|
||||||
|
"updates": false,
|
||||||
|
"port": 9070,
|
||||||
|
"telegram": {
|
||||||
|
"enable": true,
|
||||||
|
"channel": -1001871940722,
|
||||||
|
"token": "5953600362:AAELgZr0Ldstf0omK43zKrAzGXOuMGowcf8",
|
||||||
|
"normal-notify": true,
|
||||||
|
"warn-notify": false,
|
||||||
|
"crit-notify": true
|
||||||
|
},
|
||||||
|
"db": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "root",
|
||||||
|
"password": "0",
|
||||||
|
"name": "myasics"
|
||||||
|
},
|
||||||
|
"asics": {
|
||||||
|
"L3plus": {
|
||||||
|
"crit": {
|
||||||
|
"hash": 40000,
|
||||||
|
"temp": 90,
|
||||||
|
"rejects": 2.5
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
"hash": 45000,
|
||||||
|
"temp": 80,
|
||||||
|
"rejects": 1.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"S19": {
|
||||||
|
"crit": {
|
||||||
|
"hash": 80000,
|
||||||
|
"temp": 90,
|
||||||
|
"rejects": 2.5
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
"hash": 85000,
|
||||||
|
"temp": 80,
|
||||||
|
"rejects": 1.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Avalon1066": {
|
||||||
|
"crit": {
|
||||||
|
"hash": 42000,
|
||||||
|
"temp": 90,
|
||||||
|
"rejects": 2.5
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
"hash": 46000,
|
||||||
|
"temp": 80,
|
||||||
|
"rejects": 1.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Avalon1126": {
|
||||||
|
"crit": {
|
||||||
|
"hash": 64000,
|
||||||
|
"temp": 90,
|
||||||
|
"rejects": 2.5
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
"hash": 66000,
|
||||||
|
"temp": 80,
|
||||||
|
"rejects": 1.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
APPNAME='Bit.ASICmon'
|
||||||
|
VERSION='v0.1.1a'
|
||||||
|
DEBUGGING=True
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: '3.1'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mysql
|
||||||
|
command: --default-authentication-plugin=mysql_native_password
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 0
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
|
@ -0,0 +1,223 @@
|
||||||
|
const sprintf = (...[string, ...args]) => {
|
||||||
|
return string.replace(/{(\d+)}/g, (match, number) => args[number] ?? match);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getJSON = async url => {
|
||||||
|
const resp = await fetch(url)
|
||||||
|
const json = await resp.json()
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPopup = async id => {
|
||||||
|
const asics = await getJSON('/api/asictypes')
|
||||||
|
|
||||||
|
asicoptions = ''
|
||||||
|
asics.forEach(elem => {
|
||||||
|
asicoptions += sprintf('<option value="{0}">{1}</option>', elem['key'], elem['value'])
|
||||||
|
})
|
||||||
|
|
||||||
|
document.querySelector('body').innerHTML += sprintf(popup, id, asicoptions, 'content')
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletePopup = id => {
|
||||||
|
document.querySelector('#popup-' + id).remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
const update = async () => {
|
||||||
|
let inner = await getStats()
|
||||||
|
document.querySelector('.grid').innerHTML = inner
|
||||||
|
}
|
||||||
|
|
||||||
|
const datafn = async () => {
|
||||||
|
// return await getJSON('/api/webinit')
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = datafn()
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
const cols = 40
|
||||||
|
const rows = 40
|
||||||
|
|
||||||
|
|
||||||
|
const css =
|
||||||
|
`<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap');
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: 'Source Code Pro', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
background: #0b0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yellow {
|
||||||
|
background: #ff0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
background: #f11 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkred {
|
||||||
|
background: #300 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
height: 60px;
|
||||||
|
background: #222;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 0 1px 0;
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
color: #bbb;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(${cols}, 30px);
|
||||||
|
grid-template-rows: repeat(${rows}, 30px);
|
||||||
|
grid-gap: 1px;
|
||||||
|
margin: auto;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
position: relative;
|
||||||
|
background: #111;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell:hover::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
background: #ffffff33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-bg {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #00000033;
|
||||||
|
z-index: 98;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-window {
|
||||||
|
position: relative;
|
||||||
|
height: max-content;
|
||||||
|
max-height: 90vh;
|
||||||
|
width: max-content;
|
||||||
|
max-width: 90vw;
|
||||||
|
padding: 40px;
|
||||||
|
background: #222;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`
|
||||||
|
|
||||||
|
const header =
|
||||||
|
'<div class="header">'
|
||||||
|
+ '<div class="header-info">'
|
||||||
|
+ 'Всего устройств: {0}, Предупреждений, {1}, Ошибки: {2}, В сети: {3}, Не в сети: {4}'
|
||||||
|
+ '</div>'
|
||||||
|
+ '</div>'
|
||||||
|
|
||||||
|
|
||||||
|
const cell =
|
||||||
|
'<div class="cell {1}" id="cell-{0}" onclick="createPopup({0})">'
|
||||||
|
+ '{2}'
|
||||||
|
+ '</div>'
|
||||||
|
|
||||||
|
const grid =
|
||||||
|
'<div class="grid">'
|
||||||
|
+ '{0}'
|
||||||
|
+ '</div>'
|
||||||
|
|
||||||
|
const popup =
|
||||||
|
'<div class="popup" id="popup-{0}">'
|
||||||
|
+ '<div class="popup-bg" onclick="deletePopup({0})"></div>'
|
||||||
|
+ '<div class="popup-window">'
|
||||||
|
+ '<div class="row">'
|
||||||
|
+ '<select>'
|
||||||
|
+ '<option value="none">Свободно</option>'
|
||||||
|
+ '{1}'
|
||||||
|
+ '</select>'
|
||||||
|
+ '<input type="text" placeholder="Адрес...">'
|
||||||
|
+ '</div>'
|
||||||
|
+ '{2}'
|
||||||
|
+ '</div>'
|
||||||
|
+ '</div>'
|
||||||
|
|
||||||
|
const getStats = async () => {
|
||||||
|
let cells = ''
|
||||||
|
let info = await getJSON('/curstatus.json')
|
||||||
|
for(let i = 1; i <= cols * rows; i++) {
|
||||||
|
if(info.asics[i]?.status == "ok")
|
||||||
|
cells += sprintf(cell, i, 'green', info.asics[i]?.hashrate)
|
||||||
|
else if(info.asics[i]?.status == "warn")
|
||||||
|
cells += sprintf(cell, i, 'yellow', info.asics[i]?.hashrate)
|
||||||
|
else if(info.asics[i]?.status == "crit")
|
||||||
|
cells += sprintf(cell, i, 'red', info.asics[i]?.hashrate)
|
||||||
|
else if(info.asics[i]?.status == "off")
|
||||||
|
cells += sprintf(cell, i, 'darkred', '')
|
||||||
|
else
|
||||||
|
cells += sprintf(cell, i, '', '')
|
||||||
|
}
|
||||||
|
return cells
|
||||||
|
}
|
||||||
|
|
||||||
|
const run = async () => {
|
||||||
|
document.querySelector('body').innerHTML += css
|
||||||
|
document.querySelector('body').innerHTML += sprintf(header)
|
||||||
|
|
||||||
|
let inner = sprintf(grid, await getStats())
|
||||||
|
document.querySelector('body').innerHTML += inner
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
await update()
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
run()
|
|
@ -0,0 +1,76 @@
|
||||||
|
from threading import Thread
|
||||||
|
from time import sleep, time
|
||||||
|
from macros import *
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from Crypto.Random import get_random_bytes
|
||||||
|
from hashlib import sha256
|
||||||
|
from base64 import b64decode
|
||||||
|
from datetime import datetime
|
||||||
|
from urllib import request
|
||||||
|
import os, json, requests
|
||||||
|
|
||||||
|
|
||||||
|
class LThread(Thread):
|
||||||
|
_FIRST = True
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, event):
|
||||||
|
super(LThread, self).__init__()
|
||||||
|
self.event = event
|
||||||
|
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
url = 'https://license.bitheaven.ru/api/v1/license.check'
|
||||||
|
post = {
|
||||||
|
'key': CONF.get('license'),
|
||||||
|
'loc': CONF.get('location'),
|
||||||
|
'time': int(time()),
|
||||||
|
'data': get_random_bytes(16)
|
||||||
|
}
|
||||||
|
curtime = post['time']
|
||||||
|
|
||||||
|
try:
|
||||||
|
req = requests.post(url, data=post).json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if req['error']:
|
||||||
|
ERR(f"Server error! {req['msg']}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
key = sha256(post['loc'].encode('utf-8')).hexdigest()[0:32].encode("utf-8")
|
||||||
|
iv = post['data']
|
||||||
|
aes = AES.new(key, AES.MODE_OFB, iv=iv)
|
||||||
|
msg = aes.decrypt(b64decode(req['data'])).decode('unicode_escape').strip()
|
||||||
|
|
||||||
|
until = datetime.fromtimestamp(req['until']).strftime('%d.%m.%Y')
|
||||||
|
|
||||||
|
if self._FIRST:
|
||||||
|
INFO(f'License paid until: {until}')
|
||||||
|
self._FIRST = False
|
||||||
|
|
||||||
|
return f"{CONF.get('license')}{curtime}" == msg
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
if self.event.is_set():
|
||||||
|
break
|
||||||
|
|
||||||
|
cur = self.check()
|
||||||
|
|
||||||
|
if not cur:
|
||||||
|
try:
|
||||||
|
request.urlopen('http://1.1.1.1', timeout=2)
|
||||||
|
CRIT("License wrong or expired!")
|
||||||
|
WARN("Get it on https://bhev.ru/glfbam")
|
||||||
|
sleep(60)
|
||||||
|
except:
|
||||||
|
ERR("No access to network!")
|
||||||
|
# os._exit(1)
|
||||||
|
|
||||||
|
sleep(5)
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
os._exit(1)
|
Binary file not shown.
|
@ -0,0 +1,97 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from colors import *
|
||||||
|
from platform import system
|
||||||
|
import inspect
|
||||||
|
import json, os, config
|
||||||
|
|
||||||
|
|
||||||
|
__DEBUGGING_ALERT = False
|
||||||
|
|
||||||
|
MUSTDIE = system().lower() == 'windows'
|
||||||
|
LINUX = system().lower() == 'linux'
|
||||||
|
MACOS = system().lower() == 'darwin'
|
||||||
|
|
||||||
|
|
||||||
|
class CONF():
|
||||||
|
def __init__(self):
|
||||||
|
CONF.config = json.load(open('config.json'))
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get(*params):
|
||||||
|
conf = CONF.config
|
||||||
|
|
||||||
|
for param in params:
|
||||||
|
if param in conf.keys():
|
||||||
|
conf = conf[param]
|
||||||
|
|
||||||
|
if conf:
|
||||||
|
return conf
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
CONF()
|
||||||
|
|
||||||
|
|
||||||
|
def __GET_TIME():
|
||||||
|
return datetime.now().strftime("%H:%M:%S.%f")
|
||||||
|
|
||||||
|
|
||||||
|
def __GET_DATE():
|
||||||
|
return datetime.now().strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
|
||||||
|
def __ADD_TO_LOG(str):
|
||||||
|
if CONF.get('logging'):
|
||||||
|
if not os.path.isdir('log'):
|
||||||
|
os.mkdir('log')
|
||||||
|
log = open(f'log/{__GET_DATE()}.log', 'a')
|
||||||
|
log.write(str + '\n')
|
||||||
|
log.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __PRINT_LOG(str, log):
|
||||||
|
if CONF.get('debug') and config.DEBUGGING:
|
||||||
|
st = inspect.stack()[2]
|
||||||
|
caller = st.filename.split('/')[-1].split('.')[0]
|
||||||
|
callerline = st.lineno
|
||||||
|
str = f'[{__GET_TIME()}] [{caller}:{callerline}] {str}'
|
||||||
|
log = f'[{caller}:{callerline}] {log}'
|
||||||
|
else:
|
||||||
|
str = f'[{__GET_TIME()}] {str}'
|
||||||
|
|
||||||
|
print(str)
|
||||||
|
__ADD_TO_LOG(log)
|
||||||
|
|
||||||
|
|
||||||
|
def INFO(s = ''):
|
||||||
|
__PRINT_LOG(f"[INFO] {str(s)}{RESET}", str(s))
|
||||||
|
|
||||||
|
|
||||||
|
def DEBUG(s = ''):
|
||||||
|
global __DEBUGGING_ALERT
|
||||||
|
|
||||||
|
if not CONF.get('debug'):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if config.DEBUGGING:
|
||||||
|
__PRINT_LOG(f"{MAGENTA}[DEBUG]{RESET} {BGBLUE}{YELLOW}{BOLD}{str(s)}{RESET}", str(s))
|
||||||
|
elif not __DEBUGGING_ALERT:
|
||||||
|
WARN('DEBUGGING DISABLED BY OWNER!')
|
||||||
|
__DEBUGGING_ALERT = True
|
||||||
|
|
||||||
|
|
||||||
|
def SUCC(s = ''):
|
||||||
|
__PRINT_LOG(f"{GREEN}[SUCCESS]{RESET} {GREEN}{str(s)}{RESET}", str(s))
|
||||||
|
|
||||||
|
|
||||||
|
def WARN(s = ''):
|
||||||
|
__PRINT_LOG(f"{YELLOW}[WARN]{RESET} {YELLOW}{str(s)}{RESET}", str(s))
|
||||||
|
|
||||||
|
|
||||||
|
def ERR(s = ''):
|
||||||
|
__PRINT_LOG(f"{RED}[ERROR]{RESET} {RED}{str(s)}{RESET}", str(s))
|
||||||
|
|
||||||
|
|
||||||
|
def CRIT(s = ''):
|
||||||
|
__PRINT_LOG(f"{RED}{BOLD}[CRITICAL]{RESET} {BGRED}{WHITE} {str(s)} {RESET}", str(s))
|
|
@ -0,0 +1,95 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
from updater import UThread
|
||||||
|
from license import LThread
|
||||||
|
from checker import CThread
|
||||||
|
from cleaner import NThread
|
||||||
|
from telegrambot import TGThread
|
||||||
|
from webserver import WSThread
|
||||||
|
from time import sleep
|
||||||
|
from macros import *
|
||||||
|
from colors import *
|
||||||
|
from threading import Event
|
||||||
|
import config, sys, os, asyncio
|
||||||
|
|
||||||
|
|
||||||
|
INFO('╔' + '═'*(len(config.APPNAME) + len(config.VERSION) + 3) + '╗')
|
||||||
|
INFO(f"║ {CYAN}{BOLD}{config.APPNAME}{RESET} {config.VERSION} ║")
|
||||||
|
INFO('╚' + '═'*(len(config.APPNAME) + len(config.VERSION) + 3) + '╝')
|
||||||
|
|
||||||
|
if CONF.get('debug'):
|
||||||
|
INFO('DEBUG TEST START ' + '*' * 20)
|
||||||
|
DEBUG(f"{RESET}{BLACK}*{RED}*{GREEN}*{YELLOW}*{BLUE}*{MAGENTA}*{CYAN}*{WHITE}*")
|
||||||
|
DEBUG(f"{RESET}{BOLD}{BLACK}*{RED}*{GREEN}*{YELLOW}*{BLUE}*{MAGENTA}*{CYAN}*{WHITE}*")
|
||||||
|
DEBUG(f"{RESET}{BGBLACK}*{BGRED}*{BGGREEN}*{BGYELLOW}*{BGBLUE}*{BGMAGENTA}*{BGCYAN}*{BGWHITE}*")
|
||||||
|
INFO('Test message')
|
||||||
|
DEBUG('Test message')
|
||||||
|
SUCC('Test message')
|
||||||
|
WARN('Test message')
|
||||||
|
ERR('Test message')
|
||||||
|
CRIT('Test message')
|
||||||
|
INFO('DEBUG TEST END ' + '*' * 22 + RESET)
|
||||||
|
|
||||||
|
|
||||||
|
eupdate = Event()
|
||||||
|
estop = Event()
|
||||||
|
|
||||||
|
lt = LThread(eupdate)
|
||||||
|
lt.daemon = True
|
||||||
|
lt.start()
|
||||||
|
|
||||||
|
wt = WSThread(eupdate)
|
||||||
|
wt.daemon = True
|
||||||
|
wt.start()
|
||||||
|
|
||||||
|
ct = CThread(eupdate)
|
||||||
|
ct.daemon = True
|
||||||
|
ct.start()
|
||||||
|
|
||||||
|
nt = NThread(eupdate)
|
||||||
|
nt.daemon = True
|
||||||
|
nt.start()
|
||||||
|
|
||||||
|
tt = TGThread(eupdate, estop)
|
||||||
|
tt.daemon = True
|
||||||
|
tt.start()
|
||||||
|
|
||||||
|
ut = UThread()
|
||||||
|
ut.daemon = True
|
||||||
|
ut.start()
|
||||||
|
|
||||||
|
updated = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
if lt.is_alive() and wt.is_alive() and ct.is_alive() and tt.is_alive() and ut.is_alive() and nt.is_alive():
|
||||||
|
sleep(1)
|
||||||
|
elif lt.is_alive() and wt.is_alive() and ct.is_alive() and tt.is_alive() and nt.is_alive():
|
||||||
|
eupdate.set()
|
||||||
|
if lt.is_alive(): lt.join()
|
||||||
|
if wt.is_alive(): wt.join()
|
||||||
|
if ct.is_alive(): ct.join()
|
||||||
|
if nt.is_alive(): nt.join()
|
||||||
|
if tt.is_alive(): tt.join()
|
||||||
|
updated = True
|
||||||
|
SUCC('Program updated, reboot!')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
estop.set()
|
||||||
|
sleep(1)
|
||||||
|
CRIT('Program broken.')
|
||||||
|
os._exit(3)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.stdout.flush()
|
||||||
|
print('\r', end='')
|
||||||
|
estop.set()
|
||||||
|
sleep(1)
|
||||||
|
CRIT('STOP')
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
estop.set()
|
||||||
|
sleep(1)
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
if updated:
|
||||||
|
os.execl(sys.argv[0], *sys.argv)
|
|
@ -0,0 +1,4 @@
|
||||||
|
flask
|
||||||
|
python-telegram-bot
|
||||||
|
pyinstaller
|
||||||
|
pyarmor<8.0.0
|
|
@ -0,0 +1,125 @@
|
||||||
|
from threading import Thread
|
||||||
|
from telegram import Bot, error
|
||||||
|
from asics import ASICs
|
||||||
|
from time import sleep
|
||||||
|
from mysql.connector import connect, Error
|
||||||
|
from macros import *
|
||||||
|
import os, json, asyncio
|
||||||
|
|
||||||
|
|
||||||
|
class TGThread(Thread):
|
||||||
|
conn = None
|
||||||
|
bot = None
|
||||||
|
asics = {}
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, event, event2):
|
||||||
|
super(TGThread, self).__init__()
|
||||||
|
self.event = event
|
||||||
|
self.event2 = event2
|
||||||
|
|
||||||
|
|
||||||
|
async def sendmsg(self, msg):
|
||||||
|
return await self.bot.send_message(CONF.get('telegram', 'channel'), msg, 'MarkdownV2')
|
||||||
|
|
||||||
|
|
||||||
|
async def check(self):
|
||||||
|
msg = []
|
||||||
|
|
||||||
|
for i in self.asics.keys():
|
||||||
|
self.asics[i]['tooff'] -= 1
|
||||||
|
|
||||||
|
with self.conn.cursor(dictionary=True) as c:
|
||||||
|
c.execute(f"SELECT * FROM `laststate`")
|
||||||
|
|
||||||
|
for i in c.fetchall():
|
||||||
|
if not i['mac'] in self.asics.keys():
|
||||||
|
self.asics[i['mac']] = {}
|
||||||
|
self.asics[i['mac']]['alert'] = 0
|
||||||
|
|
||||||
|
msg.append(f"🟢 {i['location']} \| ASIC \"*{ASICs(i['type']).name}*\" `{i['ip']}` online\!")
|
||||||
|
|
||||||
|
self.asics[i['mac']]['tooff'] = 3
|
||||||
|
self.asics[i['mac']]['status'] = i['status']
|
||||||
|
|
||||||
|
if i['status'] == 'crit':
|
||||||
|
if CONF.get('telegram', 'crit-notify') and self.asics[i['mac']]['alert'] < 2:
|
||||||
|
msg.append(f"🔴 {i['location']} \| ASIC \"*{ASICs(i['type']).name}*\" `{i['ip']}` crit status\!")
|
||||||
|
self.asics[i['mac']]['alert'] = 2
|
||||||
|
elif i['status'] == 'warn':
|
||||||
|
if CONF.get('telegram', 'warn-notify') and self.asics[i['mac']]['alert'] < 1:
|
||||||
|
msg.append(f"🟡 {i['location']} \| ASIC \"*{ASICs(i['type']).name}*\" `{i['ip']}` warn status\!")
|
||||||
|
self.asics[i['mac']]['alert'] = 1
|
||||||
|
elif i['status'] == 'ok':
|
||||||
|
if CONF.get('telegram', 'normal-notify') and self.asics[i['mac']]['alert'] != 0:
|
||||||
|
msg.append(f"🟢 {i['location']} \| ASIC \"*{ASICs(i['type']).name}*\" `{i['ip']}` normal status\!")
|
||||||
|
self.asics[i['mac']]['alert'] = 0
|
||||||
|
|
||||||
|
if self.asics[i['mac']]['tooff'] <= 0:
|
||||||
|
msg.append(f"🔴 {i['location']} \| ASIC \"*{ASICs(i['type']).name}*\" `{i['ip']}` offline\!")
|
||||||
|
del self.asics[i['mac']]
|
||||||
|
|
||||||
|
if len(msg) > 0:
|
||||||
|
await self.sendmsg('\n'.join(msg))
|
||||||
|
|
||||||
|
|
||||||
|
async def runbot(self):
|
||||||
|
try:
|
||||||
|
res = await self.sendmsg('🟢 *Notify server online\!*')
|
||||||
|
INFO(f"Telegram channel ID: {res['chat']['id']}")
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
if self.event.is_set():
|
||||||
|
SUCC('Telegram stopped!')
|
||||||
|
await self.sendmsg('🟡 Updating software\! Recommend check all running instances.')
|
||||||
|
break
|
||||||
|
|
||||||
|
await self.check()
|
||||||
|
await asyncio.sleep(15)
|
||||||
|
|
||||||
|
except error.Forbidden as e:
|
||||||
|
WARN(str(e))
|
||||||
|
except error.TimedOut as e:
|
||||||
|
WARN(str(e))
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
# os._exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
async def stopalert(self):
|
||||||
|
while 1:
|
||||||
|
if self.event2.is_set():
|
||||||
|
await self.sendmsg('🔴 *Notify server offline\!*')
|
||||||
|
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
|
|
||||||
|
async def main(self):
|
||||||
|
await asyncio.gather(self.stopalert(), self.runbot())
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
if CONF.get('telegram', 'enable'):
|
||||||
|
SUCC('Telegram started!')
|
||||||
|
|
||||||
|
self.conn = connect(
|
||||||
|
host=CONF.get('db', 'host'),
|
||||||
|
user=CONF.get('db', 'user'),
|
||||||
|
password=CONF.get('db', 'password'),
|
||||||
|
database=CONF.get('db', 'name'))
|
||||||
|
self.conn.autocommit = True
|
||||||
|
|
||||||
|
self.bot = Bot(CONF.get('telegram', 'token'))
|
||||||
|
|
||||||
|
asyncio.run(self.main())
|
||||||
|
else:
|
||||||
|
while 1:
|
||||||
|
if self.event.is_set():
|
||||||
|
SUCC('Telegram stopped!')
|
||||||
|
break
|
||||||
|
|
||||||
|
sleep(5)
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
os._exit(1)
|
|
@ -0,0 +1,54 @@
|
||||||
|
from threading import Thread
|
||||||
|
from macros import *
|
||||||
|
from config import *
|
||||||
|
from time import sleep
|
||||||
|
import urllib, os, requests
|
||||||
|
|
||||||
|
class UThread(Thread):
|
||||||
|
path = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(UThread, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
global VERSION
|
||||||
|
|
||||||
|
try:
|
||||||
|
SUCC('Updater started!')
|
||||||
|
while 1:
|
||||||
|
if not CONF.get('updates'):
|
||||||
|
sleep(1)
|
||||||
|
continue
|
||||||
|
|
||||||
|
version = requests.get('https://mirror.bitheaven.ru/main/versions/Bit.ASICmon-a').text
|
||||||
|
|
||||||
|
if version == VERSION:
|
||||||
|
sleep(300)
|
||||||
|
continue
|
||||||
|
|
||||||
|
INFO('Update found! Downloading...')
|
||||||
|
if LINUX:
|
||||||
|
with urllib.request.urlopen("https://mirror.bitheaven.ru/main/archive/Bit.ASICmon-a_linux") as upd:
|
||||||
|
with open(self.path, "wb+") as f:
|
||||||
|
INFO('Installing update...')
|
||||||
|
f.write(upd.read())
|
||||||
|
INFO('Stopping process...')
|
||||||
|
break
|
||||||
|
elif MACOS:
|
||||||
|
with urllib.request.urlopen("https://mirror.bitheaven.ru/main/archive/Bit.ASICmon-a_macos") as upd:
|
||||||
|
with open(self.path, "wb+") as f:
|
||||||
|
INFO('Installing update...')
|
||||||
|
f.write(upd.read())
|
||||||
|
INFO('Stopping process...')
|
||||||
|
break
|
||||||
|
elif MUSTDIE:
|
||||||
|
with urllib.request.urlopen("https://mirror.bitheaven.ru/main/archive/Bit.ASICmon-a_mustdie.exe") as upd:
|
||||||
|
with open(self.path, "wb+") as f:
|
||||||
|
INFO('Installing update...')
|
||||||
|
f.write(upd.read())
|
||||||
|
INFO('Stopping process...')
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
os._exit(1)
|
|
@ -0,0 +1,107 @@
|
||||||
|
from flask import Flask, send_file, request
|
||||||
|
from time import sleep
|
||||||
|
from werkzeug.serving import make_server
|
||||||
|
from threading import Thread
|
||||||
|
from mysql.connector import connect, Error
|
||||||
|
from macros import *
|
||||||
|
import os, json, config, requests, logging
|
||||||
|
|
||||||
|
|
||||||
|
class WSThread(Thread):
|
||||||
|
app = Flask(__name__)
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
def __init__(self, event):
|
||||||
|
super(WSThread, self).__init__()
|
||||||
|
self.event = event
|
||||||
|
|
||||||
|
log = logging.getLogger('werkzeug')
|
||||||
|
log.disabled = True
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
@app.route('/index')
|
||||||
|
def index():
|
||||||
|
return '<meta charset="UTF-8"><script defer src="/index.js"></script>'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/index.js')
|
||||||
|
def indexjs():
|
||||||
|
return send_file('index.js')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/curstatus.json')
|
||||||
|
def curstatus():
|
||||||
|
j = {}
|
||||||
|
j['info'] = {}
|
||||||
|
j['info']['asics'] = {}
|
||||||
|
|
||||||
|
return json.dumps(j), 200, {'ContentType':'application/json'}
|
||||||
|
|
||||||
|
|
||||||
|
def dbinit(self):
|
||||||
|
with self.conn.cursor() as c:
|
||||||
|
c.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS `ips` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`ip` VARCHAR(64) NOT NULL,
|
||||||
|
`location` VARCHAR(64) NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
c.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS `laststate` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`ip` VARCHAR(64) NOT NULL,
|
||||||
|
`mac` VARCHAR(32) NOT NULL,
|
||||||
|
`type` INT(11) NOT NULL,
|
||||||
|
`location` VARCHAR(64) NOT NULL,
|
||||||
|
`status` VARCHAR(32) NOT NULL,
|
||||||
|
`time` INT(11) NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
c.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS `asiclogs` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`ip` VARCHAR(64) NOT NULL,
|
||||||
|
`mac` VARCHAR(64) NOT NULL,
|
||||||
|
`type` VARCHAR(64) NOT NULL,
|
||||||
|
`time` INT(11) NOT NULL,
|
||||||
|
`log` TEXT NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def runweb(self):
|
||||||
|
self.server = make_server('0.0.0.0', CONF.get('port'), self.app)
|
||||||
|
self.server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
with connect(host=CONF.get('db', 'host'), user=CONF.get('db', 'user'), password=CONF.get('db', 'password')) as conn:
|
||||||
|
with conn.cursor() as c:
|
||||||
|
c.execute(f"CREATE DATABASE IF NOT EXISTS {CONF.get('db', 'name')}")
|
||||||
|
|
||||||
|
self.conn = connect(
|
||||||
|
host=CONF.get('db', 'host'),
|
||||||
|
user=CONF.get('db', 'user'),
|
||||||
|
password=CONF.get('db', 'password'),
|
||||||
|
database=CONF.get('db', 'name'))
|
||||||
|
self.dbinit()
|
||||||
|
|
||||||
|
web = Thread(target=self.runweb)
|
||||||
|
web.daemon = True
|
||||||
|
web.start()
|
||||||
|
|
||||||
|
SUCC(f"Web interface started at port {CONF.get('port')}!")
|
||||||
|
|
||||||
|
while not self.event.is_set():
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
SUCC(f"Web server stopped!")
|
||||||
|
self.server.shutdown()
|
||||||
|
except Exception as e:
|
||||||
|
CRIT(str(e))
|
||||||
|
os._exit(1)
|
Loading…
Reference in New Issue