Skip to content

Buttonroles

Bases: Extension

Source code in extentions/buttonroles.py
 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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
class ButtonRoles(Extension):
    def __init__(self, bot: Client):
        self.bot = bot

    role_buttons = SlashCommand(name='rolebuttons', description='Manage role buttons.', default_member_permissions=Permissions.ADMINISTRATOR)

    @role_buttons.subcommand(sub_cmd_name='create', sub_cmd_description="Add role buttons to a message")
    @slash_option(name='roles', description='Roles, seperated by a comma(,)', opt_type=OptionType.STRING, required=True)
    @slash_option(name='message_id', description='Paste in a message ID', opt_type=OptionType.STRING, required=True)
    @slash_option(name='channel', description='Select a channel', opt_type=OptionType.CHANNEL, channel_types=[ChannelType.GUILD_TEXT], required=False)
    @slash_option(name="button_colours", description="Choose what colour the button will be. Default: Blurple", required=False, opt_type=OptionType.INTEGER,
    choices=[SlashCommandChoice(name="Blurple", value=1), SlashCommandChoice(name="Green", value=3), SlashCommandChoice(name="Red", value=4), SlashCommandChoice(name="Gray", value=2)])
    @slash_option(name="mode", description="Choose the mode this set of buttons will be in. Default: Click to get/remove a role", required=False, opt_type=OptionType.INTEGER,
    choices=[SlashCommandChoice(name="Get or remove a role", value=1),SlashCommandChoice(name="Get a role, no removing", value=2),SlashCommandChoice(name="Only one role allowed", value=3)])
    async def role_buttons_add(self, ctx: SlashContext, roles: str, message_id: OptionType.STRING, channel: ChannelType.GUILD_TEXT=None, button_colours: OptionType.INTEGER = 1, mode: OptionType.INTEGER=1):
        """/rolebuttons create
        Description:
            Add role buttons to a message.

        Args:
            roles (str): Role IDs or @roles, sperated by comma `,` | the roles you want to create role buttons from. There can't be more than 25 roles/buttons on one message.
            message_id (OptionType.STRING): Message ID you want the role buttons on, it has to be a message sent by Melody.
            channel (ChannelType.GUILD_TEXT, optional): Channel the message is in. Defaults to channel command is executed in.
            button_colours (OptionType.INTEGER, optional): The colour you want the buttons to be. Defaults to Blurple.
            mode (OptionType.INTEGER, optional): Mode you want buttons to be in. Defaults to 1. Modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message
        """
        await ctx.defer()
        # modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message
        if channel is None:
            channel = ctx.channel
        message: Message = await channel.fetch_message(message_id)
        if message is not None:
            components = []
            if message.components != []:
                for ob in message.components:
                    components = components + ob.components
            documents = []
            messages = []

            if button_colours == 1:
                button_colour = ButtonStyle.BLURPLE
            elif button_colours == 3:
                button_colour = ButtonStyle.GREEN
            elif button_colours == 4:
                button_colour = ButtonStyle.RED
            elif button_colours == 2:
                button_colour = ButtonStyle.GRAY

            raw_roles_list = roles.split(',')
            roles_ids = []
            for r in raw_roles_list:
                r = r.replace('<', '')
                r = r.replace('@', '')
                r = r.replace('!', '')
                r = r.replace('&', '')
                r = r.replace('>', '')
                roles_ids.append(r)
            for role_id in roles_ids:
                role = ctx.guild.get_role(role_id)
                if role is not None:
                    button_id = await button_id_generator(ctx, channel, message)
                    components.append(Button(style=button_colour,label=f"{role.name}",custom_id=button_id))
                    messages.append(f"Button for role {role.mention} was added.\nButton ID: `{button_id}`\nMode: {mode}")
                    documents.append(db.button_roles(guildid=ctx.guild_id, button_id=button_id, channelid=channel.id, msg_id=message.id, roleid=role.id, mode=mode))

            if len(components) > 25:
                return await ctx.send("There can't be more than 25 components on one message")

            rows = spread_to_rows(*components)

            await db.button_roles.insert_many(documents)
            await message.edit(components=rows)
            for m in messages:
                await ctx.send(embed=Embed(color=0xffcc50, description=m))
        else:
            return await ctx.send("Message not found.")

    @role_buttons.subcommand(sub_cmd_name='edit', sub_cmd_description="Edit the behaviour of a role button")
    @message_id()
    @button_id()
    @channel()
    @slash_option(name="mode",description="Choose the mode this set of buttons will be in. Default: Click to get/remove a role",required=False,opt_type=OptionType.INTEGER,
    choices=[SlashCommandChoice(name="Get or remove a role", value=1),SlashCommandChoice(name="Get a role, no removing", value=2),SlashCommandChoice(name="Only one role allowed", value=3)])
    @new_role()
    @slash_option(name="button_colours",description="Choose what colour the button will be. Default: Blurple",required=False,opt_type=OptionType.INTEGER,
    choices=[SlashCommandChoice(name="Blurple", value=1),SlashCommandChoice(name="Green", value=3),SlashCommandChoice(name="Red", value=4),SlashCommandChoice(name="Gray", value=2)])
    @slash_option(name="requirement_role",description="Choose a role. Members will be required to have this role to use the button.",required=False,opt_type=OptionType.ROLE)
    @slash_option(name="ignore_role",description="Choose a role. Members with this role will be ignored.",required=False,opt_type=OptionType.ROLE)
    @slash_option(name='name', description='Give the button a custom name', opt_type=OptionType.STRING, required=False)
    async def role_buttons_edit(self, ctx: InteractionContext, message_id:OptionType.STRING, button_id:OptionType.STRING, channel:OptionType.CHANNEL=None, mode:OptionType.INTEGER=None,
    button_colours:OptionType.INTEGER=None, new_role: OptionType.ROLE = None, requirement_role: OptionType.ROLE = None, ignore_role: OptionType.ROLE = None, name: OptionType.STRING=None):
        """/rolebuttons edit
        Description:
            Edit the behaviour of a role button. `Name`, `Button colour` and `New Role` can't be edited together.

        Args:
            message_id (OptionType.STRING): Message ID the role button is on.
            button_id (OptionType.STRING): Button ID of the button you want to modify.
            channel (OptionType.CHANNEL, optional): Channel the message is in. Defaults to channel command is executed in.
            mode (OptionType.INTEGER, optional): Mode you want buttons to be in. Modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message
            button_colours (OptionType.INTEGER, optional): _description_. The colour you want the buttons to be.
            new_role (OptionType.ROLE, optional): The new role you want on the button.
            requirement_role (OptionType.ROLE, optional): Choose a role. Members will be required to have this role to use the button.
            ignore_role (OptionType.ROLE, optional): Choose a role. Members with this role will be ignored.
            name (OptionType.STRING, optional): Change the button name.
        """
        await ctx.defer()
        if button_colours == 1:
            button_colour = ButtonStyle.BLURPLE
            colour = 'Blurple'
        elif button_colours == 3:
            button_colour = ButtonStyle.GREEN
            colour = 'Green'
        elif button_colours == 4:
            button_colour = ButtonStyle.RED
            colour = 'Red'
        elif button_colours == 2:
            button_colour = ButtonStyle.GRAY
            colour = 'Gray'
        if (mode is None) and (button_colours is None) and (new_role is None) and (requirement_role is None) and (ignore_role is None) and (name is None):
            return await ctx.send('You have to change at least one option to change')
        if channel is None:
            channel = ctx.channel
        edits = ''
        message = await channel.fetch_message(message_id)
        if message is not None:
            if (button_colours is not None) and (new_role is not None) and (name is not None):
                return await ctx.send("`Name`, `Button colour` and `New Role` can't be edited together")
            if button_colours is not None:
                    if message.components != []:
                        components = []
                        for ob in message.components:
                            components.append(ob.components)

                        for l in components:
                            for b in l:
                                if b.custom_id == button_id:
                                    for p in [(i, l.index(b)) for i, l in enumerate(components) if b in l]:
                                        p1, p2 = p

                        message.components[p1].components[p2].style = button_colour
                        await message.edit(components=message.components)    
                        edits = edits + f"New colour: {colour}\n"
            button = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':channel.id, 'msg_id':message.id, 'button_id':button_id})
            if mode is not None:
                button.mode = mode
                await button.save()
                if mode == 1:
                    button_mode = 'Get or remove a role'
                elif mode == 2:
                    button_mode = 'Get a role, no removing'
                elif mode == 3:
                    button_mode = 'Only one role allowed'
                edits = edits + f"New Mode: {button_mode}\n"
            if name is not None:
                if message.components != []:
                    components = []
                    for ob in message.components:
                        components.append(ob.components)

                    for l in components:
                        for b in l:
                            if b.custom_id == button_id:
                                for p in [(i, l.index(b)) for i, l in enumerate(components) if b in l]:
                                    p1, p2 = p
                    message.components[p1].components[p2].label = name
                    await message.edit(components=message.components)
                edits = edits + f"New Name: {name}\n"
            if new_role is not None:
                button.roleid = new_role.id
                await button.save()
                if message.components != []:
                    components = []
                    for ob in message.components:
                        components.append(ob.components)

                    for l in components:
                        for b in l:
                            if b.custom_id == button_id:
                                for p in [(i, l.index(b)) for i, l in enumerate(components) if b in l]:
                                    p1, p2 = p
                    if name is not None:
                        bname = name
                    elif name is None:
                        bname = new_role.name
                    message.components[p1].components[p2].label = bname
                    await message.edit(components=message.components)
                edits = edits + f"New Role: {new_role.mention}\n"
            if requirement_role is not None:
                if button.requirement_roles is not None:
                    button.requirement_roles.append(requirement_role.id)
                    await button.save()
                else:
                    button.requirement_roles = [requirement_role.id]
                    await button.save()
                edits = edits + f"Requirement Role: {requirement_role.mention}\n"
            if ignore_role is not None:
                if button.ignored_roles is not None:
                    button.ignored_roles.append(ignore_role.id)
                    await button.save()
                else:
                    button.ignored_roles = [ignore_role.id]
                    button.save()
                edits = edits + f"Ignore Role: {ignore_role.mention}\n"
            await ctx.send(embed=Embed(color=0xffcc50,description=f"Button `{button_id}` succesfully edited\n{edits}"))
        else:
            await ctx.send('Message not found')

    @role_buttons.subcommand(sub_cmd_name='remove', sub_cmd_description="Remove a role button")
    @button_id()
    @message_id()
    @channel()
    async def role_buttons_remove(self, ctx: InteractionContext, message_id:OptionType.STRING, button_id:OptionType.STRING, channel:OptionType.CHANNEL=None):
        """/rolebuttons remove
        Description:
            Delete a role button from a message.

        Args:
            message_id (OptionType.STRING): Message ID the button is on.
            button_id (OptionType.STRING): The button ID, of the button you want to delete.
            channel (OptionType.CHANNEL, optional): Channel the message is in. Defaults to channel command is executed in.
        """
        await ctx.defer()
        if channel is None:
            channel = ctx.channel
        message: Message = await channel.fetch_message(message_id)
        if message is None:
            return await ctx.send(f"Can't find a message with that id", ephemeral=True)
        components = []
        if message.components != []:
            for ob in message.components:
                components = components + ob.components

        for b in components:
            if b.custom_id == button_id:
                components.remove(b)

        rows = spread_to_rows(*components)

        await message.edit(components=rows)

        button = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':channel.id, 'msg_id':message.id, 'button_id':button_id})
        await button.delete()
        await ctx.send(f"Button `{button_id}` successfully deleted")

    @role_buttons.subcommand(sub_cmd_name='list', sub_cmd_description="List all role buttons on this server")
    async def role_buttons_list(self, ctx: InteractionContext):
        """/rolebuttons list

        Description:
            List all the role buttons, and their info.
        """
        await ctx.defer()
        from interactions.ext.paginators import Paginator
        def chunks(l, n):
            n = max(1, n)
            return (l[i:i+n] for i in range(0, len(l), n))

        def mlis(lst, s, e):
            nc = list(chunks(lst, 10))
            mc = ''
            for testlist in nc[s:e]:
                for m in testlist:
                    mc = mc + m
            return mc

        def newpage(title, buttoninfo):
            embed = Embed(title=title,
            description=buttoninfo,
            color=0xffcc50)
            return embed

        role_buttons = db.button_roles.find({'guildid':ctx.guild_id})

        buttons = []
        async for button in role_buttons:
            role = ctx.guild.get_role(button.roleid)
            if role is None:
                role = 'Role missing'
            elif role is not None:
                role = role.mention
            if button.requirement_roles is not None:
                req_role = ' '.join([ctx.guild.get_role(id).mention for id in button.requirement_roles])
                if req_role == '':
                    reqrole = 'Req. role missing'
            else:
                reqrole = 'No req. role'
            if button.ignored_roles is not None:
                ign_role = ' '.join([ctx.guild.get_role(id).mention for id in button.ignored_roles])
                if ign_role == '':
                    ignrole = 'Ign. role missing'
            else:
                ignrole = 'No ign. role'
            channel = ctx.guild.get_channel(button.channelid)
            if channel is None:
                chnl = 'Channel missing'
            elif channel is not None:
                chnl = channel.mention
            msg = await channel.fetch_message(button.msg_id)
            if msg is None:
                msg = 'Message missing'
            elif msg is not None:
                msg = f'[[Jump to message]]({msg.jump_url})'
            buttons.append(f"**Button ID:** {button.button_id} | **Role:** {role} | **Req. role:** {reqrole} | **Ign. role:** {ignrole} |** Channel:** {chnl} | **Message:** {msg}\n\n")

        if buttons == []:
            await ctx.send('There are no role buttons for this server yet')
            return            

        s = -1
        e = 0
        embedcount = 1
        nc = list(chunks(buttons, 10))

        embeds = []
        while embedcount <= len(nc):
            s = s+1
            e = e+1
            embeds.append(newpage(f'Role buttons for {ctx.guild.name}', mlis(buttons, s, e)))
            embedcount = embedcount+1

        paginator = Paginator(
            client=self.bot, 
            pages=embeds,
            timeout_interval=80,
            show_select_menu=False)
        await paginator.send(ctx)

    @listen()
    async def on_button_press_role_add_mode_1(self, event: ButtonPressed):
        ctx = event.ctx
        user = ctx.author
        rolebutton = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'button_id':ctx.custom_id, 'mode':1})
        if rolebutton is not None:
            role_to_add = ctx.guild.get_role(rolebutton.roleid)
            if role_to_add is not None:
                if not user.has_permission(Permissions.ADMINISTRATOR):
                    if (rolebutton.ignored_users is not None) and (rolebutton.ignored_users != []):
                        if user.id in rolebutton.ignored_users:
                            return await ctx.author.send('You are not allowed to use that role button.')

                    if (rolebutton.ignored_roles is not None) and (rolebutton.ignored_roles != []):
                        ign_roles = [role.name for role in user.roles if role.id in rolebutton.ignored_roles]
                        if ign_roles != []:
                            ign_roles_str = ','.join(ign_roles)
                            return await ctx.author.send(f"I can't give members with **`{ign_roles_str}`** role the **`{role_to_add.name}`** role in `{ctx.guild.name}`", ephemeral=True)

                    if (rolebutton.requirement_roles is not None) and (rolebutton.requirement_roles != []):
                        usr_roles = [role.id for role in user.roles]
                        req_roles = [ctx.guild.get_role(roleid).name for roleid in rolebutton.requirement_roles if roleid not in usr_roles]
                        if req_roles != []:
                            req_roles_str = ','.join(ign_roles)
                            return await ctx.author.send(f"You don't have **`{req_roles_str}`**, which you need to have for me to give you **`{role_to_add.name}`** in `{ctx.guild.name}`", ephemeral=True)           

                if role_to_add not in ctx.author.roles:
                    await ctx.author.add_role(role_to_add)
                    await ctx.author.send(embed=Embed(color=0xffcc50, description=f"I gave you role `{role_to_add.name}` in `{ctx.guild.name}`"), ephemeral=True)

    @listen()
    async def on_button_press_role_add_mode_2(self, event: Component):
        ctx = event.ctx
        user = ctx.author
        rolebutton = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'button_id':ctx.custom_id, 'mode':2})
        if rolebutton is not None:
            role_to_add = ctx.guild.get_role(rolebutton.roleid)
            if role_to_add is not None:
                if not user.has_permission(Permissions.ADMINISTRATOR):
                    if (rolebutton.ignored_users is not None) and (rolebutton.ignored_users != []):
                        if user.id in rolebutton.ignored_users:
                            return await ctx.author.send('You are not allowed to use that role button.')

                    if (rolebutton.ignored_roles is not None) and (rolebutton.ignored_roles != []):
                        ign_roles = [role.name for role in user.roles if role.id in rolebutton.ignored_roles]
                        if ign_roles != []:
                            ign_roles_str = ','.join(ign_roles)
                            return await ctx.author.send(f"I can't give members with **`{ign_roles_str}`** role the **`{role_to_add.name}`** role in `{ctx.guild.name}`", ephemeral=True)

                    if (rolebutton.requirement_roles is not None) and (rolebutton.requirement_roles != []):
                        usr_roles = [role.id for role in user.roles]
                        req_roles = [ctx.guild.get_role(roleid).name for roleid in rolebutton.requirement_roles if roleid not in usr_roles]
                        if req_roles != []:
                            req_roles_str = ','.join(ign_roles)
                            return await ctx.author.send(f"You don't have **`{req_roles_str}`**, which you need to have for me to give you **`{role_to_add.name}`** in `{ctx.guild.name}`", ephemeral=True)

                if role_to_add not in ctx.author.roles:
                    await ctx.author.add_role(role_to_add)
                    await ctx.author.send(embed=Embed(color=0xffcc50, description=f"I gave you role `{role_to_add.name}`"), ephemeral=True)
                elif role_to_add in ctx.author.roles:
                    await ctx.author.send(embed=Embed(color=0xffcc50, description=f"This role `{role_to_add.name}` can't be taken away from you in `{ctx.guild.name}` in `{ctx.guild.name}`"), ephemeral=True)

    @listen()
    async def on_button_press_role_add_mode_3(self, event: Component):
        ctx = event.ctx
        user = ctx.author
        rolebutton = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'button_id':ctx.custom_id, 'mode':3})
        if rolebutton is not None:
            role_to_add = ctx.guild.get_role(rolebutton.roleid)                 
            if role_to_add is not None:
                if not user.has_permission(Permissions.ADMINISTRATOR):
                    if (rolebutton.ignored_users is not None) and (rolebutton.ignored_users != []):
                        if user.id in rolebutton.ignored_users:
                            return await ctx.author.send('You are not allowed to use that role button.')

                    if (rolebutton.ignored_roles is not None) and (rolebutton.ignored_roles != []):
                        ign_roles = [role.name for role in user.roles if role.id in rolebutton.ignored_roles]
                        if ign_roles != []:
                            ign_roles_str = ','.join(ign_roles)
                            return await ctx.author.send(f"I can't give members with **`{ign_roles_str}`** role the **`{role_to_add.name}`** role in `{ctx.guild.name}`", ephemeral=True)

                    if (rolebutton.requirement_roles is not None) and (rolebutton.requirement_roles != []):
                        usr_roles = [role.id for role in user.roles]
                        req_roles = [ctx.guild.get_role(roleid).name for roleid in rolebutton.requirement_roles if roleid not in usr_roles]
                        if req_roles != []:
                            req_roles_str = ','.join(ign_roles)
                            return await ctx.author.send(f"You don't have **`{req_roles_str}`**, which you need to have for me to give you **`{role_to_add.name}`** in `{ctx.guild.name}`", ephemeral=True)

                old_roles = 'and took away '
                buttons = []
                async for b in db.button_roles.find({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'mode':3}):
                    buttons.append(b.roleid)
                if buttons != []:
                    for button_role_id in buttons:
                        if button_role_id != rolebutton.roleid:
                            old_role = ctx.guild.get_role(button_role_id)
                            if old_role is not None:
                                if old_role in ctx.author.roles:
                                    await ctx.author.remove_role(old_role)
                                    old_roles = old_roles + f'`{old_role.name}` '

                if old_roles == 'and took away ':
                    old_roles = ''

                if role_to_add not in ctx.author.roles:
                    await ctx.author.add_role(role_to_add)
                    await ctx.author.send(embed=Embed(color=0xffcc50, description=f"I gave you role `{role_to_add.name}` {old_roles} in `{ctx.guild.name}`"), ephemeral=True)

    @listen()
    async def on_button_press_role_remove_mode_1(self, event: Component):
        ctx = event.ctx
        rolebutton = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'button_id':ctx.custom_id, 'mode':1})
        if rolebutton is not None:
            role_to_remove = ctx.guild.get_role(rolebutton.roleid)
            if role_to_remove is not None:
                if role_to_remove in ctx.author.roles:
                    await ctx.author.remove_role(role_to_remove)
                    await ctx.author.send(embed=Embed(color=0xffcc50, description=f"I took away a role `{role_to_remove.name}` from you in `{ctx.guild.name}`"), ephemeral=True)

    @listen()
    async def on_button_press_role_remove_mode_3(self, event: Component):
        ctx = event.ctx
        rolebutton = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'button_id':ctx.custom_id, 'mode':3})
        if rolebutton is not None:
            old_roles = ''
            buttons = []
            async for b in db.button_roles.find({'guildid':ctx.guild_id, 'channelid':ctx.channel.id, 'msg_id':ctx.message.id, 'mode':3}):
                buttons.append(b.roleid)
            if buttons != []:
                for button_role_id in buttons:
                    if button_role_id != rolebutton.roleid:
                        old_role = ctx.guild.get_role(button_role_id)
                        if old_role is not None:
                            if old_role in ctx.author.roles:
                                await ctx.author.remove_role(old_role)
                                old_roles = old_roles + f'`{old_role.name}` '

            role_to_remove = ctx.guild.get_role(rolebutton.roleid)                 
            if role_to_remove is not None:
                if role_to_remove in ctx.author.roles:
                    await ctx.author.remove_role(role_to_remove)
                    old_roles = old_roles + f'`{role_to_remove.name}` '
                    await ctx.author.send(embed=Embed(color=0xffcc50, description=f"I took away {old_roles} in `{ctx.guild.name}`"), ephemeral=True)

role_buttons_add(ctx, roles, message_id, channel=None, button_colours=1, mode=1) async

/rolebuttons create

Description

Add role buttons to a message.

Parameters:

Name Type Description Default
roles str

Role IDs or @roles, sperated by comma , | the roles you want to create role buttons from. There can't be more than 25 roles/buttons on one message.

required
message_id OptionType.STRING

Message ID you want the role buttons on, it has to be a message sent by Melody.

required
channel ChannelType.GUILD_TEXT

Channel the message is in. Defaults to channel command is executed in.

None
button_colours OptionType.INTEGER

The colour you want the buttons to be. Defaults to Blurple.

1
mode OptionType.INTEGER

Mode you want buttons to be in. Defaults to 1. Modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message

1
Source code in extentions/buttonroles.py
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
@role_buttons.subcommand(sub_cmd_name='create', sub_cmd_description="Add role buttons to a message")
@slash_option(name='roles', description='Roles, seperated by a comma(,)', opt_type=OptionType.STRING, required=True)
@slash_option(name='message_id', description='Paste in a message ID', opt_type=OptionType.STRING, required=True)
@slash_option(name='channel', description='Select a channel', opt_type=OptionType.CHANNEL, channel_types=[ChannelType.GUILD_TEXT], required=False)
@slash_option(name="button_colours", description="Choose what colour the button will be. Default: Blurple", required=False, opt_type=OptionType.INTEGER,
choices=[SlashCommandChoice(name="Blurple", value=1), SlashCommandChoice(name="Green", value=3), SlashCommandChoice(name="Red", value=4), SlashCommandChoice(name="Gray", value=2)])
@slash_option(name="mode", description="Choose the mode this set of buttons will be in. Default: Click to get/remove a role", required=False, opt_type=OptionType.INTEGER,
choices=[SlashCommandChoice(name="Get or remove a role", value=1),SlashCommandChoice(name="Get a role, no removing", value=2),SlashCommandChoice(name="Only one role allowed", value=3)])
async def role_buttons_add(self, ctx: SlashContext, roles: str, message_id: OptionType.STRING, channel: ChannelType.GUILD_TEXT=None, button_colours: OptionType.INTEGER = 1, mode: OptionType.INTEGER=1):
    """/rolebuttons create
    Description:
        Add role buttons to a message.

    Args:
        roles (str): Role IDs or @roles, sperated by comma `,` | the roles you want to create role buttons from. There can't be more than 25 roles/buttons on one message.
        message_id (OptionType.STRING): Message ID you want the role buttons on, it has to be a message sent by Melody.
        channel (ChannelType.GUILD_TEXT, optional): Channel the message is in. Defaults to channel command is executed in.
        button_colours (OptionType.INTEGER, optional): The colour you want the buttons to be. Defaults to Blurple.
        mode (OptionType.INTEGER, optional): Mode you want buttons to be in. Defaults to 1. Modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message
    """
    await ctx.defer()
    # modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message
    if channel is None:
        channel = ctx.channel
    message: Message = await channel.fetch_message(message_id)
    if message is not None:
        components = []
        if message.components != []:
            for ob in message.components:
                components = components + ob.components
        documents = []
        messages = []

        if button_colours == 1:
            button_colour = ButtonStyle.BLURPLE
        elif button_colours == 3:
            button_colour = ButtonStyle.GREEN
        elif button_colours == 4:
            button_colour = ButtonStyle.RED
        elif button_colours == 2:
            button_colour = ButtonStyle.GRAY

        raw_roles_list = roles.split(',')
        roles_ids = []
        for r in raw_roles_list:
            r = r.replace('<', '')
            r = r.replace('@', '')
            r = r.replace('!', '')
            r = r.replace('&', '')
            r = r.replace('>', '')
            roles_ids.append(r)
        for role_id in roles_ids:
            role = ctx.guild.get_role(role_id)
            if role is not None:
                button_id = await button_id_generator(ctx, channel, message)
                components.append(Button(style=button_colour,label=f"{role.name}",custom_id=button_id))
                messages.append(f"Button for role {role.mention} was added.\nButton ID: `{button_id}`\nMode: {mode}")
                documents.append(db.button_roles(guildid=ctx.guild_id, button_id=button_id, channelid=channel.id, msg_id=message.id, roleid=role.id, mode=mode))

        if len(components) > 25:
            return await ctx.send("There can't be more than 25 components on one message")

        rows = spread_to_rows(*components)

        await db.button_roles.insert_many(documents)
        await message.edit(components=rows)
        for m in messages:
            await ctx.send(embed=Embed(color=0xffcc50, description=m))
    else:
        return await ctx.send("Message not found.")

role_buttons_edit(ctx, message_id, button_id, channel=None, mode=None, button_colours=None, new_role=None, requirement_role=None, ignore_role=None, name=None) async

/rolebuttons edit

Description

Edit the behaviour of a role button. Name, Button colour and New Role can't be edited together.

Parameters:

Name Type Description Default
message_id OptionType.STRING

Message ID the role button is on.

required
button_id OptionType.STRING

Button ID of the button you want to modify.

required
channel OptionType.CHANNEL

Channel the message is in. Defaults to channel command is executed in.

None
mode OptionType.INTEGER

Mode you want buttons to be in. Modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message

None
button_colours OptionType.INTEGER

description. The colour you want the buttons to be.

None
new_role OptionType.ROLE

The new role you want on the button.

None
requirement_role OptionType.ROLE

Choose a role. Members will be required to have this role to use the button.

None
ignore_role OptionType.ROLE

Choose a role. Members with this role will be ignored.

None
name OptionType.STRING

Change the button name.

None
Source code in extentions/buttonroles.py
 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
@role_buttons.subcommand(sub_cmd_name='edit', sub_cmd_description="Edit the behaviour of a role button")
@message_id()
@button_id()
@channel()
@slash_option(name="mode",description="Choose the mode this set of buttons will be in. Default: Click to get/remove a role",required=False,opt_type=OptionType.INTEGER,
choices=[SlashCommandChoice(name="Get or remove a role", value=1),SlashCommandChoice(name="Get a role, no removing", value=2),SlashCommandChoice(name="Only one role allowed", value=3)])
@new_role()
@slash_option(name="button_colours",description="Choose what colour the button will be. Default: Blurple",required=False,opt_type=OptionType.INTEGER,
choices=[SlashCommandChoice(name="Blurple", value=1),SlashCommandChoice(name="Green", value=3),SlashCommandChoice(name="Red", value=4),SlashCommandChoice(name="Gray", value=2)])
@slash_option(name="requirement_role",description="Choose a role. Members will be required to have this role to use the button.",required=False,opt_type=OptionType.ROLE)
@slash_option(name="ignore_role",description="Choose a role. Members with this role will be ignored.",required=False,opt_type=OptionType.ROLE)
@slash_option(name='name', description='Give the button a custom name', opt_type=OptionType.STRING, required=False)
async def role_buttons_edit(self, ctx: InteractionContext, message_id:OptionType.STRING, button_id:OptionType.STRING, channel:OptionType.CHANNEL=None, mode:OptionType.INTEGER=None,
button_colours:OptionType.INTEGER=None, new_role: OptionType.ROLE = None, requirement_role: OptionType.ROLE = None, ignore_role: OptionType.ROLE = None, name: OptionType.STRING=None):
    """/rolebuttons edit
    Description:
        Edit the behaviour of a role button. `Name`, `Button colour` and `New Role` can't be edited together.

    Args:
        message_id (OptionType.STRING): Message ID the role button is on.
        button_id (OptionType.STRING): Button ID of the button you want to modify.
        channel (OptionType.CHANNEL, optional): Channel the message is in. Defaults to channel command is executed in.
        mode (OptionType.INTEGER, optional): Mode you want buttons to be in. Modes: 1 = get or remove the role, 2 = get a role - not possible to remove the role, 3 = only one role allowed from the message
        button_colours (OptionType.INTEGER, optional): _description_. The colour you want the buttons to be.
        new_role (OptionType.ROLE, optional): The new role you want on the button.
        requirement_role (OptionType.ROLE, optional): Choose a role. Members will be required to have this role to use the button.
        ignore_role (OptionType.ROLE, optional): Choose a role. Members with this role will be ignored.
        name (OptionType.STRING, optional): Change the button name.
    """
    await ctx.defer()
    if button_colours == 1:
        button_colour = ButtonStyle.BLURPLE
        colour = 'Blurple'
    elif button_colours == 3:
        button_colour = ButtonStyle.GREEN
        colour = 'Green'
    elif button_colours == 4:
        button_colour = ButtonStyle.RED
        colour = 'Red'
    elif button_colours == 2:
        button_colour = ButtonStyle.GRAY
        colour = 'Gray'
    if (mode is None) and (button_colours is None) and (new_role is None) and (requirement_role is None) and (ignore_role is None) and (name is None):
        return await ctx.send('You have to change at least one option to change')
    if channel is None:
        channel = ctx.channel
    edits = ''
    message = await channel.fetch_message(message_id)
    if message is not None:
        if (button_colours is not None) and (new_role is not None) and (name is not None):
            return await ctx.send("`Name`, `Button colour` and `New Role` can't be edited together")
        if button_colours is not None:
                if message.components != []:
                    components = []
                    for ob in message.components:
                        components.append(ob.components)

                    for l in components:
                        for b in l:
                            if b.custom_id == button_id:
                                for p in [(i, l.index(b)) for i, l in enumerate(components) if b in l]:
                                    p1, p2 = p

                    message.components[p1].components[p2].style = button_colour
                    await message.edit(components=message.components)    
                    edits = edits + f"New colour: {colour}\n"
        button = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':channel.id, 'msg_id':message.id, 'button_id':button_id})
        if mode is not None:
            button.mode = mode
            await button.save()
            if mode == 1:
                button_mode = 'Get or remove a role'
            elif mode == 2:
                button_mode = 'Get a role, no removing'
            elif mode == 3:
                button_mode = 'Only one role allowed'
            edits = edits + f"New Mode: {button_mode}\n"
        if name is not None:
            if message.components != []:
                components = []
                for ob in message.components:
                    components.append(ob.components)

                for l in components:
                    for b in l:
                        if b.custom_id == button_id:
                            for p in [(i, l.index(b)) for i, l in enumerate(components) if b in l]:
                                p1, p2 = p
                message.components[p1].components[p2].label = name
                await message.edit(components=message.components)
            edits = edits + f"New Name: {name}\n"
        if new_role is not None:
            button.roleid = new_role.id
            await button.save()
            if message.components != []:
                components = []
                for ob in message.components:
                    components.append(ob.components)

                for l in components:
                    for b in l:
                        if b.custom_id == button_id:
                            for p in [(i, l.index(b)) for i, l in enumerate(components) if b in l]:
                                p1, p2 = p
                if name is not None:
                    bname = name
                elif name is None:
                    bname = new_role.name
                message.components[p1].components[p2].label = bname
                await message.edit(components=message.components)
            edits = edits + f"New Role: {new_role.mention}\n"
        if requirement_role is not None:
            if button.requirement_roles is not None:
                button.requirement_roles.append(requirement_role.id)
                await button.save()
            else:
                button.requirement_roles = [requirement_role.id]
                await button.save()
            edits = edits + f"Requirement Role: {requirement_role.mention}\n"
        if ignore_role is not None:
            if button.ignored_roles is not None:
                button.ignored_roles.append(ignore_role.id)
                await button.save()
            else:
                button.ignored_roles = [ignore_role.id]
                button.save()
            edits = edits + f"Ignore Role: {ignore_role.mention}\n"
        await ctx.send(embed=Embed(color=0xffcc50,description=f"Button `{button_id}` succesfully edited\n{edits}"))
    else:
        await ctx.send('Message not found')

role_buttons_remove(ctx, message_id, button_id, channel=None) async

/rolebuttons remove

Description

Delete a role button from a message.

Parameters:

Name Type Description Default
message_id OptionType.STRING

Message ID the button is on.

required
button_id OptionType.STRING

The button ID, of the button you want to delete.

required
channel OptionType.CHANNEL

Channel the message is in. Defaults to channel command is executed in.

None
Source code in extentions/buttonroles.py
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
@role_buttons.subcommand(sub_cmd_name='remove', sub_cmd_description="Remove a role button")
@button_id()
@message_id()
@channel()
async def role_buttons_remove(self, ctx: InteractionContext, message_id:OptionType.STRING, button_id:OptionType.STRING, channel:OptionType.CHANNEL=None):
    """/rolebuttons remove
    Description:
        Delete a role button from a message.

    Args:
        message_id (OptionType.STRING): Message ID the button is on.
        button_id (OptionType.STRING): The button ID, of the button you want to delete.
        channel (OptionType.CHANNEL, optional): Channel the message is in. Defaults to channel command is executed in.
    """
    await ctx.defer()
    if channel is None:
        channel = ctx.channel
    message: Message = await channel.fetch_message(message_id)
    if message is None:
        return await ctx.send(f"Can't find a message with that id", ephemeral=True)
    components = []
    if message.components != []:
        for ob in message.components:
            components = components + ob.components

    for b in components:
        if b.custom_id == button_id:
            components.remove(b)

    rows = spread_to_rows(*components)

    await message.edit(components=rows)

    button = await db.button_roles.find_one({'guildid':ctx.guild_id, 'channelid':channel.id, 'msg_id':message.id, 'button_id':button_id})
    await button.delete()
    await ctx.send(f"Button `{button_id}` successfully deleted")

role_buttons_list(ctx) async

/rolebuttons list

Description

List all the role buttons, and their info.

Source code in extentions/buttonroles.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
@role_buttons.subcommand(sub_cmd_name='list', sub_cmd_description="List all role buttons on this server")
async def role_buttons_list(self, ctx: InteractionContext):
    """/rolebuttons list

    Description:
        List all the role buttons, and their info.
    """
    await ctx.defer()
    from interactions.ext.paginators import Paginator
    def chunks(l, n):
        n = max(1, n)
        return (l[i:i+n] for i in range(0, len(l), n))

    def mlis(lst, s, e):
        nc = list(chunks(lst, 10))
        mc = ''
        for testlist in nc[s:e]:
            for m in testlist:
                mc = mc + m
        return mc

    def newpage(title, buttoninfo):
        embed = Embed(title=title,
        description=buttoninfo,
        color=0xffcc50)
        return embed

    role_buttons = db.button_roles.find({'guildid':ctx.guild_id})

    buttons = []
    async for button in role_buttons:
        role = ctx.guild.get_role(button.roleid)
        if role is None:
            role = 'Role missing'
        elif role is not None:
            role = role.mention
        if button.requirement_roles is not None:
            req_role = ' '.join([ctx.guild.get_role(id).mention for id in button.requirement_roles])
            if req_role == '':
                reqrole = 'Req. role missing'
        else:
            reqrole = 'No req. role'
        if button.ignored_roles is not None:
            ign_role = ' '.join([ctx.guild.get_role(id).mention for id in button.ignored_roles])
            if ign_role == '':
                ignrole = 'Ign. role missing'
        else:
            ignrole = 'No ign. role'
        channel = ctx.guild.get_channel(button.channelid)
        if channel is None:
            chnl = 'Channel missing'
        elif channel is not None:
            chnl = channel.mention
        msg = await channel.fetch_message(button.msg_id)
        if msg is None:
            msg = 'Message missing'
        elif msg is not None:
            msg = f'[[Jump to message]]({msg.jump_url})'
        buttons.append(f"**Button ID:** {button.button_id} | **Role:** {role} | **Req. role:** {reqrole} | **Ign. role:** {ignrole} |** Channel:** {chnl} | **Message:** {msg}\n\n")

    if buttons == []:
        await ctx.send('There are no role buttons for this server yet')
        return            

    s = -1
    e = 0
    embedcount = 1
    nc = list(chunks(buttons, 10))

    embeds = []
    while embedcount <= len(nc):
        s = s+1
        e = e+1
        embeds.append(newpage(f'Role buttons for {ctx.guild.name}', mlis(buttons, s, e)))
        embedcount = embedcount+1

    paginator = Paginator(
        client=self.bot, 
        pages=embeds,
        timeout_interval=80,
        show_select_menu=False)
    await paginator.send(ctx)