TonyD365 commited on
Commit
0ceb8b2
·
verified ·
1 Parent(s): b1325db

Update async io to connect the bot

Browse files
Files changed (1) hide show
  1. app.py +49 -101
app.py CHANGED
@@ -3,6 +3,7 @@ import discord
3
  import time
4
  from fastapi import FastAPI
5
  import uvicorn
 
6
  from threading import Thread
7
  from discord import app_commands, ui
8
  # 导入数据库基础类
@@ -15,28 +16,28 @@ from material import (
15
  delete_material_logic,
16
  list_materials_logic
17
  )
 
18
  # 初始化数据库管理器
19
- # 假设你已经设置了环境变量 MONGO_URL
20
  db_manager = MongoManager(os.getenv("MONGO_URL"), "tonyd")
21
 
22
  # 身份组权限列表
23
  mod = [1357078628969746462, 1357079285113819146, 1436537252317626449, 1357079707539214417, 1357079889160835183]
24
 
25
- # --- 1. Flask 保活 ---
26
  app = FastAPI()
27
  @app.get('/')
28
  def home(): return {"Stauts": "Alive"}
29
 
30
  def run_flask():
 
31
  port = int(os.environ.get('PORT', 7860))
32
  uvicorn.run(app, host='0.0.0.0', port=port)
33
 
34
- # --- 2. 辅助函数:处理多个 Role Ping ---
35
  def format_pings(role_input: str):
36
  pass
37
 
38
  # --- 3. Modal 表单定义 ---
39
-
40
  class AnnounceModal(ui.Modal):
41
  def __init__(self, channel: discord.TextChannel, ping_roles_str: str = None):
42
  super().__init__(title="New Announcement")
@@ -47,21 +48,15 @@ class AnnounceModal(ui.Modal):
47
  announcement_body = ui.TextInput(label="Body", placeholder="Enter message...", style=discord.TextStyle.long)
48
 
49
  async def on_submit(self, interaction: discord.Interaction):
50
- # 准备正文
51
  content = self.announcement_body.value
52
  pings = self.ping_roles_str
53
-
54
- # 按照你的要求:Ping 放在文本最下面,但是在 --- 上面
55
- if pings:
56
- pass
57
 
58
  embed = discord.Embed(
59
  title=self.announcement_title.value,
60
  description=content,
61
  color=discord.Color.blue()
62
  )
63
-
64
- # Footer 只留发送人和时间
65
  footer = f"\n\n||{pings}||\n\n---\n**Sent by:** ||{interaction.user.mention}||\n**Time:** <t:{int(time.time())}:f>"
66
  embed.description += footer
67
 
@@ -75,7 +70,6 @@ class EditAnnounceModal(ui.Modal):
75
  self.new_ping_roles_str = new_ping_roles_str
76
 
77
  old_title = message.embeds[0].title if message.embeds else ""
78
- # 裁掉旧的 Footer
79
  old_body = message.embeds[0].description.split("\n\n---")[0] if message.embeds else ""
80
 
81
  self.edit_title = ui.TextInput(label="Title", default=old_title, style=discord.TextStyle.short)
@@ -87,21 +81,18 @@ class EditAnnounceModal(ui.Modal):
87
  async def on_submit(self, interaction: discord.Interaction):
88
  content = self.edit_body.value
89
  pings = self.new_ping_roles_str
90
-
91
- if pings:
92
- pass
93
 
94
  new_embed = discord.Embed(
95
  title=self.edit_title.value,
96
  description=content,
97
  color=discord.Color.blue()
98
  )
99
-
100
  footer = f"\n\n||{pings}||\n\n---\n**Edited by:** ||{interaction.user.mention}||\n**Time:** <t:{int(time.time())}:f>"
101
  new_embed.description += footer
102
 
103
  await self.message.edit(embed=new_embed)
104
- await interaction.response.send_message(f"✅ Updated announcement {message.id} !", ephemeral=False)
105
 
106
  # --- 4. 机器人主体 ---
107
  class MyBot(discord.Client):
@@ -114,16 +105,10 @@ class MyBot(discord.Client):
114
  print(f"✅ Slash commands synced")
115
 
116
  client = MyBot()
117
- # 如果你使用的是 client.tree,请确保将 db_manager 挂载到 client 上
118
- # 这样在指令内部可以通过 interaction.client.db 访问
119
  client.db = db_manager
120
- # --- 5. 指令定义 ---
121
 
 
122
  @client.tree.command(name="announce", description="Post announcement")
123
- @app_commands.describe(
124
- channel="Target channel",
125
- role_ids="Input Role Pings"
126
- )
127
  async def announce(interaction: discord.Interaction, channel: discord.TextChannel, role_ids: str = None):
128
  if not any(role.id in mod for role in interaction.user.roles):
129
  await interaction.response.send_message("❌ **Access Denied**", ephemeral=True)
@@ -131,145 +116,108 @@ async def announce(interaction: discord.Interaction, channel: discord.TextChanne
131
  await interaction.response.send_modal(AnnounceModal(channel, role_ids))
132
 
133
  @client.tree.command(name="announce_edit", description="Edit announcement")
134
- @app_commands.describe(
135
- channel="The channel",
136
- message_id="The ID of the message",
137
- new_role_ids="New Role Pings to add to the bottom"
138
- )
139
  async def announce_edit(interaction: discord.Interaction, channel: discord.TextChannel, message_id: str, new_role_ids: str = None):
140
  if not any(role.id in mod for role in interaction.user.roles):
141
  await interaction.response.send_message("❌ **Access Denied**", ephemeral=True)
142
  return
143
-
144
  try:
145
  message = await channel.fetch_message(int(message_id))
146
  await interaction.response.send_modal(EditAnnounceModal(message, new_role_ids))
147
  except Exception as e:
148
  await interaction.response.send_message(f"❌ Error: {e}", ephemeral=False)
149
 
150
- @client.tree.command(name="wismer_commands", description="Show all avaliable commands for Wismer Bot")
151
  async def wismer_commands(interaction: discord.Interaction):
152
  await interaction.response.send_message(
153
  '''**Avaliable Commands**:
154
- `/announce`: Post an announcement in selected channel. Only Moderator+ can use it.
155
- `/announce_edit`: Edit an announcement that sent by Wismer Bot. Only Moderator+ can use it.
156
  `/mats`: Show all materials.
157
  `/uploadmat`: Upload a material.''',
158
  ephemeral=False
159
  )
160
 
 
161
 
162
-
163
- ADMIN_LOG_CHANNEL_ID = 1468391869686878455 # Replace with your Admin/Staff channel ID
164
-
165
- # --- 1. Upload Material ---
166
- # --- 1. Upload Material ---
167
- @client.tree.command(name="uploadmat", description="Upload a study link (Requires Approval)")
168
- @app_commands.describe(category="Subject", title="Resource Title", url="Resource Link")
169
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
170
  async def uploadmat(interaction: discord.Interaction, category: str, title: str, url: str):
171
- # 改为 ephemeral=True,因为你的反馈消息是发给个人看的
172
  await interaction.response.defer(ephemeral=True)
173
-
174
  if not url.startswith(("http://", "https://")):
175
- return await interaction.followup.send("❌ Invalid URL! Must start with http or https.", ephemeral=True)
176
-
177
  try:
178
- # 执行逻辑
179
  new_id = await upload_material_logic(client.db, category, title, url, interaction.user.id)
180
-
181
- # 1. Feedback to User (保持和 defer 一致的 ephemeral)
182
- await interaction.followup.send(
183
- f"✅ Submitted! **{category}** ID: `#{new_id}`. Please wait for Moderator approval.",
184
- ephemeral=True
185
- )
186
-
187
- # 2. Alert to Staff Channel (这是发送到频道,不涉及交互反馈)
188
  log_channel = client.get_channel(ADMIN_LOG_CHANNEL_ID)
189
  if log_channel:
190
- embed = discord.Embed(title="🔔 New Material Pending Approval", color=discord.Color.orange())
191
  embed.add_field(name="Subject", value=category, inline=True)
192
  embed.add_field(name="Index ID", value=f"`#{new_id}`", inline=True)
193
- embed.add_field(name="Title", value=title, inline=False)
194
- embed.add_field(name="URL", value=url, inline=False)
195
- embed.add_field(name="Uploader", value=interaction.user.mention, inline=False)
196
- embed.set_footer(text="Use /approvemat to authorize this link.")
197
  await log_channel.send(embed=embed)
198
-
199
  except Exception as e:
200
- print(f"Error in uploadmat: {e}")
201
- # 如果报错了也给用户一个反馈,防止他们因为没反应而狂点
202
- try:
203
- await interaction.followup.send(f"❌ An error occurred: {e}", ephemeral=True)
204
- except:
205
- pass
206
-
207
- # --- 2. Approve Material ---
208
- @client.tree.command(name="approvemat", description="[Mod Only] Approve a pending material")
209
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
210
  async def approvemat(interaction: discord.Interaction, category: str, index_id: str):
211
  await interaction.response.defer(ephemeral=False)
212
- # Changed MOD_ROLES to mod
213
  if not any(role.id in mod for role in interaction.user.roles):
214
- return await interaction.followup.send("❌ Access Denied: Staff Only", ephemeral=True)
215
-
216
  await approve_material_logic(client.db, category, index_id)
217
- await interaction.followup.send(
218
- f"✅ Approved **{category}** `#{index_id}`. It is now visible to everyone.",
219
- ephemeral=False
220
- )
221
 
222
- # --- 3. Delete Material ---
223
- @client.tree.command(name="deletemat", description="[Mod Only] Delete a material")
224
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
225
  async def deletemat(interaction: discord.Interaction, category: str, index_id: str):
226
- # Changed MOD_ROLES to mod
227
  await interaction.response.defer(ephemeral=False)
228
  if not any(role.id in mod for role in interaction.user.roles):
229
- return await interaction.followup.send("❌ Access Denied: Staff Only", ephemeral=True)
230
-
231
  await delete_material_logic(client.db, category, index_id)
232
- await interaction.followup.send(
233
- f"🗑️ Deleted ID `#{index_id}` from **{category}**.",
234
- ephemeral=False
235
- )
236
 
237
- # --- 4. List Approved Materials ---
238
- @client.tree.command(name="mats", description="View approved study materials")
239
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
240
  async def mats(interaction: discord.Interaction, category: str):
241
  await interaction.response.defer(ephemeral=False)
242
  materials = await list_materials_logic(client.db, category, status="approved")
243
  if not materials:
244
- return await interaction.followup.send(f"📭 No approved materials found in **{category}**.", ephemeral=True)
245
-
246
  list_text = "\n".join([f"`#{m['_id']}` [{m['title']}]({m['url']})" for m in materials])
247
- embed = discord.Embed(title=f"📚 {category} Resource Directory", description=list_text, color=discord.Color.blue())
248
  await interaction.followup.send(embed=embed)
249
 
250
- # --- 5. List Pending Materials ---
251
- @client.tree.command(name="unappmats", description="[Mod Only] View pending materials")
252
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
253
  async def unappmats(interaction: discord.Interaction, category: str):
254
- # Changed MOD_ROLES to mod
255
  await interaction.response.defer(ephemeral=False)
256
  if not any(role.id in mod for role in interaction.user.roles):
257
- return await interaction.followup.send("❌ Access Denied: Staff Only", ephemeral=True)
258
-
259
  materials = await list_materials_logic(client.db, category, status="pending")
260
  if not materials:
261
- return await interaction.followup.send(f"✅ No pending items in **{category}**.", ephemeral=True)
262
-
263
  list_text = "\n".join([f"`#{m['_id']}` **{m['title']}**\n🔗 {m['url']}" for m in materials])
264
- embed = discord.Embed(title=f"⏳ {category} Pending List", description=list_text, color=discord.Color.orange())
265
  await interaction.followup.send(embed=embed)
266
 
 
 
 
 
 
 
 
 
 
 
267
 
268
-
269
- # --- 启动 ---
270
  if __name__ == "__main__":
 
271
  t = Thread(target=run_flask)
272
  t.daemon = True
273
  t.start()
274
- token = os.getenv('DISCORD_TOKEN')
275
- client.run(token)
 
 
 
 
 
3
  import time
4
  from fastapi import FastAPI
5
  import uvicorn
6
+ import asyncio
7
  from threading import Thread
8
  from discord import app_commands, ui
9
  # 导入数据库基础类
 
16
  delete_material_logic,
17
  list_materials_logic
18
  )
19
+
20
  # 初始化数据库管理器
 
21
  db_manager = MongoManager(os.getenv("MONGO_URL"), "tonyd")
22
 
23
  # 身份组权限列表
24
  mod = [1357078628969746462, 1357079285113819146, 1436537252317626449, 1357079707539214417, 1357079889160835183]
25
 
26
+ # --- 1. FastAPI 保活 ---
27
  app = FastAPI()
28
  @app.get('/')
29
  def home(): return {"Stauts": "Alive"}
30
 
31
  def run_flask():
32
+ # Hugging Face 默认使用 7860 端口
33
  port = int(os.environ.get('PORT', 7860))
34
  uvicorn.run(app, host='0.0.0.0', port=port)
35
 
36
+ # --- 2. 辅助函数 ---
37
  def format_pings(role_input: str):
38
  pass
39
 
40
  # --- 3. Modal 表单定义 ---
 
41
  class AnnounceModal(ui.Modal):
42
  def __init__(self, channel: discord.TextChannel, ping_roles_str: str = None):
43
  super().__init__(title="New Announcement")
 
48
  announcement_body = ui.TextInput(label="Body", placeholder="Enter message...", style=discord.TextStyle.long)
49
 
50
  async def on_submit(self, interaction: discord.Interaction):
 
51
  content = self.announcement_body.value
52
  pings = self.ping_roles_str
53
+ if pings: pass
 
 
 
54
 
55
  embed = discord.Embed(
56
  title=self.announcement_title.value,
57
  description=content,
58
  color=discord.Color.blue()
59
  )
 
 
60
  footer = f"\n\n||{pings}||\n\n---\n**Sent by:** ||{interaction.user.mention}||\n**Time:** <t:{int(time.time())}:f>"
61
  embed.description += footer
62
 
 
70
  self.new_ping_roles_str = new_ping_roles_str
71
 
72
  old_title = message.embeds[0].title if message.embeds else ""
 
73
  old_body = message.embeds[0].description.split("\n\n---")[0] if message.embeds else ""
74
 
75
  self.edit_title = ui.TextInput(label="Title", default=old_title, style=discord.TextStyle.short)
 
81
  async def on_submit(self, interaction: discord.Interaction):
82
  content = self.edit_body.value
83
  pings = self.new_ping_roles_str
84
+ if pings: pass
 
 
85
 
86
  new_embed = discord.Embed(
87
  title=self.edit_title.value,
88
  description=content,
89
  color=discord.Color.blue()
90
  )
 
91
  footer = f"\n\n||{pings}||\n\n---\n**Edited by:** ||{interaction.user.mention}||\n**Time:** <t:{int(time.time())}:f>"
92
  new_embed.description += footer
93
 
94
  await self.message.edit(embed=new_embed)
95
+ await interaction.response.send_message(f"✅ Updated announcement {self.message.id} !", ephemeral=False)
96
 
97
  # --- 4. 机器人主体 ---
98
  class MyBot(discord.Client):
 
105
  print(f"✅ Slash commands synced")
106
 
107
  client = MyBot()
 
 
108
  client.db = db_manager
 
109
 
110
+ # --- 5. 指令定义 ---
111
  @client.tree.command(name="announce", description="Post announcement")
 
 
 
 
112
  async def announce(interaction: discord.Interaction, channel: discord.TextChannel, role_ids: str = None):
113
  if not any(role.id in mod for role in interaction.user.roles):
114
  await interaction.response.send_message("❌ **Access Denied**", ephemeral=True)
 
116
  await interaction.response.send_modal(AnnounceModal(channel, role_ids))
117
 
118
  @client.tree.command(name="announce_edit", description="Edit announcement")
 
 
 
 
 
119
  async def announce_edit(interaction: discord.Interaction, channel: discord.TextChannel, message_id: str, new_role_ids: str = None):
120
  if not any(role.id in mod for role in interaction.user.roles):
121
  await interaction.response.send_message("❌ **Access Denied**", ephemeral=True)
122
  return
 
123
  try:
124
  message = await channel.fetch_message(int(message_id))
125
  await interaction.response.send_modal(EditAnnounceModal(message, new_role_ids))
126
  except Exception as e:
127
  await interaction.response.send_message(f"❌ Error: {e}", ephemeral=False)
128
 
129
+ @client.tree.command(name="wismer_commands", description="Show all avaliable commands")
130
  async def wismer_commands(interaction: discord.Interaction):
131
  await interaction.response.send_message(
132
  '''**Avaliable Commands**:
133
+ `/announce`: Post an announcement.
134
+ `/announce_edit`: Edit an announcement.
135
  `/mats`: Show all materials.
136
  `/uploadmat`: Upload a material.''',
137
  ephemeral=False
138
  )
139
 
140
+ ADMIN_LOG_CHANNEL_ID = 1468391869686878455
141
 
142
+ @client.tree.command(name="uploadmat", description="Upload a study link")
 
 
 
 
 
 
143
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
144
  async def uploadmat(interaction: discord.Interaction, category: str, title: str, url: str):
 
145
  await interaction.response.defer(ephemeral=True)
 
146
  if not url.startswith(("http://", "https://")):
147
+ return await interaction.followup.send("❌ Invalid URL!", ephemeral=True)
 
148
  try:
 
149
  new_id = await upload_material_logic(client.db, category, title, url, interaction.user.id)
150
+ await interaction.followup.send(f"✅ Submitted! ID: `#{new_id}`.", ephemeral=True)
 
 
 
 
 
 
 
151
  log_channel = client.get_channel(ADMIN_LOG_CHANNEL_ID)
152
  if log_channel:
153
+ embed = discord.Embed(title="🔔 New Material Pending", color=discord.Color.orange())
154
  embed.add_field(name="Subject", value=category, inline=True)
155
  embed.add_field(name="Index ID", value=f"`#{new_id}`", inline=True)
 
 
 
 
156
  await log_channel.send(embed=embed)
 
157
  except Exception as e:
158
+ await interaction.followup.send(f" Error: {e}", ephemeral=True)
159
+
160
+ @client.tree.command(name="approvemat", description="[Mod Only] Approve material")
 
 
 
 
 
 
161
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
162
  async def approvemat(interaction: discord.Interaction, category: str, index_id: str):
163
  await interaction.response.defer(ephemeral=False)
 
164
  if not any(role.id in mod for role in interaction.user.roles):
165
+ return await interaction.followup.send("❌ Access Denied", ephemeral=True)
 
166
  await approve_material_logic(client.db, category, index_id)
167
+ await interaction.followup.send(f"✅ Approved **{category}** `#{index_id}`.")
 
 
 
168
 
169
+ @client.tree.command(name="deletemat", description="[Mod Only] Delete material")
 
170
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
171
  async def deletemat(interaction: discord.Interaction, category: str, index_id: str):
 
172
  await interaction.response.defer(ephemeral=False)
173
  if not any(role.id in mod for role in interaction.user.roles):
174
+ return await interaction.followup.send("❌ Access Denied", ephemeral=True)
 
175
  await delete_material_logic(client.db, category, index_id)
176
+ await interaction.followup.send(f"🗑️ Deleted ID `#{index_id}`.")
 
 
 
177
 
178
+ @client.tree.command(name="mats", description="View approved materials")
 
179
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
180
  async def mats(interaction: discord.Interaction, category: str):
181
  await interaction.response.defer(ephemeral=False)
182
  materials = await list_materials_logic(client.db, category, status="approved")
183
  if not materials:
184
+ return await interaction.followup.send(f"📭 No materials in **{category}**.")
 
185
  list_text = "\n".join([f"`#{m['_id']}` [{m['title']}]({m['url']})" for m in materials])
186
+ embed = discord.Embed(title=f"📚 {category}", description=list_text, color=discord.Color.blue())
187
  await interaction.followup.send(embed=embed)
188
 
189
+ @client.tree.command(name="unappmats", description="[Mod Only] View pending")
 
190
  @app_commands.choices(category=[app_commands.Choice(name=c, value=c) for c in CATEGORIES])
191
  async def unappmats(interaction: discord.Interaction, category: str):
 
192
  await interaction.response.defer(ephemeral=False)
193
  if not any(role.id in mod for role in interaction.user.roles):
194
+ return await interaction.followup.send("❌ Access Denied", ephemeral=True)
 
195
  materials = await list_materials_logic(client.db, category, status="pending")
196
  if not materials:
197
+ return await interaction.followup.send(f"✅ No pending items.")
 
198
  list_text = "\n".join([f"`#{m['_id']}` **{m['title']}**\n🔗 {m['url']}" for m in materials])
199
+ embed = discord.Embed(title=f"⏳ {category} Pending", description=list_text, color=discord.Color.orange())
200
  await interaction.followup.send(embed=embed)
201
 
202
+ # --- 6. 启动逻辑 ---
203
+ async def start_bot():
204
+ token = os.getenv('DISCORD_TOKEN')
205
+ while True:
206
+ try:
207
+ print("正在尝试连接 Discord...")
208
+ await client.start(token)
209
+ except Exception as e:
210
+ print(f"❌ 连接失败: {e},15秒后重试...")
211
+ await asyncio.sleep(15)
212
 
 
 
213
  if __name__ == "__main__":
214
+ # 启动 Web 保活
215
  t = Thread(target=run_flask)
216
  t.daemon = True
217
  t.start()
218
+
219
+ # 启动机器人异步循环
220
+ try:
221
+ asyncio.run(start_bot())
222
+ except KeyboardInterrupt:
223
+ pass