Dislocker/dislocker_client.py
2024-07-25 19:25:47 +09:00

340 lines
15 KiB
Python

import os
import json
import tkinter.messagebox
import customtkinter
from winotify import Notification, audio
import keyboard
import subprocess
import requests
import hashlib
import string
import random
import tkinter
import threading
import signal
import sys
def init(**kwargs):
app_name = "Dislocker"
if "pc_number" in kwargs:
pc_number = int(kwargs["pc_number"])
else:
pc_number = 1
config_dir_path = "./config/"
client_config_path = config_dir_path + "client.json"
if not os.path.isfile(client_config_path):
if not os.path.isdir(config_dir_path):
os.mkdir(config_dir_path)
client_config = {
"initial": 1,
"auth_host_url": "http://localhost",
"pc_number": 1,
"master_password_hash": "",
"testing": 0
}
elif os.path.isfile(client_config_path):
with open(client_config_path, "r") as r:
client_config = json.load(r)
if client_config["initial"] == 1:
master_password = master_password_gen()
msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 初回起動を検出", message=f"初回起動のようです。\nマスターパスワードを記録しておいてください。\nこれ以降二度と表示されることはないでしょう。\n\n{master_password["password"]}\n\nまた、認証先サーバーの接続先を指定してください。ロックを解除できなくなります。")
client_config["master_password_hash"] = master_password["password_hash"]
client_config["initial"] = 0
client_config["pc_number"] = pc_number
with open(client_config_path, "w") as w:
json.dump(client_config, w, indent=4)
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title(f"{app_name} | ロック中")
if client_config["testing"] == 1:
pass
else:
self.attributes('-fullscreen', True)
self.attributes('-topmost', True)
self.block_taskmgr()
self.block_key()
self.frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent')
self.frame.grid(row=0, column=0, sticky='nsew')
lock = Lock()
def exit(self):
self.unlock_taskmgr()
self.toast()
self.destroy()
def block_key(self):
block_keys = ['ctrl', 'alt', 'windows', 'shift', 'delete']
for i in block_keys:
keyboard.block_key(i)
def block_taskmgr(self):
block = subprocess.run(['reg', 'add', 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', '/v', 'DisableTaskMgr', '/t', 'REG_DWORD', '/d', '1', '/f'])
print(block)
def unlock_taskmgr(self):
unlock = subprocess.run(['reg', 'delete', 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', '/v', 'DisableTaskMgr', '/f'])
print(unlock)
def toast(self):
resource_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resource")
success = Notification(
app_id='Dislocker',
title='ご協力ありがとうございます!',
msg='パスワード認証に成功しました。\n現在使われたパスワードは削除されます。',
icon=resource_path + r'\success.png'
)
success.set_audio(audio.Default, loop=False)
success.show()
def handler_close(self):
pass
class Lock(customtkinter.CTkToplevel):
def __init__(self):
super().__init__()
if client_config["testing"] == 1:
self.title(f'{app_name} | PC番号 {client_config["pc_number"]} | テストモード')
else:
self.title(f'{app_name} | PC番号 {client_config["pc_number"]} | ロックされています')
self.window_width = 600
self.window_height = 400
self.screen_width = self.winfo_screenwidth()
self.screen_height = self.winfo_screenheight()
self.center_x = int(self.screen_width/2 - self.window_width/2)
self.center_y = int(self.screen_height/2 - self.window_height/2)
self.geometry(f"{str(self.window_width)}x{str(self.window_height)}+{str(self.center_x)}+{str(self.center_y)}")
self.resizable(height=False, width=False)
self.attributes('-topmost', True)
self.grab_set()
self.lift()
self.protocol("WM_DELETE_WINDOW", self.handler_close)
self.emoji_font = customtkinter.CTkFont(family="Segoe UI Emoji", size=32)
self.title_font = customtkinter.CTkFont(family="meiryo", size=32, weight="bold")
self.title_small_font = customtkinter.CTkFont(family="meiryo", size=16)
self.general_font = customtkinter.CTkFont(family="meiryo", size=18)
self.general_small_font = customtkinter.CTkFont(family="meiryo", size=12)
self.textbox_font = customtkinter.CTkFont(family="meiryo", size=14)
self.button_font = customtkinter.CTkFont(family="meiryo", size=14)
self.msg_title_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent')
self.msg_title_frame.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
self.icon_title_1 = customtkinter.CTkLabel(self.msg_title_frame, text='😎', font=self.emoji_font, justify="left")
self.icon_title_1.grid(row=0, column=0, padx=10, sticky="w")
self.msg_title_1 = customtkinter.CTkLabel(self.msg_title_frame, text='ちょっと待って!!', font=self.title_font, justify="left")
self.msg_title_1.grid(row=0, column=1, padx=10, sticky="w")
self.msg_title_2 = customtkinter.CTkLabel(self.msg_title_frame, text="本当にあなたですか?", font=self.title_small_font, justify="left")
self.msg_title_2.grid(row=1, column=1, padx=10, sticky="w")
self.msg_subtitle_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent')
self.msg_subtitle_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
self.msg_subtitle_frame.grid_columnconfigure(0, weight=1)
self.msg_subtitle_1 = customtkinter.CTkLabel(self.msg_subtitle_frame, text='サインインするには、Discordのダイレクトメッセージに送信された\nパスワードを入力してください。', font=self.general_font, justify="left")
self.msg_subtitle_1.grid(row=0, column=0, padx=10, sticky="ew")
self.msg_subtitle_2 = customtkinter.CTkLabel(self.msg_subtitle_frame, text='※ パスワードの有効期限は23:59までです。', font=self.general_small_font, justify="left")
self.msg_subtitle_2.grid(row=1, column=0, padx=10, sticky="w")
self.input_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent')
self.input_frame.grid(row=2, column=0, padx=10, pady=10, sticky="nsew")
self.input_frame.columnconfigure(0, weight=1)
self.password_entry = customtkinter.CTkEntry(self.input_frame, placeholder_text='パスワード', show='*', font=self.textbox_font)
self.password_entry.grid(row=0, column=0, padx=10, sticky="ew")
self.password_entry.bind("<Return>", self.auth_start_ev)
self.button_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent')
self.button_frame.grid(row=3, column=0, padx=10, pady=10, sticky="nsew")
self.button_frame.columnconfigure(0, weight=3)
self.button_frame.columnconfigure(1, weight=1)
self.button_frame.columnconfigure(2, weight=1)
self.signin_button = customtkinter.CTkButton(self.button_frame, text='サインイン', command=self.auth_start, font=self.button_font)
self.signin_button.grid(row=0, column=2, padx=10, sticky="e")
self.logout_button = customtkinter.CTkButton(self.button_frame, text='サインアウト', command=self.logout, font=self.button_font)
self.logout_button.grid(row=0, column=1, padx=10, sticky="e")
self.signin_button = customtkinter.CTkButton(self.button_frame, text='ヘルプ', command=self.help_dummy, font=self.button_font)
self.signin_button.grid(row=0, column=0, padx=10, sticky="w")
self.keyboard_listener_thread = threading.Thread(target=self.keyboard_listener)
self.keyboard_listener_thread.daemon = True
self.keyboard_listener_thread.start()
def help_wakeup(self):
help = Help()
def keyboard_listener(self):
keyboard.add_hotkey('ctrl+shift+q', self.exit)
def hash_genarate(self, source):
hashed = hashlib.md5(source.encode())
return hashed.hexdigest()
def auth_start(self):
auth_thread = threading.Thread(target=self.auth)
auth_thread.daemon = True
auth_thread.start()
def auth_start_ev(self, event):
auth_thread = threading.Thread(target=self.auth)
auth_thread.daemon = True
auth_thread.start()
def auth(self):
password = str(self.password_entry.get())
if len(password) == 10:
print("マスターパスワードで認証を試行します。")
master_password_hash = self.hash_genarate(str(self.password_entry.get()))
if client_config["master_password_hash"] == master_password_hash:
print("マスターパスワードで認証しました。")
self.exit()
else:
print("マスターパスワードで認証できませんでした。")
print("認証サーバーにアクセスします。")
auth_url = client_config["auth_host_url"] + "/verify"
auth_json = {
"pc_number": int(client_config["pc_number"]),
"password": self.hash_genarate(str(self.password_entry.get()))
}
try:
responce = requests.post(auth_url, json=auth_json)
if responce.status_code == 200:
print("認証サーバー経由で認証しました。")
self.exit()
else:
print("認証サーバー経由での認証に失敗しました。")
self.withdraw()
msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 誤ったパスワード", message=f"パスワードが間違っています!")
self.msg_subtitle_1.configure(text='パスワードが間違っています! ')
self.deiconify()
except:
print("認証サーバーにアクセスできません。マスターパスワードで認証を試行します。")
master_password_hash = self.hash_genarate(str(self.password_entry.get()))
if client_config["master_password_hash"] == master_password_hash:
print("マスターパスワードで認証しました。")
self.exit()
else:
print("マスターパスワードで認証できませんでした。")
self.withdraw()
msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | ネットワークエラー", message=f"認証サーバーにアクセスできませんでした。\n続行するには、マスターパスワードを入力してください。")
self.msg_subtitle_1.configure(text='ネットワークエラーが発生しています。\n続行するには、マスターパスワードを入力して下さい。 ')
self.deiconify()
def logout(self):
app.unlock_taskmgr()
self.destroy()
logout_command = subprocess.run(['shutdown', '/l', '/f'])
print(logout_command)
def handler_close(self):
pass
def help_dummy(self):
self.withdraw()
msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 未実装", message=f"ヘルプページは製作途中です。\nDiscordサーバーの指示に従って、認証を進めてください。")
self.deiconify()
def exit(self):
self.destroy()
app.exit()
class Help(customtkinter.CTkToplevel):
def __init__(self):
super().__init__()
if client_config["testing"] == 1:
self.title(f'{app_name} | ヘルプ | テストモード')
else:
self.title(f'{app_name} | ヘルプ')
self.iconbitmap("./resource/icon/dislocker.ico")
self.geometry("600x400")
self.resizable(height=False, width=False)
self.attributes('-topmost', True)
self.grab_set()
self.lift()
self.protocol("WM_DELETE_WINDOW", self.handler_close)
msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 未実装", message=f"ヘルプページは製作途中です。\nDiscordサーバーの指示に従って、認証を進めてください。")
self.destroy()
def handler_close(self):
self.destroy()
class Monitor():
def __init__(self) -> None:
pass
def start(self):
monitor_thread = threading.Thread(target=self.run)
monitor_thread.start()
def signal_handler(self):
print("停止処理を実行。")
stop_url = client_config["auth_host_url"] + "/stop"
stop_json = {
"pc_number": int(client_config["pc_number"])
}
try:
responce = requests.post(stop_url, json=stop_json)
if responce.status_code == 200:
print("停止処理は成功しました。")
else:
print("内部エラーにより停止処理に失敗しました。")
result_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | エラー", message=f"内部エラーにより停止処理に失敗しました。\nDiscordサーバーの指示に従って、停止処理を自身で行ってください。")
except:
print("ネットワークエラーにより停止処理に失敗しました。")
result_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | エラー", message=f"ネットワークエラーにより停止処理に失敗しました。\nDiscordサーバーの指示に従って、停止処理を自身で行ってください。")
finally:
self.shutdown()
def shutdown(self):
logout_command = subprocess.run(['shutdown', '/s', '/t', '1'])
def run(self):
signal.signal(signal.SIGTERM, self.signal_handler)
def master_password_gen():
numbers = string.digits # (1)
password = ''.join(random.choice(numbers) for _ in range(10)) # (2)
password_hash = hashlib.md5(password.encode()).hexdigest()
result = {"password": password, "password_hash": password_hash}
return result
if __name__ == '__main__':
args = sys.argv
if len(args) >= 2:
if args[1] == "stop":
monitor = Monitor()
monitor.signal_handler()
elif args[1] == "setup":
init(pc_number=args[2])
else:
print("引数エラー。")
else:
init()
app = App()
app.protocol("WM_DELETE_WINDOW", app.handler_close)
app.mainloop()