xenon4646 commited on
Commit
d56bdea
·
1 Parent(s): 3093aad

Switch to Gradio SDK

Browse files
Files changed (4) hide show
  1. Dockerfile +0 -10
  2. README.md +9 -131
  3. app.py +119 -91
  4. requirements.txt +2 -1
Dockerfile DELETED
@@ -1,10 +0,0 @@
1
- FROM python:3.11-slim
2
-
3
- WORKDIR /app
4
-
5
- COPY requirements.txt .
6
- RUN pip install --no-cache-dir -r requirements.txt
7
-
8
- COPY app.py .
9
-
10
- CMD ["python", "app.py"]
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -3,7 +3,7 @@ title: あけおめランキングボット
3
  emoji: 🏆
4
  colorFrom: blue
5
  colorTo: purple
6
- sdk: docker
7
  pinned: false
8
  ---
9
 
@@ -19,142 +19,20 @@ pinned: false
19
  - 6人検知した時点で早期発表(10分待たずに)
20
  - 結果をグループチャットに送信
21
 
22
- ---
23
-
24
- ## Hugging Face Spacesへのデプロイ方法(詳しく解説)
25
-
26
- ### ステップ1: Hugging Faceアカウントを作成
27
-
28
- 1. [Hugging Face](https://huggingface.co/) にアクセス
29
- 2. 右上の「Sign Up」からアカウントを作成
30
-
31
- ### ステップ2: 新しいSpaceを作成
32
-
33
- 1. ログイン後、右上のプロフィールアイコンをクリック
34
- 2. 「New Space」をクリック
35
- 3. 以下を入力:
36
- - **Owner**: あなたのユーザー名
37
- - **Space name**: 好きな名前(例: `akeome-ranking-bot`)
38
- - **License**: MIT など
39
- - **SDK**: **Docker** を選択 ← 重要!
40
- - **Hardware**: CPU basic (free) でOK
41
- 4. 「Create Space」をクリック
42
-
43
- ### ステップ3: ファイルをアップロード
44
-
45
- 作成したSpaceのページで「Files」タブを開き、以下の4つのファイルをアップロード:
46
-
47
- 1. **app.py** - ボットのメインプログラム
48
- 2. **requirements.txt** - 依存パッケージ
49
- 3. **Dockerfile** - Docker設定
50
- 4. **README.md** - このファイル(ヘッダー部分は削除してOK)
51
-
52
- **アップロード方法:**
53
- - 「Add file」→「Upload files」をクリック
54
- - ファイルをドラッグ&ドロップ
55
- - 「Commit changes to main」をクリック
56
-
57
- ### ステップ4: 環境変数(Secrets)を設定 ⭐ 重要!
58
-
59
- これが最も重要なステップです。
60
-
61
- 1. Spaceのページ右上の **「Settings」** タブをクリック
62
- 2. 下にスクロールして **「Variables and secrets」** セクションを見つける
63
- 3. **「New secret」** をクリック
64
- 4. 以下の3つのSecretを1つずつ追加:
65
-
66
- | 名前 (Name) | 値 (Value) | 説明 |
67
- |------------|------------|------|
68
- | `CHANNEL_ID` | `200605` | Channel.ioのチャンネルID(例のbotと同じ場合) |
69
- | `GROUP_CHAT_ID` | `536194` | グループチャットID(監視対象のチャット) |
70
- | `ACCOUNT_TOKEN` | `xoxo-...` | 認証トークン(Channel.ioのトークン) |
71
-
72
- **各Secretの追加手順:**
73
- 1. 「Name」欄に変数名(例: `CHANNEL_ID`)を入力
74
- 2. 「Value」欄に値を入力
75
- 3. 「Add secret」をクリック
76
- 4. 3つとも追加するまで繰り返し
77
-
78
- ### ステップ5: ボットを起動
79
-
80
- 1. Settings画面から「App」タブ(または「Logs」タブ)に戻る
81
- 2. Dockerビルドが自動的に開始される
82
- 3. 「Building」というステータスが表示される(1-2分程度)
83
- 4. 「Running」に変わったら起動成功!
84
-
85
- ### ステップ6: 動作確認
86
-
87
- - 「Logs」タブをクリックしてログを確認
88
- - 「設定確認OK。毎日0:00からの監視を開始します。」と表示されていればOK
89
-
90
- ---
91
 
92
- ## トラブルシューティング
93
 
94
- ### 「設定エラーのため終了します」と表示される
95
-
96
- 環境変数が正しく設定されていません。以下を確認してください:
97
-
98
- 1. Settings Variables and secrets に移動
99
- 2. 3つのSecretが正しく追加されているか確認
100
- 3. 変数名が正確に `CHANNEL_ID`, `GROUP_CHAT_ID`, `ACCOUNT_TOKEN` になっているか確認(大文字小文字も正確に)
101
-
102
- ### ビルドが失敗する
103
-
104
- - Dockerfileが正しくアップロードされているか確認
105
- - requirements.txtが正しくアップロードされているか確認
106
-
107
- ### メッセージ取得エラーが出る
108
-
109
- - `ACCOUNT_TOKEN` が正しいか確認
110
- - `CHANNEL_ID` と `GROUP_CHAT_ID` が正しいか確認
111
-
112
- ---
113
 
114
  ## トークンの取得方法
115
 
116
- Channel.ioのトークンを取得するには:
117
-
118
  1. Channel.ioのデスクトップ版(desk.channel.io)にログイン
119
  2. 開発者ツール(F12)を開く
120
  3. Networkタブを開く
121
  4. 適当な操作をしてリクエストを確認
122
- 5. リクエストヘッダーの `x-account` の値をコピー
123
- - これが `ACCOUNT_TOKEN` になります
124
-
125
- ---
126
-
127
- ## 出力例
128
-
129
- ```
130
- 🏆 あけおめランキング 🏆
131
-
132
- 🥇 ユーザーA (00:00:03)
133
- 🥈 ユーザーB (00:00:15)
134
- 🥉 ユーザーC (00:00:42)
135
- 4位 ユーザーD (00:01:23)
136
- 5位 ユーザーE (00:02:56)
137
- 6位 ユーザーF (00:05:12)
138
-
139
- 参加人数: 6人
140
- ```
141
-
142
- ---
143
-
144
- ## ローカルでの実行
145
-
146
- ```bash
147
- # 依存パッケージをインストール
148
- pip install -r requirements.txt
149
-
150
- # 環境変数を設定して実行(Windows)
151
- set CHANNEL_ID=your_channel_id
152
- set GROUP_CHAT_ID=your_group_chat_id
153
- set ACCOUNT_TOKEN=your_token
154
- python app.py
155
-
156
- # 環境変数を設定し���実行(Mac/Linux)
157
- export CHANNEL_ID="your_channel_id"
158
- export GROUP_CHAT_ID="your_group_chat_id"
159
- export ACCOUNT_TOKEN="your_token"
160
- python app.py
 
3
  emoji: 🏆
4
  colorFrom: blue
5
  colorTo: purple
6
+ sdk: gradio
7
  pinned: false
8
  ---
9
 
 
19
  - 6人検知した時点で早期発表(10分待たずに)
20
  - 結果をグループチャットに送信
21
 
22
+ ## 環境変数の設定
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ Hugging Face Spacesの **Settings** → **Variables and secrets** で以下を設定:
25
 
26
+ | 名前 | 説明 |
27
+ |------|------|
28
+ | `CHANNEL_ID` | Channel.ioのチャンネルID |
29
+ | `GROUP_CHAT_ID` | グループチャットID |
30
+ | `ACCOUNT_TOKEN` | 認証トークン |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  ## トークンの取得方法
33
 
 
 
34
  1. Channel.ioのデスクトップ版(desk.channel.io)にログイン
35
  2. 開発者ツール(F12)を開く
36
  3. Networkタブを開く
37
  4. 適当な操作をしてリクエストを確認
38
+ 5. リクエストヘッダーの `x-account` の値をコピー
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,8 +1,10 @@
1
  import os
2
  import time
 
3
  import requests
4
  from datetime import datetime, timedelta
5
  from zoneinfo import ZoneInfo
 
6
 
7
  # ===== 設定(環境変数から取得)=====
8
  CHANNEL_ID = os.getenv("CHANNEL_ID", "")
@@ -11,30 +13,33 @@ ACCOUNT_TOKEN = os.getenv("ACCOUNT_TOKEN", "")
11
 
12
  JST = ZoneInfo("Asia/Tokyo")
13
 
14
- BASE_URL = f"https://desk-api.channel.io/desk/channels/{CHANNEL_ID}"
15
- GET_MESSAGES_URL = f"{BASE_URL}/groups/{GROUP_CHAT_ID}/messages"
16
- POST_MESSAGE_URL = GET_MESSAGES_URL
17
-
18
- HEADERS_GET = {
19
- "accept": "application/json",
20
- "accept-language": "ja",
21
- "x-account": ACCOUNT_TOKEN,
22
- }
23
-
24
- HEADERS_POST = {
25
- "accept": "application/json",
26
- "accept-language": "ja",
27
- "content-type": "application/json",
28
- "x-account": ACCOUNT_TOKEN,
29
- }
30
-
31
  # ===== 設定値 =====
32
  RANKING_KEYWORD = "あけおめ"
33
- MAX_PARTICIPANTS = 1
34
  MONITOR_DURATION_MINUTES = 10
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- # ===== ユーティリティ関数 =====
38
  def parse_updated_at(value):
39
  """メッセージのupdatedAtをパースしてdatetimeを返す"""
40
  if isinstance(value, (int, float)):
@@ -47,6 +52,12 @@ def parse_updated_at(value):
47
 
48
  def get_messages():
49
  """グループチャットのメッセージを取得"""
 
 
 
 
 
 
50
  params = {
51
  "sortOrder": "asc",
52
  "limit": 100,
@@ -55,20 +66,27 @@ def get_messages():
55
 
56
  try:
57
  res = requests.get(
58
- GET_MESSAGES_URL,
59
- headers=HEADERS_GET,
60
  params=params,
61
  timeout=30,
62
  )
63
  res.raise_for_status()
64
  return res.json().get("messages", [])
65
  except Exception as e:
66
- print(f"メッセージ取得エラー: {e}")
67
  return []
68
 
69
 
70
  def post_message(text):
71
  """グループチャットにメッセージを送信"""
 
 
 
 
 
 
 
72
  payload = {
73
  "requestId": f"desk-web-{int(time.time() * 1000)}",
74
  "blocks": [
@@ -78,16 +96,16 @@ def post_message(text):
78
 
79
  try:
80
  res = requests.post(
81
- POST_MESSAGE_URL,
82
- headers=HEADERS_POST,
83
  json=payload,
84
  timeout=30,
85
  )
86
  res.raise_for_status()
87
- print("メッセージ送信成功")
88
  return True
89
  except Exception as e:
90
- print(f"メッセージ送信エラー: {e}")
91
  return False
92
 
93
 
@@ -114,13 +132,21 @@ def create_ranking_message(participants):
114
 
115
  def get_user_name(msg):
116
  """メッセージからユーザー名を取得"""
117
- # Channel.ioのメッセージ構造からユーザー名を抽出
118
- # personName または name フィールドを使用
119
  name = msg.get("personName") or msg.get("name") or "不明"
120
  return name
121
 
122
 
123
- # ===== メイン処理 =====
 
 
 
 
 
 
 
 
 
 
124
  def wait_until_midnight():
125
  """次の0:00まで待機"""
126
  now = datetime.now(JST)
@@ -130,26 +156,24 @@ def wait_until_midnight():
130
  wait_seconds = (midnight - now).total_seconds()
131
 
132
  if wait_seconds > 0:
133
- print(f"次の0:00まで {int(wait_seconds)}秒 待機します...")
134
  time.sleep(wait_seconds)
135
 
136
 
137
  def monitor_and_rank():
138
  """0:00から10分間監視し、あけおめランキングを作成"""
139
- print("あけおめ監視を開始します...")
140
 
141
  participants = []
142
  processed_ids = set()
143
  start_time = datetime.now(JST)
144
  end_time = start_time + timedelta(minutes=MONITOR_DURATION_MINUTES)
145
 
146
- print(f"監視期間: {start_time.strftime('%Y-%m-%d %H:%M:%S')} 〜 {end_time.strftime('%H:%M:%S')}")
147
 
148
  while datetime.now(JST) < end_time:
149
- # 現在時刻が0:00以降かつ監視期間内かチェック
150
  now = datetime.now(JST)
151
 
152
- # メッセージを取得
153
  messages = get_messages()
154
 
155
  for msg in messages:
@@ -163,103 +187,107 @@ def monitor_and_rank():
163
  if not plain_text or not updated_at:
164
  continue
165
 
166
- # メッセージ時刻をパース
167
  msg_time = parse_updated_at(updated_at)
168
  if not msg_time:
169
  continue
170
 
171
- # 0:00以降のメッセージかチェック
172
- midnight_today = now.replace(hour=11, minute=0, second=0, microsecond=0)
173
  if msg_time < midnight_today:
174
  processed_ids.add(msg_id)
175
  continue
176
 
177
- # 「あけおめ」を含むかチェック
178
  if RANKING_KEYWORD in plain_text:
179
  user_name = get_user_name(msg)
180
 
181
- # 既に参加者に含まれていないかチェック
182
  if not any(p["id"] == msg_id for p in participants):
183
  participants.append({
184
  "id": msg_id,
185
  "name": user_name,
186
  "time": msg_time,
187
  })
188
- print(f"参加者を検知: {user_name} ({msg_time.strftime('%H:%M:%S')})")
189
 
190
  processed_ids.add(msg_id)
191
  else:
192
  processed_ids.add(msg_id)
193
 
194
- # 6人達成チェック
195
  if len(participants) >= MAX_PARTICIPANTS:
196
- print(f"{MAX_PARTICIPANTS}人達成!ランキングを発表します。")
197
  break
198
 
199
- # 少し待機
200
  time.sleep(5)
201
 
202
- # ランキング発表
203
  ranking_msg = create_ranking_message(participants)
204
  post_message(ranking_msg)
205
 
206
- print("ランキング発表完了")
207
 
208
 
209
- def check_config():
210
- """設定が正しいかチェ"""
211
- missing = []
212
 
213
- if not CHANNEL_ID:
214
- missing.append("CHANNEL_ID")
215
- if not GROUP_CHAT_ID:
216
- missing.append("GROUP_CHAT_ID")
217
- if not ACCOUNT_TOKEN:
218
- missing.append("ACCOUNT_TOKEN")
219
 
220
- if missing:
221
- print("エラー: 以下の環境変数が設定されていません:")
222
- for var in missing:
223
- print(f" - {var}")
224
- return False
225
-
226
- return True
227
-
228
-
229
- def main():
230
- """メインループ"""
231
- print("あけおめランキングボットを起動します...")
232
-
233
- # 環境変数が設定されるまで待機
234
- while not check_config():
235
- print("環境変数が設定されるのを待機しています...")
236
- print("Hugging Face SpacesのSettings → Variables and secrets で以下を設定してください:")
237
- print(" - CHANNEL_ID")
238
- print(" - GROUP_CHAT_ID")
239
- print(" - ACCOUNT_TOKEN")
240
- print("60秒後に再試行します...")
241
- time.sleep(60)
242
-
243
- print("設定確認OK。毎日0:00からの監視を開始します。")
244
-
245
- while True:
246
  try:
247
- # 0:00まで待機
248
  wait_until_midnight()
249
-
250
- # 監視とランキング作成
251
  monitor_and_rank()
252
-
253
- # 少し待機してから次のループへ
254
  time.sleep(60)
255
-
256
- except KeyboardInterrupt:
257
- print("ボットを停止します。")
258
- break
259
  except Exception as e:
260
- print(f"エラーが発生しました: {e}")
261
  time.sleep(10)
262
 
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  if __name__ == "__main__":
265
- main()
 
1
  import os
2
  import time
3
+ import threading
4
  import requests
5
  from datetime import datetime, timedelta
6
  from zoneinfo import ZoneInfo
7
+ import gradio as gr
8
 
9
  # ===== 設定(環境変数から取得)=====
10
  CHANNEL_ID = os.getenv("CHANNEL_ID", "")
 
13
 
14
  JST = ZoneInfo("Asia/Tokyo")
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  # ===== 設定値 =====
17
  RANKING_KEYWORD = "あけおめ"
18
+ MAX_PARTICIPANTS = 6
19
  MONITOR_DURATION_MINUTES = 10
20
 
21
+ # ===== グローバル状態 =====
22
+ status_log = []
23
+ running = True
24
+
25
+
26
+ def log_message(msg):
27
+ """ログメッセージを追加"""
28
+ timestamp = datetime.now(JST).strftime("%Y-%m-%d %H:%M:%S")
29
+ status_log.append(f"[{timestamp}] {msg}")
30
+ if len(status_log) > 100:
31
+ status_log.pop(0)
32
+
33
+
34
+ def get_headers():
35
+ """ヘッダーを取得"""
36
+ return {
37
+ "accept": "application/json",
38
+ "accept-language": "ja",
39
+ "x-account": ACCOUNT_TOKEN,
40
+ }
41
+
42
 
 
43
  def parse_updated_at(value):
44
  """メッセージのupdatedAtをパースしてdatetimeを返す"""
45
  if isinstance(value, (int, float)):
 
52
 
53
  def get_messages():
54
  """グループチャットのメッセージを取得"""
55
+ if not CHANNEL_ID or not GROUP_CHAT_ID:
56
+ return []
57
+
58
+ base_url = f"https://desk-api.channel.io/desk/channels/{CHANNEL_ID}"
59
+ get_url = f"{base_url}/groups/{GROUP_CHAT_ID}/messages"
60
+
61
  params = {
62
  "sortOrder": "asc",
63
  "limit": 100,
 
66
 
67
  try:
68
  res = requests.get(
69
+ get_url,
70
+ headers=get_headers(),
71
  params=params,
72
  timeout=30,
73
  )
74
  res.raise_for_status()
75
  return res.json().get("messages", [])
76
  except Exception as e:
77
+ log_message(f"メッセージ取得エラー: {e}")
78
  return []
79
 
80
 
81
  def post_message(text):
82
  """グループチャットにメッセージを送信"""
83
+ if not CHANNEL_ID or not GROUP_CHAT_ID:
84
+ log_message("設定エラー: メッセージ送信できません")
85
+ return False
86
+
87
+ base_url = f"https://desk-api.channel.io/desk/channels/{CHANNEL_ID}"
88
+ post_url = f"{base_url}/groups/{GROUP_CHAT_ID}/messages"
89
+
90
  payload = {
91
  "requestId": f"desk-web-{int(time.time() * 1000)}",
92
  "blocks": [
 
96
 
97
  try:
98
  res = requests.post(
99
+ post_url,
100
+ headers=get_headers(),
101
  json=payload,
102
  timeout=30,
103
  )
104
  res.raise_for_status()
105
+ log_message("メッセージ送信成功")
106
  return True
107
  except Exception as e:
108
+ log_message(f"メッセージ送信エラー: {e}")
109
  return False
110
 
111
 
 
132
 
133
  def get_user_name(msg):
134
  """メッセージからユーザー名を取得"""
 
 
135
  name = msg.get("personName") or msg.get("name") or "不明"
136
  return name
137
 
138
 
139
+ def check_config():
140
+ """設定が正しいかチェック"""
141
+ if not CHANNEL_ID:
142
+ return False, "CHANNEL_ID が設定されていません"
143
+ if not GROUP_CHAT_ID:
144
+ return False, "GROUP_CHAT_ID が設定されていません"
145
+ if not ACCOUNT_TOKEN:
146
+ return False, "ACCOUNT_TOKEN が設定されていません"
147
+ return True, "設定OK"
148
+
149
+
150
  def wait_until_midnight():
151
  """次の0:00まで待機"""
152
  now = datetime.now(JST)
 
156
  wait_seconds = (midnight - now).total_seconds()
157
 
158
  if wait_seconds > 0:
159
+ log_message(f"次の0:00まで {int(wait_seconds)}秒 待機します...")
160
  time.sleep(wait_seconds)
161
 
162
 
163
  def monitor_and_rank():
164
  """0:00から10分間監視し、あけおめランキングを作成"""
165
+ log_message("あけおめ監視を開始します...")
166
 
167
  participants = []
168
  processed_ids = set()
169
  start_time = datetime.now(JST)
170
  end_time = start_time + timedelta(minutes=MONITOR_DURATION_MINUTES)
171
 
172
+ log_message(f"監視期間: {start_time.strftime('%Y-%m-%d %H:%M:%S')} 〜 {end_time.strftime('%H:%M:%S')}")
173
 
174
  while datetime.now(JST) < end_time:
 
175
  now = datetime.now(JST)
176
 
 
177
  messages = get_messages()
178
 
179
  for msg in messages:
 
187
  if not plain_text or not updated_at:
188
  continue
189
 
 
190
  msg_time = parse_updated_at(updated_at)
191
  if not msg_time:
192
  continue
193
 
194
+ midnight_today = now.replace(hour=0, minute=0, second=0, microsecond=0)
 
195
  if msg_time < midnight_today:
196
  processed_ids.add(msg_id)
197
  continue
198
 
 
199
  if RANKING_KEYWORD in plain_text:
200
  user_name = get_user_name(msg)
201
 
 
202
  if not any(p["id"] == msg_id for p in participants):
203
  participants.append({
204
  "id": msg_id,
205
  "name": user_name,
206
  "time": msg_time,
207
  })
208
+ log_message(f"参加者を検知: {user_name} ({msg_time.strftime('%H:%M:%S')})")
209
 
210
  processed_ids.add(msg_id)
211
  else:
212
  processed_ids.add(msg_id)
213
 
 
214
  if len(participants) >= MAX_PARTICIPANTS:
215
+ log_message(f"{MAX_PARTICIPANTS}人達成!ランキングを発表します。")
216
  break
217
 
 
218
  time.sleep(5)
219
 
 
220
  ranking_msg = create_ranking_message(participants)
221
  post_message(ranking_msg)
222
 
223
+ log_message("ランキング発表完了")
224
 
225
 
226
+ def bot_loop():
227
+ """トのメインループ"""
228
+ global running
229
 
230
+ log_message("あけおめランキングボットを起動します...")
 
 
 
 
 
231
 
232
+ while running:
233
+ ok, msg = check_config()
234
+ if not ok:
235
+ log_message(f"設定エラー: {msg}")
236
+ log_message("環境変数を設定してください: CHANNEL_ID, GROUP_CHAT_ID, ACCOUNT_TOKEN")
237
+ time.sleep(60)
238
+ continue
239
+
240
+ log_message("設定確認OK。毎日0:00からの監視を開始します。")
241
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  try:
 
243
  wait_until_midnight()
 
 
244
  monitor_and_rank()
 
 
245
  time.sleep(60)
 
 
 
 
246
  except Exception as e:
247
+ log_message(f"エラーが発生しました: {e}")
248
  time.sleep(10)
249
 
250
 
251
+ # ===== Gradio UI =====
252
+ def get_status():
253
+ """ステータスを取得"""
254
+ ok, msg = check_config()
255
+ config_status = f"✅ {msg}" if ok else f"❌ {msg}"
256
+ return config_status
257
+
258
+
259
+ def get_logs():
260
+ """ログを取得"""
261
+ return "\n".join(status_log[-50:])
262
+
263
+
264
+ def refresh_status():
265
+ """ステータスを更新"""
266
+ return get_status(), get_logs()
267
+
268
+
269
+ # Gradioインターフェースを作成
270
+ with gr.Blocks(title="あけおめランキングボット") as demo:
271
+ gr.Markdown("# 🏆 あけおめランキングボット")
272
+ gr.Markdown("毎日0:00から10分間、「あけおめ」といった人を監視し、ランキングを作成します。")
273
+
274
+ with gr.Row():
275
+ config_box = gr.Textbox(label="設定状態", value=get_status(), interactive=False)
276
+
277
+ with gr.Row():
278
+ log_box = gr.Textbox(label="ログ", value=get_logs(), lines=15, interactive=False)
279
+
280
+ refresh_btn = gr.Button("🔄 更新")
281
+ refresh_btn.click(refresh_status, inputs=[], outputs=[config_box, log_box])
282
+
283
+ # 定期的に更新
284
+ demo.load(refresh_status, inputs=[], outputs=[config_box, log_box], every=10)
285
+
286
+
287
+ # バックグラウンドでボットを開始
288
+ bot_thread = threading.Thread(target=bot_loop, daemon=True)
289
+ bot_thread.start()
290
+
291
+
292
  if __name__ == "__main__":
293
+ demo.launch()
requirements.txt CHANGED
@@ -1 +1,2 @@
1
- requests>=2.28.0
 
 
1
+ requests>=2.28.0
2
+ gradio>=4.0.0