Compare commits

...

161 commits

Author SHA1 Message Date
84d1b0cb59 使用目的がカスタムで送信できないバグを修正 2024-09-17 14:02:14 +09:00
9389441262 configにstop用のeraserキーを追加 2024-09-10 18:30:33 +09:00
45d6b9f3c8 testingの成果をマージ 2024-09-10 18:17:00 +09:00
45b1b767ac tempディレクトリは追跡外に 2024-09-10 18:13:10 +09:00
8658955998 tempディレクトリ削除 2024-09-10 18:12:40 +09:00
8ade781d4b 使用理由が聞かれないバグを修正 2024-09-09 14:20:26 +09:00
cf47a5b2ad 自前のボタンを追加 2024-09-09 14:04:19 +09:00
c08afa08c3 使用目的のプリセットを追加 2024-09-08 15:14:06 +09:00
5a709197e7 configの内容をtestingからもってくる 2024-09-08 15:03:16 +09:00
487dfbd96d delete_appdataの修正をtestingからもってくる 2024-09-06 14:47:31 +09:00
7fc3ca701c 大幅な仕様変更のためtestingをmainにマージ 2024-08-22 22:46:41 +09:00
66f39eb293 ウィンドウのアイコンを適用できるように 2024-08-22 11:46:52 +09:00
19cf8e195c 多重起動防止の処理をロールバック 2024-08-22 11:18:04 +09:00
9cb5f22cfc ボタンの色を修正
init周りを修正
2024-08-21 13:21:08 +09:00
d7094656a3 アイコンのパスを修正 2024-08-21 13:11:29 +09:00
19afc4a57a issue #7 の解決案(ボタン無効化)を適用 2024-08-21 12:59:45 +09:00
9dc9938732 READMEを更新 2024-08-21 12:16:50 +09:00
04ae26862f clientの動作確認用 2024-08-21 12:16:41 +09:00
111686c257 念のため他のテーブルの型も修正 2024-08-19 22:57:07 +09:00
8a88a1a31f ユーザー登録時に文字数超過のエラーが出ることへの暫定的な対応 2024-08-19 10:01:49 +09:00
8019674b13 Monitorのpc_listのprintを無効化 2024-08-16 13:23:12 +09:00
60d4fc9c81 MonitorのResultがNONEだった時のログを出力しないように 2024-08-16 13:15:10 +09:00
7a1aa6a7db configファイルを明示的にUTF-8で読み書きするように 2024-08-15 22:46:55 +09:00
f7ecdc535f monitorの時間類の設定をconfigファイルへ移動 2024-08-15 22:41:38 +09:00
3073f12e6c 多重起動防止の処理をいったんやめにする 2024-08-14 00:03:47 +09:00
f304346426 initのif文を修正 2024-08-13 14:43:47 +09:00
b5fc778715 fstopに理由を要求するように
使用停止忘れのための停止処理を追加
2024-08-11 15:25:01 +09:00
3aad819485 生成されるconfigファイルの内容を合わせる 2024-08-10 18:47:16 +09:00
c55fd3162b データベースの環境変数をenvファイルに移動 2024-08-10 18:33:48 +09:00
7f92544102 volumeが逆だったので修正 2024-08-10 18:30:12 +09:00
dca3cad736 それぞれのDockerfileとcompose.ymlを追加 2024-08-10 18:27:24 +09:00
a10f5c47e9 Flaskのデバッグモードを明示的に無効化 2024-08-10 18:21:35 +09:00
ba9f227e09 タイムスタンプのバグを修正
自動停止処理が時間をおいて起動するように
2024-08-10 18:10:35 +09:00
b9d9e797d3 環境変数をミスってたので修正 2024-08-10 17:38:09 +09:00
a9d15357c4 PCの使用履歴のシートを除外 2024-08-10 17:37:49 +09:00
2663ad2ff6 ユーザーとして登録されていないときに停止ボタンを押したときに内部エラーと表示される仕様を変更
定期監視の一部仕様変更
2024-08-10 17:32:14 +09:00
425edead34 Dockerで動かす用 2024-08-10 17:06:53 +09:00
c4b4718583 終了処理にself忘れてた 2024-08-09 11:30:03 +09:00
16ea317730 引数の名前修正 2024-08-09 11:09:06 +09:00
8deb85e9fb 停止処理をできるように 2024-08-09 10:49:48 +09:00
b47305439c Riotのデータを削除するように 2024-08-08 00:31:34 +09:00
8e8c052b0c 多重起動防止のための処理追加 2024-08-07 11:37:23 +09:00
b5285c0f21 EAとSteamのデータを削除するように 2024-08-07 10:42:43 +09:00
3bf7918bb4 アクティビティの変更を忘れていた 2024-07-26 13:29:09 +09:00
a8f9ad0ec6 アクティビティを設定できるように 2024-07-26 13:23:21 +09:00
95e8855c27 アプリデータの削除処理を追加 2024-07-26 11:45:13 +09:00
a9109dba0a タイムアウト処理の実装 2024-07-25 23:44:47 +09:00
e48a0fc3c2 PC番号をもっと大きく表示するように 2024-07-25 21:32:08 +09:00
2edd37fbfd ショートカットの作成処理を修正 2024-07-25 21:13:50 +09:00
765a0bd2ba pyinstaller用のコマンドを追加 2024-07-25 20:29:07 +09:00
348fa96759 pyinstallerのカレントディレクトリ問題を対策 2024-07-25 20:19:03 +09:00
5a1ac0e3c1 クライアントへインストール用のスクリプトを追加 2024-07-25 20:05:01 +09:00
5653b79906 testing_2ブランチの変更をマージ 2024-07-25 20:02:12 +09:00
db9afe38f2 Merge branch 'testing_2' into testing 2024-07-25 19:59:38 +09:00
b630d7aa98 カレントディレクトリの処理を追加 2024-07-25 19:57:38 +09:00
a193d25877 クライアント用 2024-07-25 19:57:09 +09:00
1cc6d1d60f 引数setupを追加 2024-07-25 19:35:22 +09:00
ad5f84d238 initのテスト 2024-07-25 19:25:47 +09:00
e23662422e 入力欄でEnterを押したときの処理を追加 2024-07-25 16:17:33 +09:00
451ea60140 monitorを無効化 2024-07-25 14:31:18 +09:00
e9a322f839 デバイス番号を10番までに変更 2024-07-25 14:24:24 +09:00
7b3708dd51 認証ウィンドウを中央に配置
メッセージウィンドウが表示できるように
2024-07-25 10:44:07 +09:00
41788bb622 アイコンを追加 2024-07-24 23:45:42 +09:00
78114d3f04 引数stopで使用停止処理とシャットダウンを同時に行えるように 2024-07-24 23:34:49 +09:00
ab7147ffde シャットダウン時の使用停止処理を追加 2024-07-23 15:15:44 +09:00
18de0e7b85 停止処理を追加 2024-07-23 15:15:24 +09:00
34ce84532d タイムアウトまでの時間を5分に延長 2024-07-22 20:58:04 +09:00
c1ce0ceb5b ログアウト時にタスクマネージャーを復活するように 2024-07-22 12:00:40 +09:00
e3ff1a78b2 printデバッグ用 2024-07-22 11:58:05 +09:00
45323b2f48 機能修正 2024-07-22 11:57:50 +09:00
b908ce17ed ボタン操作ができるように、タイムアウト機能の実装、スラッシュコマンド対応への準備 2024-07-21 16:36:16 +09:00
f7b6caca30 メッセージ内容を修正 2024-07-20 23:18:36 +09:00
1bf03a600e PCの登録がチャンネル上でできるように 2024-07-20 23:08:27 +09:00
7129a08508 PCの登録を追加 2024-07-20 20:12:19 +09:00
585ddf8cf1 ユーザー登録をユーザー自身でもできるように 2024-07-20 20:11:11 +09:00
00a4c00fcf 認証サーバーがserver.jsonの設定ファイルを読み込むように 2024-07-20 20:10:18 +09:00
707e2e3ed6 ログアウトボタンの表記をサインアウトに変更 2024-07-08 11:42:19 +09:00
d76ba87887 ヘルプ、サインアウトの関数を追加 2024-07-08 11:41:38 +09:00
889c464c5f 使用履歴出力のreturn修正 2024-07-08 11:30:20 +09:00
abf03bd4d6 Excelシートで出力できるように 2024-07-08 11:23:19 +09:00
b37573a371 出力されるcsvファイルが履歴のid順で並ぶように 2024-07-07 18:15:15 +09:00
9f59b8dab1 csv出力のコードを修正その5 2024-07-07 18:09:25 +09:00
ce3bf1cd3c csv出力のコードを修正その4 2024-07-07 18:07:53 +09:00
e1fee8b2e5 csv出力のコードを修正その3 2024-07-07 18:00:49 +09:00
d640155e75 csv出力のコード修正その2 2024-07-07 17:48:54 +09:00
8b7e7a2a65 csv出力のコード修正 2024-07-07 17:43:36 +09:00
f26e0d9ad9 force_stopのreturnを追加 2024-07-07 17:40:03 +09:00
c9b7259728 fstopの引数が間違っていたのを修正 2024-07-07 17:37:53 +09:00
aeb9553113 int判定を修正 2024-07-07 17:34:51 +09:00
80a266142b printfデバックを削除、チャンネルIDがintで記述されていない場合は起動しなくなるように 2024-07-07 17:24:33 +09:00
62537a1038 printデバック用 2024-07-07 17:10:05 +09:00
57f428e910 使用登録の強制停止を実装 2024-07-07 16:35:57 +09:00
18970c9079 メッセージの文言を変更 2024-07-07 16:08:17 +09:00
9e4f39f8fa ようやく理解した selfを追加 2024-07-07 16:04:46 +09:00
60af46a32b returnを回避 2024-07-07 15:55:14 +09:00
bf418936d4 tryを修正 2024-07-07 15:52:46 +09:00
73502817e0 きれそう 2024-07-07 15:48:43 +09:00
4ecf209e7e データベース接続時の例外処理を追加 2024-07-07 15:43:43 +09:00
f7f7033ea5 class名だめだったから修正 2024-07-07 15:33:30 +09:00
2ef4d639bf やっぱ初期処理と関連の変数のクラスを分けた 2024-07-07 15:24:55 +09:00
a16a0424dc 初期挙動をbotクラスに統合、csvファイルの出力ができるように 2024-07-07 15:02:54 +09:00
41e85cb6bc csv出力のテスト 2024-07-07 15:02:11 +09:00
dde933fc14 名前変更に伴い削除 2024-07-07 15:01:24 +09:00
5de39f92a4 PC使用履歴のテーブルからcsvファイルを作成できるように 2024-07-07 13:50:57 +09:00
7d06ad18c4 設定がひとつのjsonファイルで完結するように 2024-07-07 13:49:53 +09:00
0a7bb89a98 GUIを再設計 (thanks for 八咫烏!) 2024-07-04 20:40:03 +09:00
5cd099a411 GUIデザインをコンセプトに近づける 2024-07-01 12:51:17 +09:00
e3d9f73fd7 終了用のショートカットキーを追加 2024-06-16 17:03:15 +09:00
a6082c1f0a たのむ 2024-06-10 14:52:27 +09:00
18076cdf31 あーーーーー 2024-06-10 14:38:33 +09:00
1147b50fd1 新規ユーザー(使用履歴がない)ときの処理を追加 2024-06-10 14:31:40 +09:00
3e4798e8d5 テスト用設定の追加 2024-06-10 13:46:42 +09:00
f6c7342002 initialを修正 2024-06-10 13:41:05 +09:00
0f21c03003 マスターパスワードの照合を追加 2024-06-10 13:34:22 +09:00
7087090211 クライアント用のライブラリを追加 2024-06-10 13:34:10 +09:00
d8f9717304 初回起動時の処理を追加 2024-06-10 13:27:21 +09:00
0c4da769ac 動かないのを修正したい 2024-06-09 20:15:06 +09:00
260779fca3 初回の処理を修正 2024-06-09 20:11:20 +09:00
5c15c0b092 ユーザー登録が指定テキストチャンネルでできるように 2024-06-09 19:56:59 +09:00
de96b457eb 無駄なreturnをなくす 2024-06-09 19:04:32 +09:00
d03d5ec4ef 返り値を修正 2024-06-09 18:49:46 +09:00
0427c021a0 諸々の処理を関数に導入 2024-06-09 18:39:42 +09:00
128ebc88b3 背景のボタンを削除 2024-06-08 22:42:26 +09:00
5133e550ed 背景に触れないように 2024-06-08 22:38:53 +09:00
7d8c14c080 db_configのままだったのを修正 2024-06-08 22:17:18 +09:00
7093733a69 int型を明示 2024-06-08 22:07:51 +09:00
dedccf5f95 ダミーページへの誘導を削除 2024-06-08 22:07:30 +09:00
9b8d9dac2c どのIPからも応答できるように 2024-06-08 20:28:00 +09:00
94d4e16cfd 一応ダミーページを返すように 2024-06-08 20:26:26 +09:00
c1e9ca4878 ダミー用 2024-06-08 20:26:11 +09:00
9dec6938dd パスワードをハッシュ化してデータベースに保存するように 2024-06-08 19:19:07 +09:00
26902aa0b3 認証用サーバーにつなぐように 2024-06-08 19:18:50 +09:00
32a183244f 認証用サーバーを追加 2024-06-08 19:18:35 +09:00
bd12378f3c test.pyを追跡外に 2024-06-08 19:18:16 +09:00
451d7facc5 psycopg2をバイナリ版でインストールするように 2024-06-08 11:50:23 +09:00
e58ae29ca7 パスワードの入力を非表示にする 2024-06-08 09:59:33 +09:00
5b9395a1d2 block_taskmgr関数を呼び出してなかったので修正 2024-06-08 09:46:24 +09:00
a52533f34b レジストリのパスを修正 2024-06-08 09:40:37 +09:00
0b03527e33 レジストリでタスクマネージャーの実行をブロックするように 2024-06-08 09:33:54 +09:00
4d391c7a9a テスト用 2024-06-07 22:29:00 +09:00
3f1611dde1 タスクマネージャー対策への第一歩 2024-06-07 22:28:51 +09:00
bdaf1ac0d8 bot_configの修正 2024-06-07 12:29:49 +09:00
f2679a8307 特定のキーをブロックするように 2024-06-06 23:08:15 +09:00
e849afbf91 クライアント用にkeyboard追加 2024-06-06 23:07:43 +09:00
7f6bbfc420 フリーズを修正 2024-06-06 20:04:03 +09:00
4cbd7e1cf1 クライアント用に追加 2024-06-06 19:42:26 +09:00
7f7b8733b6 仮でクライアント用 2024-06-06 19:40:41 +09:00
639fd23970 認証完了時のトースト通知用 2024-06-06 19:40:33 +09:00
f748e48517 PC用のテーブルを作成 2024-06-05 18:38:14 +09:00
0436035b0a パスワードを生成できるように 2024-06-05 18:37:50 +09:00
b01ce7b373 いらないコードを削除、configにログ送信先チャンネルのIDを記載するように 2024-06-05 16:13:57 +09:00
b084ead6b7 改行を修正 2024-06-05 11:36:37 +09:00
a81327a680 データベースへの接続バグを修正 2024-06-05 11:27:57 +09:00
22bc5fe240 ワンタイムパスワード用にマージ予定 2024-06-05 11:22:35 +09:00
ace0af03d6 for_psqlブランチをマージ 2024-06-04 23:06:11 +09:00
7cfd94053e for_psqlブランチをマージ 2024-06-04 23:06:03 +09:00
20d9d15063 pull 2024-06-04 23:04:10 +09:00
ab92ecfb4a sqlite用にちょっと変更 2024-06-04 14:17:35 +09:00
83e44da330 フォルダだけ同期するように 2024-06-04 14:17:11 +09:00
6d4717e471 データベースをSQLiteに変更 2024-06-04 13:26:28 +09:00
b57b1f8b5c .gitignoreの変更 2024-06-04 13:25:57 +09:00
26 changed files with 1596 additions and 143 deletions

4
.gitignore vendored
View file

@ -162,3 +162,7 @@ cython_debug/
config/ config/
db/ db/
test.py
data/
export/
temp/

22
Dockerfile_auth Normal file
View file

@ -0,0 +1,22 @@
FROM python:3
USER root
RUN mkdir /dislocker
RUN apt-get update
RUN apt-get -y install locales && \
localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
RUN apt-get install -y nano less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN python -m pip install flask psycopg2-binary requests
WORKDIR /dislocker
CMD python -u ./dislocker_auth.py

22
Dockerfile_bot Normal file
View file

@ -0,0 +1,22 @@
FROM python:3
USER root
RUN mkdir /dislocker
RUN apt-get update
RUN apt-get -y install locales && \
localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
RUN apt-get install -y nano less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN python -m pip install discord.py psycopg2-binary requests openpyxl
WORKDIR /dislocker
CMD python -u ./dislocker.py

View file

@ -1,2 +1,9 @@
# Dislocker # Dislocker
課題研究用リポジトリ
# 環境構築
## サーバー側
基本的にはDocker上での起動を推奨します。
このリポジトリをクローンし、`docker compose up -d`で起動すると一式のコンテナが起動します。
データベースだけを起動したい場合は、ファイルに`compose_db.yml`を指定してください。
## クライアント側
pyinstallerでビルドしたものを、起動してください。

45
compose.yml Normal file
View file

@ -0,0 +1,45 @@
services:
bot:
build:
context: "./"
dockerfile: "Dockerfile_bot"
restart: always
environment:
- TZ=Asia/Tokyo
depends_on:
- db
volumes:
- ./:/dislocker
networks:
- dislocker_network
auth:
build:
context: "./"
dockerfile: "Dockerfile_auth"
restart: always
environment:
- TZ=Asia/Tokyo
volumes:
- ./:/dislocker
ports:
- 12244:5000
networks:
- dislocker_network
db:
image: postgres:alpine3.20
restart: always
environment:
- TZ=Asia/Tokyo
volumes:
- ./data/db:/var/lib/postgresql/data
ports:
- 12245:5432
env_file:
- ./data/.env
networks:
- dislocker_network
networks:
dislocker_network:

18
compose_db.yml Normal file
View file

@ -0,0 +1,18 @@
services:
db:
image: postgres:alpine3.20
restart: always
environment:
- TZ=Asia/Tokyo
- POSTGRES_DB=dislocker
- POSTGRES_USER=dislocker
- POSTGRES_PASSWORD=Password
volumes:
- ./data/db:/var/lib/postgresql/data
ports:
- 12245:5432
networks:
- dislocker_network
networks:
dislocker_network:

View file

@ -1,136 +1,843 @@
import json import json
import discord import discord
from discord import Interaction, TextStyle, app_commands
from discord.ui import TextInput, View, Modal
import os import os
import psycopg2 import psycopg2
import datetime from psycopg2 import sql
import hashlib
import string
import random
from datetime import datetime, timedelta
from openpyxl import Workbook
import threading
import time
class Database(): class DL():
def __init__(self): def __init__(self):
self.db = psycopg2.connect("host=localhost dbname=dislocker user=suti7 password=testing") self.config_dir_path = "./config/"
self.export_dir_path = "./export/"
self.server_config_path = self.config_dir_path + "server.json"
try:
if not os.path.isdir(self.config_dir_path):
print("config ディレクトリが見つかりません... 作成します。")
os.mkdir(self.config_dir_path)
def register(self, username, userid): if not os.path.isfile(self.server_config_path):
cursor = self.db.cursor() print("config ファイルが見つかりません... 作成します。")
insert_sql = "INSERT INTO user_list (discord_username, discord_userid) VALUES (%s, %s);" self.server_config = {
cursor.execute(insert_sql, (username, userid)) "db": {
self.db.commit() "host": "localhost",
"port": "5432",
"db_name": "dislocker",
"username": "user",
"password": "password"
},
"bot": {
"token": "TYPE HERE BOTS TOKEN KEY",
"activity": {
"name": "Dislocker",
"details": "ロック中...",
"type": "playing",
"state": "ロック中..."
},
"log_channel_id" : "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)",
"config_channel_id": "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)",
"config_public_channel_id": "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)",
"monitor": {
"search_frequency": 1,
"allowable_time": 180,
"fstop_time": "21:00:00"
},
"preset_games": ["TEST1", "TEST2", "TEST3", "TEST4", "TEST5"],
"admin_user_id": "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)",
"debug": False
}
}
with open(self.server_config_path, "w", encoding="utf-8") as w:
json.dump(self.server_config, w, indent=4, ensure_ascii=False)
elif os.path.isfile(self.server_config_path):
with open(self.server_config_path, "r", encoding="utf-8") as r:
self.server_config = json.load(r)
print("config ファイルを読み込みました。")
if not os.path.isdir(self.export_dir_path):
print("export ディレクトリが見つかりません... 作成します。")
os.mkdir(self.export_dir_path)
if type(self.server_config["bot"]["log_channel_id"]) is not int or type(self.server_config["bot"]["config_channel_id"]) is not int:
print("config ファイル内でチャンネルIDがint型で記述されていません。int型で記述して、起動してください。")
self.init_result = "not_int"
else:
self.db = psycopg2.connect(f"host={self.server_config['db']['host']} dbname={self.server_config['db']['db_name']} port={self.server_config['db']['port']} user={self.server_config['db']['username']} password={self.server_config['db']['password']}")
cursor = self.db.cursor()
self.pc_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
self.preset_games = self.server_config["bot"]["preset_games"]
cursor.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'club_member')")
find_club_member_table = cursor.fetchall()
print(find_club_member_table)
if find_club_member_table[0][0] == False:
cursor.execute("CREATE TABLE club_member (id SERIAL NOT NULL, name VARCHAR(128) NOT NULL, discord_username VARCHAR(128) NOT NULL, discord_userid VARCHAR(18) NOT NULL, PRIMARY KEY (id))")
self.db.commit()
cursor.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'pc_list')")
find_pc_list_table = cursor.fetchall()
print(find_pc_list_table)
if find_pc_list_table[0][0] == False:
cursor.execute("CREATE TABLE pc_list (pc_number INTEGER NOT NULL, using_user_id INTEGER, password_hash VARCHAR(32), PRIMARY KEY (pc_number), FOREIGN KEY (using_user_id) REFERENCES club_member(id))")
for i in range(10):
print(i)
cursor.execute("INSERT INTO pc_list (pc_number) VALUES (%s)", (i + 1,))
self.db.commit()
cursor.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'pc_usage_history')")
find_pc_usage_history_table = cursor.fetchall()
print(find_pc_usage_history_table)
if find_pc_usage_history_table[0][0] == False:
cursor.execute("CREATE TABLE pc_usage_history (id SERIAL NOT NULL, member_id INTEGER NOT NULL, pc_number INTEGER NOT NULL, device_number INTEGER NOT NULL, start_use_time TIMESTAMP NOT NULL, end_use_time TIMESTAMP, use_detail VARCHAR(128), bot_about VARCHAR(128), PRIMARY KEY (id), FOREIGN KEY (member_id) REFERENCES club_member(id), FOREIGN KEY (pc_number) REFERENCES pc_list(pc_number))")
self.db.commit()
cursor.close()
self.init_result = "ok"
except (Exception) as error:
print("初回処理でエラーが発生しました。\nエラー内容\n" + str(error))
self.init_result = "error"
finally:
pass
def get_username(self, username, password):
cursor = self.db.cursor()
get_sql = "SELECT * FROM web_auth WHERE username = %s AND password = %s "
cursor.execute(get_sql, (username, password))
result = cursor.fetchall()
print(result)
if result == []:
return 1
elif result[0][1] == username and result[0][2] == password:
return 0
class Bot(discord.Client): class Bot(discord.Client):
def db_connect(self, host, db, user, password):
self.db = psycopg2.connect(f"host={host} dbname={db} user={user} password={password}") def password_generate(self, length):
numbers = string.digits # (1)
password = ''.join(random.choice(numbers) for _ in range(length)) # (2)
return password
def hash_genarate(self, source):
hashed = hashlib.md5(source.encode())
return hashed.hexdigest()
def register(self, **kwargs):
try:
user_info = {
"id": str(kwargs["user_id"]),
"name": str(kwargs["name"]),
"display_name": str(kwargs["display_name"]),
"pc_number": int(kwargs["pc_number"]),
"device_number": int(kwargs["device_number"]),
"detail": None
}
if "detail" in kwargs:
user_info["detail"] = str(kwargs["detail"])
else:
pass
#パスワード生成、ハッシュ化
password = self.password_generate(4)
password_hash = self.hash_genarate(password)
print("password generated")
#メンバーリストと送信者を照合
cursor = dislocker.db.cursor()
cursor.execute("SELECT * FROM club_member WHERE discord_userid = %s", (user_info["id"],))
user_record = cursor.fetchall()
#ユーザーデータがなかったら(未登録の場合)
if not user_record:
result = {"result": "user_data_not_found"}
#ユーザーデータが見つかった場合(登録済みの場合)
else:
print("found user data")
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id=%s ORDER BY id DESC LIMIT 1", (user_record[0][0],))
pc_usage_history_record = cursor.fetchall()
if pc_usage_history_record:
print("used")
if pc_usage_history_record[0][5] == None:
result = {"result": "pc_already_in_use_by_you", "pc_number": str(pc_usage_history_record[0][2]), "device_number": str(pc_usage_history_record[0][3]), "start_time": str(pc_usage_history_record[0][4]), "detail": str(pc_usage_history_record[0][6])}
else:
cursor.execute("SELECT * FROM pc_list WHERE pc_number=%s", (user_info["pc_number"],))
pc_list_record = cursor.fetchall()
if not pc_list_record[0][1] == None:
result = {"result": "pc_already_in_use_by_other"}
else:
if user_info["detail"] == None:
cursor.execute("INSERT INTO pc_usage_history (member_id, pc_number, device_number, start_use_time) VALUES (%s, %s, %s, clock_timestamp())", (user_record[0][0], user_info["pc_number"], user_info["device_number"]))
#使用用途があるとき
else:
cursor.execute("INSERT INTO pc_usage_history (member_id, pc_number, device_number, start_use_time, use_detail) VALUES (%s, %s, %s, clock_timestamp(), %s)", (user_record[0][0], user_info["pc_number"], user_info["device_number"], user_info["detail"]))
cursor.execute("UPDATE pc_list SET using_user_id = %s WHERE pc_number = %s", (user_record[0][0], user_info["pc_number"]))
cursor.execute("UPDATE pc_list SET password_hash = %s WHERE pc_number = %s", (password_hash, user_info["pc_number"]))
dislocker.db.commit()
result = {"result": "ok", "password": str(password), "name": str(user_record[0][1])}
else:
print("unused")
cursor.execute("SELECT * FROM pc_list WHERE pc_number=%s", (user_info["pc_number"],))
pc_list_record = cursor.fetchall()
if pc_list_record:
if not pc_list_record[0][1] == None:
result = {"result": "pc_already_in_use_by_other"}
else:
if user_info["detail"] == None:
cursor.execute("INSERT INTO pc_usage_history (member_id, pc_number, device_number, start_use_time) VALUES (%s, %s, %s, clock_timestamp())", (user_record[0][0], user_info["pc_number"], user_info["device_number"]))
#使用用途があるとき
else:
cursor.execute("INSERT INTO pc_usage_history (member_id, pc_number, device_number, start_use_time, use_detail) VALUES (%s, %s, %s, clock_timestamp(), %s)", (user_record[0][0], user_info["pc_number"], user_info["device_number"], user_info["detail"]))
cursor.execute("UPDATE pc_list SET using_user_id = %s WHERE pc_number = %s", (user_record[0][0], user_info["pc_number"]))
cursor.execute("UPDATE pc_list SET password_hash = %s WHERE pc_number = %s", (password_hash, user_info["pc_number"]))
dislocker.db.commit()
result = {"result": "ok", "password": str(password), "name": str(user_record[0][1])}
else:
if user_info["detail"] == None:
cursor.execute("INSERT INTO pc_usage_history (member_id, pc_number, device_number, start_use_time) VALUES (%s, %s, %s, clock_timestamp())", (user_record[0][0], user_info["pc_number"], user_info["device_number"]))
#使用用途があるとき
else:
cursor.execute("INSERT INTO pc_usage_history (member_id, pc_number, device_number, start_use_time, use_detail) VALUES (%s, %s, %s, clock_timestamp(), %s)", (user_record[0][0], user_info["pc_number"], user_info["device_number"], user_info["detail"]))
cursor.execute("UPDATE pc_list SET using_user_id = %s WHERE pc_number = %s", (user_record[0][0], user_info["pc_number"]))
cursor.execute("UPDATE pc_list SET password_hash = %s WHERE pc_number = %s", (password_hash, user_info["pc_number"]))
dislocker.db.commit()
result = {"result": "ok", "password": str(password), "name": str(user_record[0][1])}
except Exception as error:
print("登録処理中にエラーが発生しました。\nエラー内容")
print(str(error.__class__.__name__))
print(str(error.args))
print(str(error))
result = {"result": "error"}
finally:
cursor.close()
return result
def stop(self, **kwargs):
try:
discord_user_id = str(kwargs["user_id"])
if "bot_about" in kwargs:
bot_about = kwargs["bot_about"]
else:
bot_about = None
cursor = dislocker.db.cursor()
cursor.execute("SELECT * FROM club_member WHERE discord_userid = %s", (discord_user_id,))
user_record = cursor.fetchall()
if user_record:
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id= %s ORDER BY id DESC LIMIT 1", (user_record[0][0],))
pc_usage_history_record = cursor.fetchall()
if pc_usage_history_record:
if not pc_usage_history_record[0][5] == None:
result = {"result": "unused"}
else:
if not bot_about == None:
cursor.execute("UPDATE pc_usage_history SET end_use_time = clock_timestamp(), bot_about = %s WHERE id = %s", (bot_about, pc_usage_history_record[0][0]))
else:
cursor.execute("UPDATE pc_usage_history SET end_use_time = clock_timestamp() WHERE id = %s", (pc_usage_history_record[0][0],))
cursor.execute("UPDATE pc_list SET using_user_id = NULL, password_hash = NULL WHERE pc_number = %s", (pc_usage_history_record[0][2],))
dislocker.db.commit()
result = {"result": "ok", "pc_number": str(pc_usage_history_record[0][2]), "name": str(user_record[0][1])}
else:
result = {"result": "unused"}
else:
result = {"result": "user_data_not_found"}
except:
print("停止処理にエラーが発生しました。")
result = {"result": "error"}
finally:
cursor.close()
return result
def user_register(self, **kwargs):
try:
discord_user_id = str(kwargs["discord_user_id"])
discord_user_name = str(kwargs["discord_user_name"])
name = str(kwargs["name"])
cursor = dislocker.db.cursor()
cursor.execute("SELECT * FROM club_member WHERE discord_userid = %s", (discord_user_id,))
user_record = cursor.fetchall()
if not user_record:
cursor.execute("INSERT INTO club_member (name, discord_username, discord_userid) VALUES (%s, %s, %s)", (name, discord_user_name, discord_user_id))
dislocker.db.commit()
result = {"result": "ok"}
else:
result = {"result": "already_exists"}
except Exception as error:
print("ユーザー登録中にエラーが発生しました。\nエラー内容")
print(str(error.__class__.__name__))
print(str(error.args))
print(str(error))
result = {"result": "error"}
finally:
cursor.close()
return result
def format_datetime(self, value):
if isinstance(value, datetime):
return value.strftime('%Y-%m-%d %H:%M:%S')
return value
def pc_register(self, **kwargs):
try:
pc_number = int(kwargs["pc_number"])
cursor = dislocker.db.cursor()
cursor.execute("SELECT * FROM pc_list WHERE pc_number = %s", (pc_number,))
pc_list = cursor.fetchall()
if not pc_list:
cursor.execute("INSERT INTO pc_list (pc_number) VALUES (%s)", (pc_number,))
dislocker.db.commit()
result = {"result": "ok"}
else:
result = {"result": "already_exists"}
except Exception as error:
print("PCの登録中にエラーが発生しました。\nエラー内容")
print(str(error.__class__.__name__))
print(str(error.args))
print(str(error))
result = {"result": "error"}
finally:
cursor.close()
return result
def report_export(self, **kwargs):
try:
cursor = dislocker.db.cursor()
csv_file_path = dislocker.export_dir_path + "pc_usage_history.csv"
main_table = "pc_usage_history"
related_table = "club_member"
excel_file_path = dislocker.export_dir_path + "pc_usage_history.xlsx"
# メインテーブルの列情報を取得user_idを除く
cursor.execute(sql.SQL("SELECT * FROM {} LIMIT 0").format(sql.Identifier(main_table)))
main_columns = [desc[0] for desc in cursor.description if desc[0] != 'member_id']
# クエリを作成(列名を明確に指定)
query = sql.SQL("""
SELECT {main_columns}, {related_table}.name
FROM {main_table}
LEFT JOIN {related_table} ON {main_table}.member_id = {related_table}.id
ORDER BY id
""").format(
main_columns=sql.SQL(', ').join([sql.SQL("{}.{}").format(sql.Identifier(main_table), sql.Identifier(col)) for col in main_columns]),
main_table=sql.Identifier(main_table),
related_table=sql.Identifier(related_table)
)
cursor.execute(query)
# 列名を再構成nameを2番目に配置
column_names = [main_columns[0], 'name'] + main_columns[1:]
rows = cursor.fetchall()
# Excelワークブックを作成
wb = Workbook()
ws = wb.active
# 列名を書き込み
ws.append(column_names)
# データを書き込み
for row in rows:
# nameを2番目に移動
formatted_row = [self.format_datetime(row[0])] + [row[-1]] + [self.format_datetime(field) if field is not None else '' for field in row[1:-1]]
ws.append(formatted_row)
# 列幅を自動調整
for col in ws.columns:
max_length = 0
column = col[0].column_letter
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = (max_length + 2) * 1.2
ws.column_dimensions[column].width = adjusted_width
# Excelファイルを保存
wb.save(excel_file_path)
print(f"テーブル '{main_table}' の内容を '{excel_file_path}' に出力しました。")
result = {"result": "ok", "file_path": excel_file_path}
except (Exception, psycopg2.Error) as error:
print("使用履歴のエクスポート時にエラーが発生しました\nエラー内容\n", str(error))
result = {"result": "export_error"}
finally:
cursor.close()
return result
def force_stop(self, **kwargs):
try:
pc_number = kwargs["pc_number"]
if "bot_about" in kwargs:
bot_about = kwargs["bot_about"]
cursor = dislocker.db.cursor()
cursor.execute("SELECT * FROM pc_list WHERE pc_number = %s", (pc_number,))
pc_list_record = cursor.fetchall()
if not pc_list_record[0][1] == None:
cursor.execute("UPDATE pc_list SET using_user_id = NULL WHERE pc_number = %s", (pc_number,))
if not pc_list_record[0][2] == None:
cursor.execute("UPDATE pc_list SET password_hash = NULL WHERE pc_number = %s", (pc_number,))
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id = %s AND pc_number = %s ORDER BY id DESC LIMIT 1", (pc_list_record[0][1], pc_number))
pc_usage_history_record = cursor.fetchall()
cursor.execute("UPDATE pc_usage_history SET end_use_time = clock_timestamp(), bot_about = %s WHERE id = %s", (bot_about, pc_usage_history_record[0][0]))
dislocker.db.commit()
result = {"result": "ok"}
else:
result = {"result": "not_used"}
else:
bot_about = None
result = {"result": "bot_about_not_found"}
except:
result = {"result": "error"}
finally:
cursor.close()
return result
async def timeout_notify(self, **kwargs):
try:
pc_number = kwargs["pc_number"]
discord_display_name = kwargs["discord_display_name"]
await self.get_channel(dislocker.server_config["bot"]["log_channel_id"]).send(f':negative_squared_cross_mark: {discord_display_name} さんのPC {pc_number} の使用登録はタイムアウトにより解除されました。')
result = {"result": "ok"}
except Exception as error:
print("自動停止処理中にエラーが発生しました。\nエラー内容")
print(str(error.__class__.__name__))
print(str(error.args))
print(str(error))
result = {"result": "error"}
finally:
return result
async def on_ready(self): async def on_ready(self):
print("ログイン成功") print("DiscordのBotが起動しました。")
dislocker_activity = discord.Activity(
name=dislocker.server_config["bot"]["activity"]["name"],
type=discord.ActivityType.competing,
details=dislocker.server_config["bot"]["activity"]["details"],
state=dislocker.server_config["bot"]["activity"]["state"]
)
await bot.change_presence(activity=dislocker_activity)
async def on_interaction(self, interaction:discord.Interaction):
try:
if interaction.data["component_type"] == 2:
await self.on_button(interaction)
except KeyError:
pass
async def on_message(self, message): async def on_message(self, message):
if message.author.bot: if message.author.bot:
return pass
if isinstance(message.channel, discord.DMChannel): elif isinstance(message.channel, discord.DMChannel):
user_id = message.author.id
user_name = message.author.name
msg_split = message.content.split() msg_split = message.content.split()
if msg_split[0] == "/password": if msg_split[0] == "/password" or msg_split[0] == "/start":
#メッセージの要素が2つ以下の場合は拒否
if len(msg_split) <= 2: if len(msg_split) <= 2:
await message.channel.send("PC番号、もしくはデバイス番号が入力されていません。") await message.channel.send("# :warning: PC番号、もしくはデバイス番号が入力されていません。")
#メッセージの要素が3つ以上の場合
elif len(msg_split) == 3: elif len(msg_split) >= 3:
#番号が数字であることを確認
if msg_split[1].isdigit() and msg_split[2].isdigit(): if msg_split[1].isdigit() and msg_split[2].isdigit():
#PC番号が1以上10以下であることを確認
if int(msg_split[1]) <= 10 and int(msg_split[1]) >= 1: if int(msg_split[1]) <= 10 and int(msg_split[1]) >= 1:
cursor = self.db.cursor() if len(msg_split) == 3:
cursor.execute("SELECT * FROM club_member WHERE discord_userid = %s", (str(message.author.id),)) register = self.register(user_id=message.author.id, name=message.author.name, display_name=message.author.display_name, pc_number=msg_split[1], device_number=msg_split[2])
member_info = cursor.fetchall() elif len(msg_split) == 4:
cursor.execute("INSERT INTO pc_usage_history ( member_id, pc_number, device_number, start_use_time ) VALUES ( %s, %s, %s, current_timestamp)", (member_info[0][0], int(msg_split[1]), int(msg_split[2]))) register = self.register(user_id=message.author.id, name=message.author.name, display_name=message.author.display_name, pc_number=msg_split[1], device_number=msg_split[2], detail=msg_split[3])
self.db.commit()
await message.channel.send(f"使用が開始されました。\nPC番号 | {msg_split[1]}\nデバイス番号 | {msg_split[2]}\n")
else:
await message.channel.send("パソコンの台数が\n# だめです")
else:
await message.channel.send("構文が不正です。")
elif len(msg_split) >= 4: if register["result"] == "ok":
if msg_split[1].isdigit() and msg_split[2].isdigit(): if len(msg_split) == 3:
if int(msg_split[1]) <= 10 and int(msg_split[1]) >= 1: await message.channel.send(f":white_check_mark: 使用が開始されました。\n>>> # パスワード | {register["password"]}\n## PC番号 | {msg_split[1]}\n## デバイス番号 | {msg_split[2]}")
cursor = self.db.cursor() elif len(msg_split) == 4:
cursor.execute("SELECT * FROM club_member WHERE discord_userid = %s", (str(message.author.id),)) await message.channel.send(f":white_check_mark: 使用が開始されました。\n>>> # パスワード | {register["password"]}\n## PC番号 | {msg_split[1]}\n## デバイス番号 | {msg_split[2]}\n## 使用目的 | {msg_split[3]}")
member_info = cursor.fetchall() await self.get_channel(dislocker.server_config["bot"]["log_channel_id"]).send(f':white_check_mark: {register["name"]} さんがPC {msg_split[1]} の使用を開始しました。')
cursor.execute("INSERT INTO pc_usage_history ( member_id, pc_number, device_number, start_use_time, use_detail ) VALUES ( %s, %s, %s, current_timestamp, %s)", (member_info[0][0], int(msg_split[1]), int(msg_split[2]), str(msg_split[3]))) elif register["result"] == "user_data_not_found":
self.db.commit() await message.channel.send("# :dizzy_face: ユーザーとして登録されていないようです。\n最初にサーバーで登録を行ってください。")
await message.channel.send(f"使用が開始されました。\nPC番号 | {msg_split[1]}\nデバイス番号 | {msg_split[2]}\n") elif register["result"] == "pc_already_in_use_by_you":
await message.channel.send(f"# :exploding_head: あなたはPCをもう使用されているようです。\n使用状態を解除するには /stop で使用終了をお知らせください。\n>>> # PC番号 | {register["pc_number"]}\n# デバイス番号 | {register["device_number"]}\n# 使用開始時刻 | {register["start_time"]}\n# 使用目的 | {register["detail"]}")
elif register["result"] == "pc_already_in_use_by_other":
await message.channel.send(f"# :man_gesturing_no: そのPCは他のメンバーによって使用されています。\n別のPC番号を指定して、再度お試しください。")
else: else:
await message.channel.send("パソコンの台数が\n# だめです") await message.channel.send("# :dizzy_face: 番号がおかしいようです。")
else: else:
await message.channel.send("構文が不正です。") await message.channel.send("# :dizzy_face: 指定された番号は不正です。")
elif msg_split[0] == "/stop": elif msg_split[0] == "/stop":
cursor = self.db.cursor() stop = self.stop(user_id=message.author.id)
cursor.execute("SELECT * FROM club_member WHERE discord_userid = %s", (str(message.author.id),)) if stop["result"] == "unused":
member_info = cursor.fetchall() await message.channel.send("# :shaking_face: 使用されていないようです...")
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id=%s ORDER BY id DESC LIMIT 1", (member_info[0][0],)) elif stop["result"] == "ok":
history_info = cursor.fetchall() await message.channel.send(f":white_check_mark: PC番号 {stop["pc_number"]} の使用が終了されました。")
cursor.execute("UPDATE pc_usage_history SET end_use_time = current_timestamp WHERE id = %s", (history_info[0][0],)) await self.get_channel(dislocker.server_config["bot"]["log_channel_id"]).send(f':negative_squared_cross_mark: {stop["name"]} さんがPC {stop["pc_number"]} の使用を終了しました。')
self.db.commit()
await message.channel.send(f"使用が終了されました。") elif message.channel.id == dislocker.server_config["bot"]["config_channel_id"]:
msg_split = message.content.split()
if msg_split[0] == "/register":
print(len(msg_split))
if len(msg_split) == 1:
register = self.user_register(name=message.author.display_name, discord_user_name=message.author.name, discord_user_id=message.author.id)
print(register)
if register["result"] == "ok":
await message.channel.send(f"# :white_check_mark: ユーザー情報が登録されました。\n>>> ユーザー名:{message.author.display_name}")
elif register["result"] == "already_exists":
await message.channel.send("# :no_entry: 登録できませんでした。\nもう登録されている可能性があります。")
else:
await message.channel.send("# :no_entry: 登録できませんでした。\n内部エラーが発生しています。")
else: elif len(msg_split) <= 3:
await message.channel.send("# :japanese_goblin: 入力内容に不備があります。\n名前、Discordのユーザー名、DiscordのユーザーIDのいずれかが入力されていません。")
elif len(msg_split) == 4:
if msg_split[3].isdigit():
register = self.user_register(name=msg_split[1], discord_user_name=msg_split[2], discord_user_id=msg_split[3])
if register["result"] == "ok":
await message.channel.send(f"# :white_check_mark: 登録が完了しました。\n>>> # 名前 | {msg_split[1]}\n# Discordのユーザー名 | {msg_split[2]}\n# DiscordのユーザーID | {msg_split[3]}")
elif register["result"] == "already_exists":
await message.channel.send("# :skull_crossbones: 登録できませんでした。\nそのDiscordアカウントはすでに登録されています。")
else:
await message.channel.send("# :skull_crossbones: 登録できませんでした。\nDiscordのユーザーIDが不正です。")
else:
await message.channel.send("# :skull_crossbones: 登録できませんでした。\n内部エラーが発生しています。")
if message.content == "/hello": elif msg_split[0] == "/export":
await message.channel.send("こんにちは!!!!") export = self.report_export()
if export["result"] == "ok":
await message.channel.send("# :page_facing_up: 使用履歴のレポートです。", file=discord.File(export["file_path"]))
pass
elif export["result"] == "export_error":
await message.channel.send("# :volcano: エクスポートに失敗しました。")
elif msg_split[0] == "/fstop":
if len(msg_split) == 1:
await message.channel.send("# :warning: 登録を解除できませんでした。\n使用を停止したいPC番号を指定してください。\n-# /fstop PC番号")
elif len(msg_split) == 2:
if msg_split[1].isdigit():
fstop = self.force_stop(pc_number=msg_split[1], bot_about="管理者による強制停止。")
if fstop["result"] == "ok":
await message.channel.send(f"# :white_check_mark: PC番号 {msg_split[1]} の使用登録を解除しました。")
elif fstop["result"] == "not_used":
await message.channel.send("# :exploding_head: 登録を解除できませんでした。\nPCは使用されていないようです...")
else:
await message.channel.send("# :x: 登録を解除できませんでした。\n内部エラーが発生しています。")
else:
await message.channel.send("# :warning: 登録を解除できませんでした。\nPC番号を認識できません。\n-# 半角数字で入力してください。")
else:
await message.channel.send("# warning: 登録を解除できませんでした。\構文が間違っています。\n-# /fstop PC番号")
elif msg_split[0] == "/pcregister":
if len(msg_split) == 1:
await message.channel.send("# :warning: PCを登録できませんでした。\n登録したいPC番号を指定してください。\n-# 半角数字で入力してください。")
elif len(msg_split) == 2:
if msg_split[1].isdigit():
pc_register = self.pc_register(pc_number=msg_split[1])
if pc_register["result"] == "ok":
await message.channel.send(f"# :white_check_mark: PCを登録しました。\n>>> # PC番号 | {msg_split[1]}")
elif pc_register["result"] == "already_exists":
await message.channel.send(f":x: PCを登録できませんでした。\nその番号のPCは既に存在します。")
else:
await message.channel.send("# :x: PCを登録できませんでした。\n内部エラーが発生しています。")
else:
await message.channel.send("# :warning: PCを登録できませんでした。\nPC番号を認識できません。\n-# 半角数字で入力してください。")
else:
await message.channel.send("# :warning: PCを登録できませんでした。\n構文が間違っています。\n-# /pcregister PC番号")
elif msg_split[0] == "/registerbutton":
pc_button_view = View(timeout=None)
for i in range(1, 11):
pc_register_button = discord.ui.Button(style=discord.ButtonStyle.primary, label=f"{i}", custom_id=f"pcregister_{i}")
pc_button_view.add_item(pc_register_button)
await self.get_channel(dislocker.server_config["bot"]["config_public_channel_id"]).send(f'# :index_pointing_at_the_viewer: 使いたいPCの番号を選んでください', view=pc_button_view)
elif msg_split[0] == "/stopbutton":
stop_button_view = View(timeout=None)
stop_button = discord.ui.Button(style=discord.ButtonStyle.danger, label="PCの使用を停止", custom_id="stop")
stop_button_view.add_item(stop_button)
await self.get_channel(dislocker.server_config["bot"]["config_public_channel_id"]).send(f'# :index_pointing_at_the_viewer: 使用を停止しますか?', view=stop_button_view)
elif msg_split[0] == "/userbutton":
user_register_button_view = View(timeout=None)
user_register_button = discord.ui.Button(style=discord.ButtonStyle.green, label="ユーザー登録", custom_id="user_register")
user_register_button_view.add_item(user_register_button)
await self.get_channel(dislocker.server_config["bot"]["config_public_channel_id"]).send(f'# :index_pointing_at_the_viewer: ユーザー登録はお済ですか?', view=user_register_button_view)
elif message.channel.id == dislocker.server_config["bot"]["config_public_channel_id"]:
msg_split = message.content.split()
if msg_split[0] == "/register":
print(len(msg_split))
if len(msg_split) == 1:
register = self.user_register(name=message.author.display_name, discord_user_name=message.author.name, discord_user_id=message.author.id)
print(register)
if register["result"] == "ok":
await message.channel.send(f"# :white_check_mark: ユーザー情報が登録されました。\nユーザー名:{message.author.display_name}")
elif register["result"] == "already_exists":
await message.channel.send("# :skull_crossbones: 登録できませんでした。\nそのDiscordアカウントはすでに登録されています。")
else:
await message.channel.send("# :skull_crossbones: 登録できませんでした。\n内部エラーが発生しています。")
else:
await message.channel.send("# :skull_crossbones: 登録できませんでした。\n\n-# もしかして...\n-# 手動でメンバーを登録したいですか?\n-# もしそうなら、このチャンネルにはその権限がありません。\n-# そのチャンネルに移動してから、もう一度試してみてください!")
async def on_button(self, interaction: Interaction):
custom_id = interaction.data["custom_id"]
custom_id_split = custom_id.split("_")
print(custom_id, custom_id_split[0])
if custom_id_split[0] == "pcregister":
device_register_view = View(timeout=15)
pc_number = custom_id_split[1]
print(custom_id_split)
for i in range(1, 11):
device_register_button = discord.ui.Button(style=discord.ButtonStyle.primary, label=f"{i}", custom_id=f"deviceregister_{str(pc_number)}_{i}")
device_register_view.add_item(device_register_button)
device_own_register_button = discord.ui.Button(style=discord.ButtonStyle.primary, label="自前", custom_id=f"deviceregister_{str(pc_number)}_own")
device_register_view.add_item(device_own_register_button)
await interaction.response.send_message(f"# :keyboard: デバイス番号を選んでください!\n>>> # PC番号 | {str(pc_number)}", view=device_register_view, ephemeral=True)
elif custom_id_split[0] == "deviceregister":
pc_number = custom_id_split[1]
device_number = custom_id_split[2]
reason_register_view = View(timeout=15)
for i in dislocker.preset_games:
reason_quick_button = reason_button = discord.ui.Button(style=discord.ButtonStyle.primary, label=f"{str(i)}", custom_id=f"reasonregister_{str(pc_number)}_{str(device_number)}_quick_{str(i)}")
reason_register_view.add_item(reason_quick_button)
reason_button = discord.ui.Button(style=discord.ButtonStyle.primary, label="使用目的を入力する", custom_id=f"reasonregister_{str(pc_number)}_{str(device_number)}")
reason_register_view.add_item(reason_button)
await interaction.response.send_message(f"# :regional_indicator_q: 使用目的を書いてください!\n>>> # PC番号 | {str(pc_number)}\n# デバイス番号 | {str(device_number)}", view=reason_register_view, ephemeral=True)
elif custom_id_split[0] == "reasonregister":
pc_number = custom_id_split[1]
device_number = custom_id_split[2]
if len(custom_id_split) >= 4:
if custom_id_split[3] == "quick":
reason = custom_id_split[4]
if device_number == "own":
device_number = 0
else:
device_number = custom_id_split[2]
register = bot.register(user_id=interaction.user.id, name=interaction.user.name, display_name=interaction.user.display_name, pc_number=pc_number, device_number=device_number, detail=reason)
print(register["result"])
if register["result"] == "ok":
await interaction.response.send_message(f":white_check_mark: 使用が開始されました。\n>>> # パスワード | {register["password"]}\n## PC番号 | {pc_number}\n## デバイス番号 | {device_number}\n## 使用目的 | {reason}", ephemeral=True)
await bot.get_channel(dislocker.server_config["bot"]["log_channel_id"]).send(f':white_check_mark: {register["name"]} さんがPC {pc_number} の使用を開始しました。')
elif register["result"] == "pc_already_in_use_by_you":
await interaction.response.send_message(f"# :exploding_head: あなたはPCをもう使用されているようです。\n使用状態を解除するには 終了ボタン で使用終了をお知らせください。\n>>> # PC番号 | {register["pc_number"]}\n# デバイス番号 | {register["device_number"]}\n# 使用開始時刻 | {register["start_time"]}\n# 使用目的 | {register["detail"]}", ephemeral=True)
elif register["result"] == "pc_already_in_use_by_other":
await interaction.response.send_message(f"# :man_gesturing_no: そのPCは他のメンバーによって使用されています。\n別のPC番号を指定して、再度お試しください。", ephemeral=True)
elif register["result"] == "user_data_not_found":
await interaction.response.send_message("# :dizzy_face: ユーザーとして登録されていないようです。\n最初にサーバーで登録を行ってください。", ephemeral=True)
else:
await interaction.response.send_message("# :skull_crossbones: 登録できませんでした。\n内部エラーが発生しています。", ephemeral=True)
else:
reason_input_form = Reason(title="Dislocker | 登録", pc_number=str(pc_number), device_number=str(device_number))
await interaction.response.send_modal(reason_input_form)
else: else:
await message.channel.send("どうしましたか?") reason_input_form = Reason(title="Dislocker | 登録", pc_number=str(pc_number), device_number=str(device_number))
await interaction.response.send_modal(reason_input_form)
#cert = Cert() elif custom_id_split[0] == "stop":
print("STOP running")
pc_stop = self.stop(user_id=interaction.user.id)
print(pc_stop)
stop_view = View(timeout=15)
if pc_stop["result"] == "unused":
await interaction.response.send_message("# :shaking_face: 使用されていないようです...", ephemeral=True)
elif pc_stop["result"] == "user_data_not_found":
await interaction.response.send_message("# :dizzy_face: ユーザーとして登録されていないようです。\n最初にサーバーで登録を行ってください。", ephemeral=True)
elif pc_stop["result"] == "ok":
await interaction.response.send_message(f":white_check_mark: PC番号 {pc_stop["pc_number"]} の使用が終了されました。", ephemeral=True)
await self.get_channel(dislocker.server_config["bot"]["log_channel_id"]).send(f':negative_squared_cross_mark: {pc_stop["name"]} さんがPC {pc_stop["pc_number"]} の使用を終了しました。')
else:
await interaction.response.send_message("# :skull_crossbones: 停止できませんでした。\n内部エラーが発生しています。", ephemeral=True)
config_dir_path = "./config/" elif custom_id_split[0] == "user" and custom_id_split[1] == "register":
db_config_path = config_dir_path + "db.json" print("User Register RUnning")
if not os.path.isfile(db_config_path): user_register = self.user_register(name=interaction.user.display_name, discord_user_name=interaction.user.name, discord_user_id=interaction.user.id)
if not os.path.isdir(config_dir_path): if user_register["result"] == "ok":
os.mkdir(config_dir_path) await interaction.response.send_message(f"# :white_check_mark: ユーザー情報が登録されました。\n>>> ユーザー名:{interaction.user.display_name}", ephemeral=True)
elif user_register["result"] == "already_exists":
await interaction.response.send_message("# :no_entry: 登録できませんでした。\nもう登録されている可能性があります。", ephemeral=True)
else:
await interaction.response.send_message("# :no_entry: 登録できませんでした。\n内部エラーが発生しています。", ephemeral=True)
db_config = { class Monitor():
"host": "localhost", def __init__(self, **kwargs) -> None:
"db": "dislocker", self.search_frequency = kwargs["search_frequency"]
"username": "user", self.allowable_time = kwargs["allowable_time"]
"password": "example_pass", self.fstop_time = kwargs["fstop_time"]
"port": "5432" self.init_wait_time = 10
}
with open(db_config_path, "w") as w:
json.dump(db_config, w, indent=4)
elif os.path.isfile(db_config_path):
with open(db_config_path, "r") as r:
db_config = json.load(r)
bot_config_path = config_dir_path + "bot.json" def start(self, **kwargs):
if not os.path.isfile(bot_config_path): search_thread = threading.Thread(target=self.search)
if not os.path.isdir(config_dir_path): search_thread.start()
os.mkdir(config_dir_path)
bot_config = {
"token": "TYPE HERE BOTS TOKEN KEY"
}
with open(bot_config_path, "w") as w:
json.dump(bot_config, w, indent=4)
elif os.path.isfile(bot_config_path): def search(self):
with open(bot_config_path, "r") as r: try:
bot_config = json.load(r) time.sleep(self.init_wait_time)
while True:
cursor = dislocker.db.cursor()
cursor.execute("SELECT * FROM pc_list WHERE password_hash IS NOT NULL")
pc_list = cursor.fetchall()
current_datetime = datetime.now()
fstop_time = self.fstop_time
if current_datetime.time().strftime("%H:%M:%S") == fstop_time:
for i in dislocker.pc_list:
stop = bot.force_stop(pc_number=i, bot_about="使用停止忘れによるBotによる強制停止。")
result = {"result": "FSTOP"}
else:
if pc_list:
if len(pc_list) == 1:
user_id = pc_list[0][1]
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id= %s AND end_use_time IS NULL ORDER BY id DESC LIMIT 1", (user_id,))
pc_usage = cursor.fetchall()
print(pc_usage)
start_time = pc_usage[0][4]
print(start_time)
print(type(start_time))
time_difference = current_datetime - start_time
print(current_datetime, start_time)
print(time_difference.seconds, timedelta(seconds=self.allowable_time).seconds)
if time_difference.seconds >= timedelta(seconds=self.allowable_time).seconds:
cursor.execute("SELECT * FROM club_member WHERE id = %s", (user_id,))
user_info = cursor.fetchall()
stop = bot.stop(user_id=user_info[0][3], bot_about="パスワードのタイムアウトでBotによる強制停止。")
intents = discord.Intents.default() bot.timeout_notify(pc_number=pc_list[0][0], discord_display_name=user_info[0][1])
intents.message_content = True result = {"result": "STOP", "details": str(pc_usage)}
else:
result = {"result": "BUT SAFE", "details": str(pc_usage)}
bot = Bot(intents=intents)
bot.db_connect(db_config["host"], db_config["db"], db_config["username"], db_config["password"]) elif len(pc_list) >= 2:
bot.run(bot_config["token"]) for i in pc_list:
print(i)
user_id = i[1]
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id= %s AND end_use_time IS NULL ORDER BY id DESC LIMIT 1", (user_id,))
pc_usage = cursor.fetchall()
print(pc_usage)
start_time = pc_usage[0][4]
print(start_time)
print(type(start_time))
time_difference = current_datetime - start_time
print(time_difference.seconds, timedelta(seconds=self.allowable_time).seconds)
if time_difference.seconds >= timedelta(seconds=self.allowable_time).seconds:
cursor.execute("SELECT * FROM club_member WHERE id = %s", (user_id,))
user_info = cursor.fetchall()
stop = bot.stop(user_id=user_info[0][3], bot_about="タイムアウトでBotによる強制停止。")
bot.timeout_notify(pc_number=i[0], discord_display_name=user_info[0][1])
result = {"result": "STOP", "details": str(pc_usage)}
else:
result = {"result": "BUT SAFE", "details": str(pc_usage)}
else:
result = {"result": "NONE"}
else:
result = {"result": "NONE"}
if result["result"] == "NONE":
pass
else:
print(current_datetime)
print(result["result"])
time.sleep(self.search_frequency)
except Exception as error:
print("自動停止処理中にエラーが発生しました。\nエラー内容")
print(str(error.__class__.__name__))
print(str(error.args))
print(str(error))
result = {"result": "error"}
dislocker.db.rollback()
finally:
cursor.close()
print(result["result"])
return result
class Reason(Modal):
def __init__(self, title: str, pc_number: str, device_number: str, timeout=15) -> None:
super().__init__(title=title, timeout=timeout)
print(pc_number)
print(device_number)
self.reason_input_form = TextInput(label="使用目的を入力してください", style=TextStyle.short, custom_id=f"register_{pc_number}_{device_number}")
self.add_item(self.reason_input_form)
async def on_submit(self, interaction: Interaction) -> None:
custom_id = interaction.data["components"][0]["components"][0]["custom_id"]
print(custom_id)
custom_id_split = custom_id.split("_")
pc_number = custom_id_split[1]
device_number = custom_id_split[2]
if device_number == "own":
device_number = 0
else:
device_number = custom_id_split[2]
register = bot.register(user_id=interaction.user.id, name=interaction.user.name, display_name=interaction.user.display_name, pc_number=pc_number, device_number=device_number, detail=self.reason_input_form.value)
print(register["result"])
if register["result"] == "ok":
await interaction.response.send_message(f":white_check_mark: 使用が開始されました。\n>>> # パスワード | {register["password"]}\n## PC番号 | {pc_number}\n## デバイス番号 | {device_number}\n## 使用目的 | {self.reason_input_form.value}", ephemeral=True)
await bot.get_channel(dislocker.server_config["bot"]["log_channel_id"]).send(f':white_check_mark: {register["name"]} さんがPC {pc_number} の使用を開始しました。')
elif register["result"] == "pc_already_in_use_by_you":
await interaction.response.send_message(f"# :exploding_head: あなたはPCをもう使用されているようです。\n使用状態を解除するには 終了ボタン で使用終了をお知らせください。\n>>> # PC番号 | {register["pc_number"]}\n# デバイス番号 | {register["device_number"]}\n# 使用開始時刻 | {register["start_time"]}\n# 使用目的 | {register["detail"]}", ephemeral=True)
elif register["result"] == "pc_already_in_use_by_other":
await interaction.response.send_message(f"# :man_gesturing_no: そのPCは他のメンバーによって使用されています。\n別のPC番号を指定して、再度お試しください。", ephemeral=True)
elif register["result"] == "user_data_not_found":
await interaction.response.send_message("# :dizzy_face: ユーザーとして登録されていないようです。\n最初にサーバーで登録を行ってください。", ephemeral=True)
else:
await interaction.response.send_message("# :skull_crossbones: 登録できませんでした。\n内部エラーが発生しています。", ephemeral=True)
dislocker = DL()
if dislocker.init_result == "ok":
print("Botを起動します...")
intents = discord.Intents.default()
intents.message_content = True
bot = Bot(intents=intents)
monitor = Monitor(search_frequency=dislocker.server_config["bot"]["monitor"]["search_frequency"], allowable_time=dislocker.server_config["bot"]["monitor"]["allowable_time"], fstop_time=dislocker.server_config["bot"]["monitor"]["fstop_time"])
monitor.start()
bot.run(dislocker.server_config['bot']['token'])
else:
pass

125
dislocker_auth.py Normal file
View file

@ -0,0 +1,125 @@
import psycopg2
import os
import json
from flask import Flask, request, jsonify, render_template
config_dir_path = "./config/"
server_config_path = config_dir_path + "server.json"
if not os.path.isfile(server_config_path):
if not os.path.isdir(config_dir_path):
os.mkdir(config_dir_path)
server_config = {
"db": {
"host": "localhost",
"port": "5432",
"db_name": "dislocker",
"username": "user",
"password": "password"
},
"bot": {
"token": "TYPE HERE BOTS TOKEN KEY",
"activity": {
"name": "Dislocker",
"details": "ロック中...",
"type": "playing",
"state": "ロック中..."
},
"log_channel_id" : "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)",
"config_channel_id": "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)",
"config_public_channel_id": "TYPE HERE CHANNEL ID (YOU MUST USE INT !!!!)"
}
}
with open(server_config_path, "w") as w:
json.dump(server_config, w, indent=4)
elif os.path.isfile(server_config_path):
with open(server_config_path, "r") as r:
server_config = json.load(r)
class Auth():
def __init__(self, host, db, port, user, password):
self.db = psycopg2.connect(f"host={host} dbname={db} port={port} user={user} password={password}")
def check(self, pc_number, password):
try:
cursor = self.db.cursor()
cursor.execute("SELECT * FROM pc_list WHERE pc_number = %s AND password_hash = %s", (pc_number, password))
pc_info = cursor.fetchall()
if not pc_info:
return 1
else:
return 0
finally:
cursor.close()
def delete(self, pc_number):
try:
cursor = self.db.cursor()
cursor.execute("UPDATE pc_list SET password_hash = NULL WHERE pc_number = %s", (pc_number,))
self.db.commit()
finally:
cursor.close()
def stop(self, **kwargs):
try:
pc_number = int(kwargs["pc_number"])
cursor = self.db.cursor()
cursor.execute("SELECT * FROM pc_list WHERE pc_number = %s", (pc_number,))
pc_list_record = cursor.fetchall()
if not pc_list_record[0][1] == None:
cursor.execute("UPDATE pc_list SET using_user_id = NULL WHERE pc_number = %s", (pc_number,))
if not pc_list_record[0][2] == None:
cursor.execute("UPDATE pc_list SET password_hash = NULL WHERE pc_number = %s", (pc_number,))
cursor.execute("SELECT * FROM pc_usage_history WHERE member_id = %s AND pc_number = %s ORDER BY id DESC LIMIT 1", (pc_list_record[0][1], pc_number))
pc_usage_history_record = cursor.fetchall()
cursor.execute("UPDATE pc_usage_history SET end_use_time = current_timestamp WHERE id = %s", (pc_usage_history_record[0][0],))
self.db.commit()
result = {"result": "ok"}
else:
result = {"result": "not_used"}
except:
result = {"result": "error"}
finally:
cursor.close()
return result
app = Flask(__name__, static_folder="./resource/")
auth = Auth(server_config["db"]["host"], server_config["db"]["db_name"], server_config["db"]["port"], server_config["db"]["username"], server_config["db"]["password"])
@app.route('/verify', methods=['POST'])
def verify():
pc_number = int(request.json.get('pc_number'))
password = request.json.get('password')
print(str(pc_number) + "の認証処理を開始...")
if auth.check(pc_number, password) == 0:
auth.delete(pc_number)
print(str(pc_number) + "の認証処理は成功しました.")
return jsonify({'message': 'ok'}), 200
else:
print(str(pc_number) + "の認証処理は失敗しました.")
return jsonify({'message': 'damedesu'}), 401
@app.route('/stop', methods=['POST'])
def stop():
pc_number = int(request.json.get('pc_number'))
print(str(pc_number) + "の使用停止処理を開始...")
pc_stop = auth.stop(pc_number=pc_number)
if pc_stop["result"] == "ok":
print(str(pc_number) + "の使用停止処理は成功しました.")
return jsonify({'message': 'ok'}), 200
else:
print(str(pc_number) + "の使用停止処理は失敗しました.")
return jsonify({'message': 'error'}), 500
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=False)

486
dislocker_client.py Normal file
View file

@ -0,0 +1,486 @@
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 sys
import shutil
import time
app_name = "Dislocker"
dislocker_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
os.chdir(dislocker_dir)
resource_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resource")
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,
"eraser": 1
}
elif os.path.isfile(client_config_path):
with open(client_config_path, "r") as r:
client_config = json.load(r)
def init(**kwargs):
sp_startupinfo = subprocess.STARTUPINFO()
sp_startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
sp_startupinfo.wShowWindow = subprocess.SW_HIDE
task_exist = subprocess.run('tasklist /fi "IMAGENAME eq dislocker_client.exe"', startupinfo=sp_startupinfo, stdout=subprocess.PIPE, text=True)
if 'dislocker_client.exe' in task_exist.stdout:
task_count = task_exist.stdout.count("dislocker_client.exe")
if task_count == 1:
pass
else:
return 1
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
if "pc_number" in kwargs:
client_config["pc_number"] = int(kwargs["pc_number"])
else:
client_config["pc_number"] = 1
with open(client_config_path, "w") as w:
json.dump(client_config, w, indent=4)
return 2
else:
return 0
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title(f"{app_name} | ロック中")
self.iconbitmap(default=resource_path + '\\icon\\dislocker.ico')
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 delete_appdata(self, **kwargs):
process_name = kwargs["process_name"]
dir_path = kwargs["dir_path"]
if not os.path.exists(dir_path):
print(f"エラー: 指定されたディレクトリ {dir_path} が存在しません。")
return 1
i = 0
i_max = 10
result = 1
while i != i_max:
i += 1
try:
# プロセスの終了
subprocess.run(['taskkill', '/f', '/t', '/im', process_name])
print(f"{process_name} を終了しました。")
time.sleep(0.1)
# ディレクトリの削除
shutil.rmtree(dir_path)
if os.path.isdir(dir_path):
pass
else:
print(f"{dir_path} を削除しました。")
result = 0
i = i_max
except subprocess.CalledProcessError as e:
print(f"プロセス終了エラー: {e}")
except PermissionError as e:
print(f"権限エラー: {e}")
except Exception as e:
print("エラーが発生しました。\nエラー内容:")
print(f"エラータイプ: {e.__class__.__name__}")
print(f"エラー引数: {e.args}")
print(f"エラーメッセージ: {str(e)}")
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):
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.iconbitmap(default=resource_path + '\\icon\\dislocker.ico')
self.window_width = 600
self.window_height = 320
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.pc_number_font = customtkinter.CTkFont(family="meiryo", size=64, 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=f'ちょっと待って!! PC番号 | {client_config["pc_number"]}', 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.signout_button = customtkinter.CTkButton(self.button_frame, text='サインアウト', command=self.signout, font=self.button_font)
self.signout_button.grid(row=0, column=1, padx=10, sticky="e")
self.help_button = customtkinter.CTkButton(self.button_frame, text='ヘルプ', command=self.help_dummy, font=self.button_font)
self.help_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 button_disable(self):
self.help_button.configure(state="disabled", fg_color="gray")
self.signin_button.configure(state="disabled", fg_color="gray")
self.signout_button.configure(state="disabled", fg_color="gray")
def button_enable(self):
self.help_button.configure(state="normal", fg_color="#3c8dd0")
self.signin_button.configure(state="normal", fg_color="#3c8dd0")
self.signout_button.configure(state="normal", fg_color="#3c8dd0")
def auth(self):
self.button_disable()
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("マスターパスワードで認証できませんでした。")
self.withdraw()
msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 誤ったパスワード", message=f"パスワードが間違っています!")
self.msg_subtitle_1.configure(text='パスワードが間違っています! ')
self.button_enable()
self.deiconify()
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.button_enable()
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.button_enable()
self.deiconify()
def signout(self):
app.unlock_taskmgr()
self.destroy()
signout_command = subprocess.run(['shutdown', '/l', '/f'])
print(signout_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(default=resource_path + '\\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 Stop():
def __init__(self) -> None:
pass
def run(self):
stop_thread = threading.Thread(target=self.stop)
stop_thread.run()
def delete_appdata(self, **kwargs):
process_name = kwargs["process_name"]
dir_path = kwargs["dir_path"]
if not os.path.exists(dir_path):
print(f"エラー: 指定されたディレクトリ {dir_path} が存在しません。")
return 1
i = 0
i_max = 10
result = 1
while i != i_max:
i += 1
try:
# プロセスの終了
subprocess.run(['taskkill', '/f', '/t', '/im', process_name])
print(f"{process_name} を終了しました。")
time.sleep(0.1)
# ディレクトリの削除
shutil.rmtree(dir_path)
if os.path.isdir(dir_path):
pass
else:
print(f"{dir_path} を削除しました。")
result = 0
i = i_max
except subprocess.CalledProcessError as e:
print(f"プロセス終了エラー: {e}")
except PermissionError as e:
print(f"権限エラー: {e}")
except Exception as e:
print("エラーが発生しました。\nエラー内容:")
print(f"エラータイプ: {e.__class__.__name__}")
print(f"エラー引数: {e.args}")
print(f"エラーメッセージ: {str(e)}")
return result
def shutdown(self):
shutdown_command = subprocess.run(['shutdown', '/s', '/t', '1'])
def stop(self):
print("停止処理を実行。")
if client_config["eraser"] == 1:
appdata_local = os.path.expandvars("%LOCALAPPDATA%")
appdata_roaming = os.path.expandvars("%APPDATA%")
epic_del = app.delete_appdata(process_name="EpicGamesLauncher.exe", dir_path=f"{appdata_local}\\EpicGamesLauncher\\Saved")
chrome_del = app.delete_appdata(process_name="chrome.exe", dir_path=f"{appdata_local}\\Google\\Chrome\\User Data")
discord_del = app.delete_appdata(process_name="discord.exe", dir_path=f"{appdata_roaming}\\discord")
steam_del = app.delete_appdata(process_name="steam.exe", dir_path=f"{appdata_local}\\Steam")
ea_del = app.delete_appdata(process_name="EADesktop.exe", dir_path=f"{appdata_local}\\Electronic Arts")
riot_del = app.delete_appdata(process_name="RiotClientServices.exe", dir_path=f"{appdata_local}\\Riot Games\\Riot Client")
else:
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("内部エラーにより停止処理に失敗しました。")
except:
print("ネットワークエラーにより停止処理に失敗しました。")
finally:
self.shutdown()
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":
init_result = init()
if init_result == 1:
print("多重起動エラー。")
elif init_result == 2:
pass
else:
stop = Stop()
stop.run()
elif args[1] == "setup":
init_result = init(pc_number=args[2])
if init_result == 1:
warning_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | 多重起動エラー", message=f"すでに {app_name} は実行されています。\n正常に起動しない場合は、既に起動しているプロセスを終了してから、もう一度起動してみてください。")
elif init_result == 2:
pass
else:
pass
else:
print("引数エラー。")
else:
init_result = init()
if init_result == 1:
warning_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | 多重起動エラー", message=f"すでに {app_name} は実行されています。\n正常に起動しない場合は、既に起動しているプロセスを終了してから、もう一度起動してみてください。")
elif init_result == 2:
pass
else:
app = App()
app.protocol("WM_DELETE_WINDOW", app.handler_close)
app.mainloop()

1
docs/pyinstaller.txt Normal file
View file

@ -0,0 +1 @@
pyinstaller .\dislocker_client.py --noconsole --icon .\resource\icon\dislocker.ico --add-data ".\resource:resource"

View file

@ -2,7 +2,10 @@
psql -U suti7 -d dislocker psql -U suti7 -d dislocker
CREATE TABLE club_member (id SERIAL NOT NULL, name VARCHAR(30) NOT NULL, discord_username VARCHAR(32) NOT NULL, discord_userid VARCHAR(18) NOT NULL, PRIMARY KEY (id)); CREATE TABLE club_member (id SERIAL NOT NULL, name VARCHAR(30) NOT NULL, discord_username VARCHAR(32) NOT NULL, discord_userid VARCHAR(18) NOT NULL, PRIMARY KEY (id));
CREATE TABLE pc_usage_history (id SERIAL NOT NULL, member_id INTEGER NOT NULL, pc_number INTEGER NOT NULL, device_number INTEGER NOT NULL, start_use_time TIMESTAMP NOT NULL, end_use_time TIMESTAMP, use_detail VARCHAR(30), PRIMARY KEY (id), FOREIGN KEY (memb CREATE TABLE pc_list (pc_number INTEGER NOT NULL, using_user_id INTEGER, password_hash VARCHAR(32), PRIMARY KEY (pc_number), FOREIGN KEY (using_user_id) REFERENCES club_member(id));
er_id) REFERENCES club_member(id)); CREATE TABLE pc_usage_history (id SERIAL NOT NULL, member_id INTEGER NOT NULL, pc_number INTEGER NOT NULL, device_number INTEGER NOT NULL, start_use_time TIMESTAMP NOT NULL, end_use_time TIMESTAMP, use_detail VARCHAR(30), PRIMARY KEY (id), FOREIGN KEY (member_id) REFERENCES club_member(id), FOREIGN KEY (pc_number) REFERENCES pc_list(pc_number));
# PC登録
INSERT INTO pc_list (pc_number) VALUES (PC番号をいれる);
INSERTのとき文字列はシングルクォーテーションで囲む INSERTのとき文字列はシングルクォーテーションで囲む

View file

@ -1,4 +1,8 @@
discord.py discord.py
flask flask
flask-login flask-login
psycopg2 psycopg2-binary
winotify
customtkinter
keyboard
requests

4
requirements_client.txt Normal file
View file

@ -0,0 +1,4 @@
customtkinter
winotify
keyboard
requests

BIN
resource/icon/dislocker.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
resource/icon/png/128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
resource/icon/png/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

BIN
resource/icon/png/256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
resource/icon/png/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
resource/icon/png/512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
resource/icon/png/64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
resource/success.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

10
resource/verify.html Normal file
View file

@ -0,0 +1,10 @@
<html>
<head>
<title>Verify</title>
</head>
<body>
<h1>だめです</h1>
</body>
</html>

21
script/setup.cmd Normal file
View file

@ -0,0 +1,21 @@
@echo off
set dir=%~dp0
cd %dir%
schtasks /create /tn "Dislocker_Client" /tr "%dir%dislocker_client.exe" /sc onlogon /ru "%USERNAME%" /rl highest /f
if %ERRORLEVEL% == 0 (
echo タスクスケジューラーの設定は完了しました。
)
if %ERRORLEVEL% == 1 (
echo タスクスケジューラーの設定でエラーが発生しました。
)
Cscript "%dir%shortcut.vbs" "%dir%" "%dir%" "dislocker_client.exe" "stop" "シャットダウンと停止処理"
if %ERRORLEVEL% == 0 (
Cscript "%dir%shortcut.vbs" "%USERPROFILE%/Desktop" "%dir%" "dislocker_client.exe" "stop" "シャットダウンと停止処理"
echo ショートカットの作成は完了しました。
)
if %ERRORLEVEL% == 1 (
echo ショートカットの作成でエラーが発生しました。
)
set /P pc_number=PC番号を入力
start %dir%dislocker_client.exe setup %pc_number%
pause

8
script/shortcut.vbs Normal file
View file

@ -0,0 +1,8 @@
Set WshShell = CreateObject("WScript.Shell")
Set args = Wscript.Arguments
Set Shortcut = WshShell.CreateShortcut(args(0) & "\シャットダウンと終了処理.lnk")
Shortcut.TargetPath = args(1) & args(2)
Shortcut.Arguments = args(3)
Shortcut.WorkingDirectory = args(1)
Shortcut.Save
WScript.Quit(0)

34
test.py
View file

@ -1,34 +0,0 @@
# インストールした discord.py を読み込む
import discord
import secrets
import string
# 接続に必要なオブジェクトを生成
client = discord.Client()
# 起動時に動作する処理
@client.event
async def on_ready():
# 起動したらターミナルにログイン通知が表示される
print('ログインしました')
# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
# メッセージ送信者がBotだった場合は無視する
if message.author.bot:
return
# 「/neko」と発言したら「にゃーん」が返る処理
if message.content == '/neko':
await message.channel.send('にゃーん')
if message.content == '/password':
await message.channel.send(get_random_password_string(4))
def get_random_password_string(length):
pass_chars = string.ascii_letters + string.digits
password = ''.join(secrets.choice(pass_chars) for x in range(length))
return password
# Botの起動とDiscordサーバーへの接続
client.run('MTI0NzA1Mzc1NzUxOTM2NDEyNw.Gh5gIt.kz1acBMxphff9mEZLLWrEdEoVD4RJwgBW5P14o')

View file

@ -1,6 +1,6 @@
from flask import Flask, request, render_template, redirect, url_for from flask import Flask, request, render_template, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user
import psycopg2 import sqlite3
app = Flask(__name__, static_folder="./resource/") app = Flask(__name__, static_folder="./resource/")
app.secret_key = 'your_secret_key' app.secret_key = 'your_secret_key'
@ -9,17 +9,17 @@ login_manager.init_app(app)
class Cert(): class Cert():
def __init__(self): def __init__(self):
self.db = psycopg2.connect("host=localhost dbname=dislocker user=suti7 password=testing") self.db = sqlite3.connect("host=localhost dbname=dislocker user=suti7 password=testing")
def register(self, username, userid): def register(self, username, userid):
cursor = self.db.cursor() cursor = self.db.cursor()
insert_sql = "INSERT INTO user_list (discord_username, discord_userid) VALUES (%s, %s);" insert_sql = "INSERT INTO user_list (discord_username, discord_userid) VALUES (?, ?)"
cursor.execute(insert_sql, (username, userid)) cursor.execute(insert_sql, (username, userid))
self.db.commit() self.db.commit()
def login(self, username, password): def login(self, username, password):
cursor = self.db.cursor() cursor = self.db.cursor()
get_sql = "SELECT * FROM web_auth WHERE username = %s AND password = %s " get_sql = "SELECT * FROM web_auth WHERE username = ? AND password = ? "
cursor.execute(get_sql, (username, password)) cursor.execute(get_sql, (username, password))
result = cursor.fetchall() result = cursor.fetchall()
print(result) print(result)