Wipe branch
This commit is contained in:
		
							
								
								
									
										162
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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/ | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -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. | ||||||
							
								
								
									
										3
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | MAKE BACKUPS FOR LICENSE SERVER!!! | ||||||
|  | Enable LED when WARN or CRIT | ||||||
|  | Switch debug remotely | ||||||
							
								
								
									
										27
									
								
								asics.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								asics.py
									
									
									
									
									
										Normal file
									
								
							| @ -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() | ||||||
							
								
								
									
										4
									
								
								build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4
									
								
								build.sh
									
									
									
									
									
										Executable file
									
								
							| @ -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 | ||||||
							
								
								
									
										334
									
								
								checker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										334
									
								
								checker.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
							
								
								
									
										3
									
								
								checkmem
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								checkmem
									
									
									
									
									
										Executable file
									
								
							| @ -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"' | ||||||
							
								
								
									
										43
									
								
								cleaner.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								cleaner.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
							
								
								
									
										20
									
								
								colors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								colors.py
									
									
									
									
									
										Normal file
									
								
							| @ -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" | ||||||
							
								
								
									
										73
									
								
								config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								config.json
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								config.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | APPNAME='Bit.ASICmon' | ||||||
|  | VERSION='v0.1.1a' | ||||||
|  | DEBUGGING=True | ||||||
							
								
								
									
										12
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
							
								
								
									
										223
									
								
								index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								index.js
									
									
									
									
									
										Normal file
									
								
							| @ -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() | ||||||
							
								
								
									
										76
									
								
								license.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								license.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
							
								
								
									
										
											BIN
										
									
								
								log/.2023-05-10.log.swp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								log/.2023-05-10.log.swp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										97
									
								
								macros.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								macros.py
									
									
									
									
									
										Normal file
									
								
							| @ -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)) | ||||||
							
								
								
									
										95
									
								
								main.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								main.py
									
									
									
									
									
										Executable file
									
								
							| @ -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) | ||||||
							
								
								
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | flask | ||||||
|  | python-telegram-bot | ||||||
|  | pyinstaller | ||||||
|  | pyarmor<8.0.0 | ||||||
							
								
								
									
										125
									
								
								telegrambot.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								telegrambot.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
							
								
								
									
										54
									
								
								updater.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								updater.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
							
								
								
									
										107
									
								
								webserver.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								webserver.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
		Reference in New Issue
	
	Block a user