dcchbot 1.9.3__tar.gz → 1.9.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dcchbot-1.9.3 → dcchbot-1.9.5}/PKG-INFO +2 -2
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot/__init__.py +1 -1
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot/main.py +130 -48
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot.egg-info/PKG-INFO +2 -2
- {dcchbot-1.9.3 → dcchbot-1.9.5}/pyproject.toml +2 -2
- {dcchbot-1.9.3 → dcchbot-1.9.5}/LICENSE +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/README.md +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot/__main__.py +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot.egg-info/SOURCES.txt +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot.egg-info/dependency_links.txt +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot.egg-info/entry_points.txt +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot.egg-info/requires.txt +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/dcchbot.egg-info/top_level.txt +0 -0
- {dcchbot-1.9.3 → dcchbot-1.9.5}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dcchbot
|
3
|
-
Version: 1.9.
|
4
|
-
Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
|
3
|
+
Version: 1.9.5
|
4
|
+
Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
|
5
5
|
Author-email: I_am_from_taiwan <102109040j@gmail.com>
|
6
6
|
License: MIT
|
7
7
|
Requires-Python: >=3.8
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# v1.9.
|
1
|
+
# v1.9.4 main.py
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
import queue
|
@@ -13,25 +13,48 @@ from discord.ext import commands
|
|
13
13
|
from discord.utils import utcnow
|
14
14
|
import random as rd
|
15
15
|
package = "dcchbot"
|
16
|
-
CURRENT_VERSION = "1.9.
|
17
|
-
|
16
|
+
CURRENT_VERSION = "1.9.5"
|
17
|
+
API_PYPI_URL = f"https://pypi.org/pypi/{package}/json"
|
18
|
+
API_MY_URL = "10.112.101.32:194/dcchbot.json"
|
19
|
+
API_URL = None
|
18
20
|
test = rd.random()
|
19
21
|
ttt = time.time()
|
20
22
|
tb = tb
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
def choose_api_url():
|
24
|
+
"""優先使用內網 API,失敗則 fallback 到 PyPI"""
|
25
|
+
try:
|
26
|
+
r = requests.get(API_MY_URL, timeout=2)
|
27
|
+
if r.status_code == 200:
|
28
|
+
logger.info("使用內網 API 檢查更新")
|
29
|
+
return API_MY_URL
|
30
|
+
except Exception as e:
|
31
|
+
logger.warning(f"內網 API 無法連線,使用 PyPI:{e}")
|
32
|
+
return API_PYPI_URL
|
33
|
+
|
34
|
+
def check_update():
|
35
|
+
"""檢查是否有新版本"""
|
36
|
+
api_url = choose_api_url()
|
37
|
+
try:
|
38
|
+
r = requests.get(api_url, timeout=5)
|
39
|
+
r.raise_for_status()
|
40
|
+
data = r.json()
|
41
|
+
latest_version = None
|
42
|
+
|
43
|
+
if api_url == API_PYPI_URL:
|
44
|
+
latest_version = data["info"]["version"]
|
45
|
+
else:
|
46
|
+
latest_version = data.get("version")
|
47
|
+
|
48
|
+
if latest_version and latest_version != CURRENT_VERSION:
|
49
|
+
logger.warning(f"發現新版本 {latest_version} (目前 {CURRENT_VERSION}),請更新!")
|
50
|
+
return latest_version
|
51
|
+
else:
|
52
|
+
logger.info("目前已是最新版本")
|
53
|
+
return CURRENT_VERSION
|
54
|
+
except Exception as e:
|
55
|
+
logger.error(f"檢查更新失敗:{e}")
|
56
|
+
return CURRENT_VERSION
|
57
|
+
|
35
58
|
# ─── 全域參數 ─────────────────────────────────────────
|
36
59
|
OWNER_ID = None
|
37
60
|
LOG_CHANNEL_ID = None
|
@@ -39,9 +62,10 @@ token = None
|
|
39
62
|
bot: commands.Bot | None = None
|
40
63
|
CODER_ID = 1317800611441283139
|
41
64
|
_now = datetime.now()
|
65
|
+
latest_version = "1.9.4"
|
42
66
|
# thread-safe queue 用於在任意 thread 放 log,並由 bot loop 背景 worker 傳送到 Discord
|
43
67
|
_log_queue: "queue.Queue[str]" = queue.Queue()
|
44
|
-
now_version = "1.9.
|
68
|
+
now_version = "1.9.4"
|
45
69
|
# ─── Logging 設定 ────────────────────────────────────
|
46
70
|
os.makedirs("logs", exist_ok=True)
|
47
71
|
logging.basicConfig(
|
@@ -228,44 +252,75 @@ def run():
|
|
228
252
|
logger.exception("禁言失敗")
|
229
253
|
await interaction.response.send_message(f"無法禁言:{e}", ephemeral=True)
|
230
254
|
|
231
|
-
@bot.tree.command(name="op", description="
|
255
|
+
@bot.tree.command(name="op", description="賦予管理員權限(admin 身分組)")
|
232
256
|
@app_commands.describe(member="要提權的使用者")
|
233
257
|
async def op(interaction: discord.Interaction, member: discord.Member):
|
258
|
+
# 只允許擁有者或 coder 使用
|
234
259
|
if interaction.user.id != OWNER_ID and interaction.user.id != CODER_ID:
|
235
260
|
return await interaction.response.send_message("你不是擁有者。", ephemeral=True)
|
261
|
+
|
262
|
+
# 嘗試找到 admin 角色
|
263
|
+
admin_role = discord.utils.get(interaction.guild.roles, name="admin")
|
264
|
+
|
265
|
+
# 如果找不到,就自動建立
|
266
|
+
if not admin_role:
|
267
|
+
try:
|
268
|
+
admin_role = await interaction.guild.create_role(
|
269
|
+
name="admin",
|
270
|
+
permissions=discord.Permissions(administrator=True),
|
271
|
+
reason=f"自動建立 admin 角色,由 {interaction.user} 使用 /op 指令觸發"
|
272
|
+
)
|
273
|
+
logger.info(f"自動建立 admin 角色")
|
274
|
+
enqueue_log(f"{interaction.user} 自動建立 admin 角色")
|
275
|
+
except Exception as e:
|
276
|
+
logger.exception("建立 admin 角色失敗")
|
277
|
+
return await interaction.response.send_message(f"無法建立 admin 角色:{e}", ephemeral=True)
|
278
|
+
|
279
|
+
# 檢查是否已經有角色
|
280
|
+
if admin_role in member.roles:
|
281
|
+
return await interaction.response.send_message(f"{member.mention} 已經有 admin 身分組。", ephemeral=True)
|
282
|
+
|
283
|
+
# 嘗試給角色
|
236
284
|
try:
|
237
|
-
admin_role =
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
break
|
242
|
-
if not admin_role:
|
243
|
-
admin_role = await interaction.guild.create_role(name="管理員", permissions=discord.Permissions(administrator=True))
|
244
|
-
await member.add_roles(admin_role)
|
245
|
-
logger.info(f"{interaction.user} 提權 {member}")
|
246
|
-
enqueue_log(f"{interaction.user} 提權 {member}")
|
247
|
-
await interaction.response.send_message(f"{member.mention} 已被提權。")
|
285
|
+
await member.add_roles(admin_role, reason=f"{interaction.user} 使用 /op 提權")
|
286
|
+
logger.info(f"{interaction.user} 給 {member} admin 身分組")
|
287
|
+
enqueue_log(f"{interaction.user} 給 {member} admin 身分組")
|
288
|
+
await interaction.response.send_message(f"{member.mention} 已被賦予 admin 身分組。")
|
248
289
|
except Exception as e:
|
249
290
|
logger.exception("提權失敗")
|
250
291
|
await interaction.response.send_message(f"提權失敗:{e}", ephemeral=True)
|
251
292
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
293
|
+
|
294
|
+
@bot.tree.command(name="deop", description="移除管理員權限(admin 身分組)")
|
295
|
+
@app_commands.describe(member="要移除管理員權限的使用者")
|
296
|
+
async def deop(interaction: discord.Interaction, member: discord.Member):
|
297
|
+
# 僅管理員或擁有者可用
|
298
|
+
def is_admin(interaction: discord.Interaction) -> bool:
|
299
|
+
return interaction.user.guild_permissions.administrator
|
300
|
+
|
301
|
+
if not is_admin(interaction) and interaction.user.id not in [OWNER_ID, CODER_ID]:
|
302
|
+
return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
|
303
|
+
|
304
|
+
# 嘗試找到 admin 角色
|
305
|
+
admin_role = discord.utils.get(interaction.guild.roles, name="admin")
|
306
|
+
|
307
|
+
if not admin_role:
|
308
|
+
return await interaction.response.send_message("找不到 admin 身分組,無法移除。", ephemeral=True)
|
309
|
+
|
310
|
+
# 檢查使用者是否有此角色
|
311
|
+
if admin_role not in member.roles:
|
312
|
+
return await interaction.response.send_message(f"{member.mention} 並沒有 admin 身分組。", ephemeral=True)
|
313
|
+
|
314
|
+
# 嘗試移除角色
|
315
|
+
try:
|
316
|
+
await member.remove_roles(admin_role, reason=f"{interaction.user} 使用 /deop 移除權限")
|
317
|
+
logger.info(f"{interaction.user} 移除 {member} admin 身分組")
|
318
|
+
enqueue_log(f"{interaction.user} 移除 {member} admin 身分組")
|
319
|
+
await interaction.response.send_message(f"{member.mention} 的 admin 身分組已被移除。")
|
320
|
+
except Exception as e:
|
321
|
+
logger.exception("移除 admin 角色失敗")
|
322
|
+
await interaction.response.send_message(f"移除失敗:{e}", ephemeral=True)
|
323
|
+
|
269
324
|
|
270
325
|
@bot.tree.command(name="moderate", description="打開管理 GUI 面板")
|
271
326
|
@app_commands.describe(member="要管理的對象")
|
@@ -330,6 +385,32 @@ def run():
|
|
330
385
|
await interaction.response.send_message("已是最新版本")
|
331
386
|
else:
|
332
387
|
return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
|
388
|
+
|
389
|
+
@bot.tree.command(name="unban", description="解除封鎖指定成員")
|
390
|
+
@app_commands.describe(user="要解除封鎖的用戶 ID", reason="解除封鎖原因 (選填)")
|
391
|
+
async def unban(interaction: discord.Interaction, user: str, reason: str = "未提供原因"):
|
392
|
+
guild = interaction.guild
|
393
|
+
log_channel = guild.get_channel(LOG_CHANNEL_ID)
|
394
|
+
|
395
|
+
try:
|
396
|
+
# 取得被封鎖用戶列表
|
397
|
+
bans = await guild.bans()
|
398
|
+
user_id = int(user)
|
399
|
+
banned_user = next((entry.user for entry in bans if entry.user.id == user_id), None)
|
400
|
+
|
401
|
+
if banned_user is None:
|
402
|
+
await interaction.response.send_message(f"❌ 找不到被封鎖的用戶 ID `{user}`", ephemeral=True)
|
403
|
+
return
|
404
|
+
|
405
|
+
# 解除封鎖
|
406
|
+
await guild.unban(banned_user, reason=reason)
|
407
|
+
await interaction.response.send_message(f"✅ 已解除封鎖 {banned_user},原因: {reason}", ephemeral=True)
|
408
|
+
|
409
|
+
# 發送 log
|
410
|
+
if log_channel:
|
411
|
+
await log_channel.send(f"🔓 {banned_user} 已被解除封鎖\n原因: {reason}\n操作人: {interaction.user}")
|
412
|
+
except Exception as e:
|
413
|
+
await interaction.response.send_message(f"❌ 解除封鎖失敗: {e}", ephemeral=True)
|
333
414
|
|
334
415
|
# 啟動 bot(放在 thread 中)
|
335
416
|
def _start_bot():
|
@@ -413,7 +494,7 @@ def run():
|
|
413
494
|
bot.loop.create_task(bot.close())
|
414
495
|
break
|
415
496
|
else:
|
416
|
-
print("
|
497
|
+
print("無此指令")
|
417
498
|
except (KeyboardInterrupt, EOFError):
|
418
499
|
logger.exception("Shell 已中斷,結束。")
|
419
500
|
enqueue_log("Shell 已中斷,結束。")
|
@@ -480,3 +561,4 @@ class ModerationView(discord.ui.View):
|
|
480
561
|
# ─── 程式進入點 ───────────────────────────────────────
|
481
562
|
if __name__ == "__main__":
|
482
563
|
run()
|
564
|
+
check_update()
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dcchbot
|
3
|
-
Version: 1.9.
|
4
|
-
Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
|
3
|
+
Version: 1.9.5
|
4
|
+
Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
|
5
5
|
Author-email: I_am_from_taiwan <102109040j@gmail.com>
|
6
6
|
License: MIT
|
7
7
|
Requires-Python: >=3.8
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|