File size: 12,144 Bytes
62543a2
 
 
 
 
 
 
 
b37be24
b3f2694
b37be24
a96c89a
 
 
 
 
b37be24
 
a96c89a
b3f2694
62543a2
 
 
 
 
 
 
 
 
 
 
d23dc37
 
62543a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279fc8c
 
62543a2
c4d8884
 
62543a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d23dc37
b3f2694
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
import gradio as gr
import discord
from discord.ext import commands, tasks
from collections import defaultdict
import json
import os
from dotenv import load_dotenv
import os
from huggingface_hub import HfApi, HfFolder

HfFolder.save_token(os.getenv("HUGGING_FACE_TOKEN"))  # Save the token for authentication
# Calculate the sleep time until the next midnight in IST
ist_timezone = pytz.timezone('Asia/Kolkata')
current_time = datetime.now(ist_timezone)
next_midnight = current_time.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)
sleep_time = (next_midnight - current_time).total_seconds()

api = HfApi()
api.set_space_sleep_time(repo_id="bhagatsuryainatom/Vesperal", sleep_time=sleep_time)

load_dotenv() 

intents = discord.Intents.default()
intents.messages = True
intents.message_content = True
intents.voice_states = True
intents.reactions = True

bot = commands.Bot(command_prefix='!', intents=intents)

# Place your bot's token here
TOKEN = os.getenv('TOKEN')
YOUR_CHANNEL_ID= os.getenv('YOUR_CHANNEL_ID')
# Initialize the sessions and pending_messages dictionaries
sessions = {}
pending_messages = {}

class Session:
    def __init__(self):
        self.camera_required = False
        self.time_without_video = defaultdict(int)
        self.warnings_given = defaultdict(int)
        self.exemptions = set()
        self.instructions_sent = False  # Flag to track if instructions have been sent

    def to_dict(self):
        # Convert sets of Member IDs to lists for JSON serialization
        return {
            "camera_required": self.camera_required,
            "time_without_video": {str(member_id): time for member_id, time in self.time_without_video.items()},
            "warnings_given": {str(member_id): warnings for member_id, warnings in self.warnings_given.items()},
            "exemptions": list(self.exemptions)  # Assuming this already stores member IDs
        }

    @classmethod
    def from_dict(cls, data):
        session = cls()
        session.camera_required = data.get("camera_required", False)
        session.time_without_video = defaultdict(int, data.get("time_without_video", {}))
        session.warnings_given = defaultdict(int, data.get("warnings_given", {}))
        session.exemptions = set(data.get("exemptions", []))
        session.instructions_sent = data.get("instructions_sent", False)
        return session


def save_sessions():
    with open('sessions.json', 'w') as f:
        json.dump({str(vc_id): session.to_dict() for vc_id, session in sessions.items()}, f, ensure_ascii=False, indent=4)
def correct_and_load_json(path):
    try:
        with open(path, 'r') as file:
            return json.load(file)
    except json.JSONDecodeError:
        print("JSON format error detected. Attempting to correct.")
        with open(path, 'r') as file:
            file_content = file.read()
            corrected_content = file_content.replace("'", '"')  # Replace single quotes with double quotes
            # Implement other corrections here as needed
        with open(path, 'w') as file:
            file.write(corrected_content)
        with open(path, 'r') as file:
            return json.load(file)  # Attempt to load corrected content


def load_sessions():
    if os.path.exists('sessions.json') and os.path.getsize('sessions.json') > 0:
        try:
            session_data = correct_and_load_json('sessions.json')
            for vc_id, data in session_data.items():
                sessions[int(vc_id)] = Session.from_dict(data)
        except json.JSONDecodeError:
            print("Failed to correct JSON file. Please check the file format manually.")
    else:
        print("Session file not found or is empty, starting fresh.")
@bot.event
async def on_ready():
    load_sessions()
    print(f'Logged in as {bot.user.name}')
    check_camera_status.start()
    # Ensure save_sessions_task starts properly within the on_ready event
    if not save_sessions_task.is_running():
        save_sessions_task.start()


@bot.command()
async def test(ctx):
    await ctx.send("Test successful!")


@tasks.loop(seconds=30)
async def check_camera_status():
    for vc_id, session in sessions.items():
        if not session.camera_required:
            continue
        vc = bot.get_channel(vc_id)
        if vc is None:
            continue
        for member in vc.members:
            if member.id in session.exemptions:
                continue
            if not member.voice.self_video:
                session.time_without_video[member] += 30
                if session.time_without_video[member] == 30:
                    session.warnings_given[member] = 1
                    await member.send("Warning 1: Please turn on your camera within the next 30 seconds.")
                elif session.time_without_video[member] >= 60:
                    session.warnings_given[member] = 2
                    await member.send("Final Warning: You are being removed for not turning on your camera.")
                    await member.move_to(None)
                    # Reset the member's session data
                    session.time_without_video.pop(member, None)
                    session.warnings_given.pop(member, None)
            else:
                # Reset the member's session data if their camera is on
                session.time_without_video.pop(member, None)
                session.warnings_given.pop(member, None)

@tasks.loop(minutes=5)  # Save sessions every 5 minutes
async def save_sessions_task():
    save_sessions()
    print("Sessions saved.")

@save_sessions_task.before_loop
async def before_save_sessions():
    await bot.wait_until_ready()


@bot.event
async def on_voice_state_update(member, before, after):
    if member.bot:
        return
    
    if after.channel and (not before.channel or before.channel.id != after.channel.id):
        session = sessions.get(after.channel.id)
        if session is None:
            session = Session()
            sessions[after.channel.id] = session

        if any(role.name == "Admin" for role in member.roles):
            other_admins_in_channel = [m for m in after.channel.members if m != member and any(role.name == "Admin" for role in m.roles)]

            if not other_admins_in_channel:
                # Replace "YOUR_CHANNEL_ID" with your actual general channel ID
                general_channel_id = YOUR_CHANNEL_ID
                general_channel = member.guild.get_channel(int(general_channel_id))
                if not general_channel:
                    print(f"Couldn't find the channel with ID: {general_channel_id}.")
                    return

                message_content = f"Admin {member.mention}, do you require cameras to be ON for this session in {after.channel.name}? React with πŸ“· for YES or ❌ for NO."

                try:
                    message = await general_channel.send(message_content)
                    pending_messages[message.id] = after.channel.id

                    await message.add_reaction("πŸ“·")
                    await message.add_reaction("❌")
                except discord.DiscordException as e:
                    print(f"Failed to send message or add reactions due to: {e}")


@bot.command()
@commands.has_permissions(manage_channels=True)
async def setperms(ctx, channel: discord.TextChannel = None):
    channel = channel or ctx.channel
    if not channel.permissions_for(ctx.guild.me).manage_channels:
        await ctx.send("I don't have permission to manage channels here.")
        return
    bot_member = ctx.guild.get_member(bot.user.id)
    overwrites = {
        ctx.guild.default_role: discord.PermissionOverwrite(read_messages=False),
        bot_member: discord.PermissionOverwrite(read_messages=True, send_messages=True, manage_messages=True, add_reactions=True)
    }
    try:
        await channel.set_permissions(ctx.guild.default_role, read_messages=False)
        await channel.set_permissions(bot_member, **overwrites)
        await ctx.send(f"Permissions set for {channel.mention} successfully!")
    except Exception as e:
        await ctx.send(f"Failed to set permissions for {channel.mention} due to: {e}")

@bot.event
async def on_reaction_add(reaction, user):
    await process_reaction(reaction, user)

@bot.event
async def on_reaction_remove(reaction, user):
    await process_reaction(reaction, user)

async def process_reaction(reaction, user):
    if reaction.message.id in pending_messages and not user.bot:
        channel_id = pending_messages[reaction.message.id]
        session = sessions.get(channel_id)

        if session and any(role.name == "Admin" for role in user.roles):
            if str(reaction.emoji) == "πŸ“·":
                session.camera_required = True
                session.exemptions.clear()
            elif str(reaction.emoji) == "❌":
                session.camera_required = False
                session.exemptions.clear()

            # Edit the message to reflect the updated session status
            await reaction.message.edit(content=f"Camera requirement for {reaction.message.channel.guild.get_channel(channel_id).name} has been {'UPDATED to ON' if session.camera_required else 'UPDATED to OFF'}.")

            # Send instructions only if they haven't been sent before
            if not session.instructions_sent:
                follow_up_message = (
                    "**Next Steps for Admins:**\n"
                    "- Use `#camoff @user` to exempt any specific user from the camera requirement.\n"
                    "- Use `#camoff` to exempt yourself if actively participating in the session.\n"
                    "- Use `#end` to conclude the session and reset all settings.\n"
                    "**Note:** These commands work within the context of the current voice session."
                )
                await reaction.message.channel.send(follow_up_message)
                session.instructions_sent = True  # Update the flag to prevent re-sending

            # Cleanup
            del pending_messages[reaction.message.id]
def dummy_function(input_text):
    return "This is a dummy function for the Gradio UI."
iface = gr.Interface(fn=dummy_function, 
                     inputs=gr.Textbox(label="Input"),
                     outputs=gr.Textbox(label="Output"),
                     title="Discord Bot",
                     description="This Space runs a Discord bot in the background.")


@bot.event
async def on_message(message):
    # Skip bot messages and non-admin users
    if message.author.bot or not any(role.name == "Admin" for role in message.author.roles):
        await bot.process_commands(message)  # Ensure other commands are still processed
        return

    # Process commands only if the author is in a voice channel
    if message.author.voice:
        vc_id = message.author.voice.channel.id
        session = sessions.get(vc_id)

        if not session:
            await message.channel.send("No active session found for this voice channel.")
            await bot.process_commands(message)
            return

        # Admin commands
        if "#camoff" in message.content:
            # If no users are mentioned, exempt the author; otherwise, exempt mentioned users
            users_to_exempt = message.mentions if message.mentions else [message.author]
            for user in users_to_exempt:
                session.exemptions.add(user.id)
                await message.channel.send(f"{user.display_name} is now exempt from the camera requirement.")
        elif "#end" in message.content:
            # Clear exemptions and reset camera requirement
            session.exemptions.clear()
            session.camera_required = False
            await message.channel.send("Session ended: Camera requirements and exemptions have been reset.")

    await bot.process_commands(message)  # Ensure this is at the end to process other commands

    # Process other commands
    await bot.process_commands(message)
@check_camera_status.before_loop
async def before_camera_check():
    await bot.wait_until_ready()
bot.run(TOKEN)
iface.launch(inline=False,share=True)