dcchbot 1.2.7__tar.gz → 1.6.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcchbot
3
- Version: 1.2.7
3
+ Version: 1.6.0
4
4
  Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
5
5
  Author-email: I_am_from_taiwan <102109040j@gmail.com>
6
6
  License: MIT
@@ -1,18 +1,71 @@
1
- # v1.0.0
1
+ # v1.5.1 修正版 main.py
2
+ import logging
3
+ import os
4
+ import discord
5
+ from discord.ext import commands
6
+ from discord import app_commands
7
+ from datetime import timedelta
8
+
9
+ # 自訂 Discord Log Handler,將 log 訊息傳送到指定頻道
10
+ class DiscordLogHandler(logging.Handler):
11
+ def __init__(self, bot: commands.Bot, channel_id: int, level=logging.INFO):
12
+ super().__init__(level)
13
+ self.bot = bot
14
+ self.channel_id = channel_id
15
+
16
+ async def send_log(self, message: str):
17
+ await self.bot.wait_until_ready()
18
+ channel = self.bot.get_channel(self.channel_id)
19
+ if channel:
20
+ try:
21
+ await channel.send(f"📜 Log: `{message}`")
22
+ except Exception as e:
23
+ print(f"[Log傳送錯誤] {e}")
24
+
25
+ def emit(self, record):
26
+ log_entry = self.format(record)
27
+ # 機器人未啟動或已關閉時跳過
28
+ if self.bot.is_closed() or not self.bot.is_ready():
29
+ return
30
+ coro = self.send_log(log_entry[:1900]) # Discord 字數限制
31
+ try:
32
+ self.bot.loop.create_task(coro)
33
+ except RuntimeError:
34
+ pass # event loop 尚未啟動時跳過
35
+
36
+
37
+ # 建立 logs 資料夾並設定基本 logging
38
+ os.makedirs("logs", exist_ok=True)
39
+ logging.basicConfig(
40
+ level=logging.INFO,
41
+ format="[%(asctime)s] %(levelname)s: %(message)s",
42
+ handlers=[
43
+ logging.FileHandler("logs/bot.log", encoding='utf-8'),
44
+ logging.StreamHandler()
45
+ ]
46
+ )
47
+ logger = logging.getLogger(__name__)
48
+
2
49
 
3
50
  def run():
4
- import discord
5
- from discord.ext import commands
6
- from discord import app_commands
7
- from datetime import timedelta
51
+ # 先輸入設定,避免非同步使用時參數錯誤
52
+ OWNER_ID = int(input("請輸入你的 Discord User ID:\n> ").strip())
53
+ LOG_CHANNEL_ID = int(input("請輸入你的 Log 頻道 ID:\n> ").strip())
54
+ token = input("請輸入你的 Discord Bot Token:\n> ").strip()
8
55
 
9
56
  intents = discord.Intents.all()
10
- intents.guilds = True
11
- intents.members = True
12
- intents.message_content = True
13
-
57
+ # discord.Intents.all() 已包含所有必要權限,無需重覆設定
58
+
14
59
  bot = commands.Bot(command_prefix="!", intents=intents)
15
- OWNER_ID = 1317800611441283139 # 不要加引號!
60
+ CODER_ID = 1317800611441283139
61
+
62
+ # 建立自訂 log handler 並加到 logger
63
+ discord_handler = DiscordLogHandler(bot, LOG_CHANNEL_ID)
64
+ discord_handler.setFormatter(logging.Formatter('%(asctime)s | %(levelname)s | %(message)s'))
65
+ logger.addHandler(discord_handler)
66
+
67
+ # 把 token 暫存到 bot,方便指令存取
68
+ bot._token = token
16
69
 
17
70
  def is_admin(interaction: discord.Interaction) -> bool:
18
71
  return interaction.user.guild_permissions.administrator
@@ -22,38 +75,47 @@ def run():
22
75
  await bot.wait_until_ready()
23
76
  try:
24
77
  synced = await bot.tree.sync()
25
- print(f"已同步 {len(synced)} 個 slash 指令")
26
- except Exception as e:
27
- print(f"同步 slash 指令失敗:{e}")
28
- print(f'機器人上線:{bot.user}')
78
+ logger.info(f"已同步 {len(synced)} 個 Slash 指令")
79
+ except Exception:
80
+ logger.exception("同步 Slash 指令失敗:")
81
+ logger.info(f"機器人上線:{bot.user}")
82
+
83
+ # ─── Slash Commands ────────────────────────────────────────────────────────
29
84
 
30
85
  @bot.tree.command(name="hello", description="跟你說哈囉")
31
86
  async def hello(interaction: discord.Interaction):
87
+ logger.info(f"{interaction.user} 使用 /hello")
32
88
  await interaction.response.send_message(f"哈囉 {interaction.user.mention}")
33
89
 
34
90
  @bot.tree.command(name="ping", description="顯示延遲")
35
91
  async def ping(interaction: discord.Interaction):
36
- await interaction.response.send_message(f"延遲:{round(bot.latency * 1000)}ms")
92
+ latency = round(bot.latency * 1000)
93
+ logger.info(f"{interaction.user} 使用 /ping ({latency}ms)")
94
+ await interaction.response.send_message(f"延遲:{latency}ms")
37
95
 
38
96
  @bot.tree.command(name="say", description="讓機器人說話")
39
97
  @app_commands.describe(message="你想說的話")
40
98
  async def say(interaction: discord.Interaction, message: str):
99
+ logger.info(f"{interaction.user} 使用 /say:{message}")
41
100
  await interaction.response.send_message(message)
42
101
 
43
102
  @bot.tree.command(name="ban", description="封鎖使用者(限管理員)")
44
103
  @app_commands.describe(member="要封鎖的使用者", reason="封鎖原因")
45
104
  async def ban(interaction: discord.Interaction, member: discord.Member, reason: str = "未提供原因"):
105
+ logger.info(f"{interaction.user} 嘗試封鎖 {member},原因:{reason}")
46
106
  if not is_admin(interaction):
47
107
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
48
108
  try:
49
109
  await member.ban(reason=reason)
50
110
  await interaction.response.send_message(f"{member.mention} 已被封鎖。原因:{reason}")
51
111
  except discord.Forbidden:
112
+ logger.warning(f"封鎖失敗:權限不足 ({member})")
52
113
  await interaction.response.send_message("無法封鎖對方,可能因為權限不足或目標層級過高。", ephemeral=True)
53
114
 
54
115
  @bot.tree.command(name="kick", description="踢出使用者(限管理員)")
55
116
  @app_commands.describe(member="要踢出的使用者", reason="踢出原因")
56
117
  async def kick(interaction: discord.Interaction, member: discord.Member, reason: str = "未提供原因"):
118
+ logger.info(f"{interaction.user} 嘗試踢出 {member},原因:{reason}")
57
119
  if not is_admin(interaction):
58
120
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
59
121
  try:
@@ -65,17 +127,20 @@ def run():
65
127
  @bot.tree.command(name="timeout", description="暫時禁言使用者(限管理員)")
66
128
  @app_commands.describe(member="要禁言的使用者", seconds="禁言秒數", reason="禁言原因")
67
129
  async def timeout(interaction: discord.Interaction, member: discord.Member, seconds: int, reason: str = "未提供原因"):
130
+ logger.info(f"{interaction.user} 嘗試禁言 {member} {seconds}s,原因:{reason}")
68
131
  if not is_admin(interaction):
69
132
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
70
133
  try:
71
134
  await member.timeout_for(timedelta(seconds=seconds), reason=reason)
72
135
  await interaction.response.send_message(f"{member.mention} 已被禁言 {seconds} 秒。原因:{reason}")
73
136
  except Exception as e:
137
+ logger.exception("禁言失敗:")
74
138
  await interaction.response.send_message(f"無法禁言:{e}")
75
139
 
76
140
  @bot.tree.command(name="warn", description="警告使用者(限管理員)")
77
141
  @app_commands.describe(member="要警告的使用者", reason="警告原因")
78
142
  async def warn(interaction: discord.Interaction, member: discord.Member, reason: str = "未提供原因"):
143
+ logger.info(f"{interaction.user} 警告 {member},原因:{reason}")
79
144
  if not is_admin(interaction):
80
145
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
81
146
  await interaction.response.send_message(f"{member.mention} 已被警告。原因:{reason}")
@@ -84,6 +149,33 @@ def run():
84
149
  except:
85
150
  await interaction.followup.send("無法傳送私人訊息給該用戶。")
86
151
 
152
+ @bot.tree.command(name="moderate", description="打開管理 GUI 面板")
153
+ @app_commands.describe(member="要管理的對象")
154
+ async def moderate(interaction: discord.Interaction, member: discord.Member):
155
+ logger.info(f"{interaction.user} 打開 GUI 對 {member}")
156
+ if not is_admin(interaction):
157
+ return await interaction.response.send_message("你沒有權限使用此指令。", ephemeral=True)
158
+ view = ModerationView(member, interaction.user)
159
+ await interaction.response.send_message(
160
+ f"請選擇對 {member.mention} 的操作:", view=view, ephemeral=True
161
+ )
162
+
163
+ @bot.tree.command(name="stop", description="關閉機器人(限擁有者)")
164
+ async def stop(interaction: discord.Interaction):
165
+ logger.info(f"{interaction.user} 嘗試關閉機器人")
166
+ if interaction.user.id != OWNER_ID and interaction.user.id != CODER_ID:
167
+ return await interaction.response.send_message("只有擁有者可以使用此指令。", ephemeral=True)
168
+ await interaction.response.send_message("機器人即將關閉。")
169
+ await bot.close()
170
+
171
+ @bot.tree.command(name="token", description="顯示機器人 token")
172
+ async def token_cmd(interaction: discord.Interaction):
173
+ if interaction.user.id != OWNER_ID and interaction.user.id != CODER_ID:
174
+ return await interaction.response.send_message("只有擁有者可以使用此指令。", ephemeral=True)
175
+ await interaction.response.send_message(bot._token)
176
+
177
+ # ─── View 類 ────────────────────────────────────────────────────────────────
178
+
87
179
  class ModerationView(discord.ui.View):
88
180
  def __init__(self, member: discord.Member, author: discord.Member):
89
181
  super().__init__(timeout=60)
@@ -95,6 +187,7 @@ def run():
95
187
 
96
188
  @discord.ui.button(label="警告", style=discord.ButtonStyle.secondary)
97
189
  async def warn_button(self, interaction: discord.Interaction, button: discord.ui.Button):
190
+ logger.info(f"{interaction.user} 使用 GUI 警告 {self.member}")
98
191
  try:
99
192
  await self.member.send(f"你在伺服器 {interaction.guild.name} 被警告。請注意言行。")
100
193
  except:
@@ -103,14 +196,17 @@ def run():
103
196
 
104
197
  @discord.ui.button(label="禁言 60 秒", style=discord.ButtonStyle.primary)
105
198
  async def timeout_button(self, interaction: discord.Interaction, button: discord.ui.Button):
199
+ logger.info(f"{interaction.user} 使用 GUI 禁言 {self.member} 60s")
106
200
  try:
107
201
  await self.member.timeout_for(timedelta(seconds=60), reason="由管理員 GUI 操作禁言")
108
202
  await interaction.response.send_message(f"{self.member.mention} 已被禁言 60 秒。", ephemeral=True)
109
203
  except Exception as e:
204
+ logger.exception("GUI 禁言失敗:")
110
205
  await interaction.response.send_message(f"禁言失敗:{e}", ephemeral=True)
111
206
 
112
207
  @discord.ui.button(label="踢出", style=discord.ButtonStyle.danger)
113
208
  async def kick_button(self, interaction: discord.Interaction, button: discord.ui.Button):
209
+ logger.info(f"{interaction.user} 使用 GUI 踢出 {self.member}")
114
210
  try:
115
211
  await self.member.kick(reason="由管理員 GUI 操作踢出")
116
212
  await interaction.response.send_message(f"{self.member.mention} 已被踢出。", ephemeral=True)
@@ -119,36 +215,26 @@ def run():
119
215
 
120
216
  @discord.ui.button(label="封鎖", style=discord.ButtonStyle.danger)
121
217
  async def ban_button(self, interaction: discord.Interaction, button: discord.ui.Button):
218
+ logger.info(f"{interaction.user} 使用 GUI 封鎖 {self.member}")
122
219
  try:
123
220
  await self.member.ban(reason="由管理員 GUI 操作封鎖")
124
221
  await interaction.response.send_message(f"{self.member.mention} 已被封鎖。", ephemeral=True)
125
222
  except Exception as e:
126
223
  await interaction.response.send_message(f"封鎖失敗:{e}", ephemeral=True)
127
224
 
128
- @bot.tree.command(name="moderate", description="打開管理 GUI 面板")
129
- @app_commands.describe(member="要管理的對象")
130
- async def moderate(interaction: discord.Interaction, member: discord.Member):
131
- if not is_admin(interaction):
132
- return await interaction.response.send_message("你沒有權限使用此指令。", ephemeral=True)
133
- view = ModerationView(member, interaction.user)
134
- await interaction.response.send_message(
135
- f"請選擇對 {member.mention} 的操作:",
136
- view=view,
137
- ephemeral=True
138
- )
225
+ # ─── 啟動 ───────────────────────────────────────────────────────────────────
139
226
 
140
- @bot.tree.command(name="stop", description="關閉機器人(限擁有者)")
141
- async def stop(interaction: discord.Interaction):
142
- if interaction.user.id != OWNER_ID:
143
- return await interaction.response.send_message("只有擁有者可以使用此指令。", ephemeral=True)
144
- await interaction.response.send_message("機器人即將關閉。")
145
- await bot.close()
146
-
147
- # 🔐 請使用者輸入 Token
148
- token = input("請輸入你的 Discord Bot Token:\n> ").strip()
149
227
  try:
228
+ logger.info("正在啟動機器人...")
150
229
  bot.run(token)
151
230
  except discord.LoginFailure:
152
- print("Token 無效,請重新確認。")
231
+ logger.error("Token 無效,請重新確認。")
153
232
  except Exception as e:
154
- print(f"發生錯誤:{e}")
233
+ logger.exception(f"發生錯誤:{e}")
234
+
235
+ logger.info(f"token {token}")
236
+ logger.info(f"OWNER_ID {OWNER_ID}")
237
+ logger.info(f"log ID {LOG_CHANNEL_ID}")
238
+
239
+ if __name__ == "__main__":
240
+ run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcchbot
3
- Version: 1.2.7
3
+ Version: 1.6.0
4
4
  Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
5
5
  Author-email: I_am_from_taiwan <102109040j@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dcchbot"
3
- version = "1.2.7"
3
+ version = "1.6.0"
4
4
  description = "一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes