Create a Discord channel upon receiving DMs - python

So I'm working on a bot, and I'd like it to have it's own ticket support system.
What I'd like it to do is that, upon receiving a DM it:
- Creates a new channel named after the DM author (such as #David0001) in a "ticket" category
- Sets up permissions for that channel to be only accessible to read and write by the DM author (and admins ofc)
- Restranscribes whatever the DM author wrote in his DM
I'm currently using the latest Async discord.py branch (I know I should probably be using rewrite but oh well)
#client.event
async def on_message(message):
if message.server is None and message.author != client.user:
server = client.get_server("serverid")
for channel in server.channels:
if channel.name == str(message.author):
await client.send_message(message.author, "Hey you already have a support ticket open!")
break
else:
await client.create_channel(server, str(message.author), type=discord.ChannelType.text)
overwrite = discord.PermissionOverwrite()
overwrite.read_messages = True
overwrite.send_messages = True
overwrite.ban_members = False
for channel in server.channels:
if channel.name == str(message.author):
await client.edit_channel_permissions(channel.id, message.author, overwrite)
await client.send_message(channel.id, message.content)
break
else:
break
break
await client.process_commands(message)
I'd also like it to verify first if a support channel with the users name doesn't already exist and if so to send a little message like "hey you already have a support ticket channel open"
This code seems to work at first but not well, it does create a "david0001" channel upon DM, however it doesn't set up the permissions properly, doesn't set it up in a pre-existing ticket catgeory (cause I don't know how to do that), it does not retranscribe whatever the user wrote in the DMs and it does not verify if the user has a open channel, it just keeps making a new one

There is multiple solutions that can be proposed for your situation :
As the old async branch is depreciated, here is the up to date code.
Through a command :
I assume you're using a cog system :
This is a command designed for your request, when the user uses it, it inspect the support server, if there is no Support category, it creates one, and creates a channel for the user. If the requirement already exist it just send the message stored in request into the correct channel.
import asyncio, discord
from discord.ext import commands
from discord.utils import get
class Cmd_support(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
async def support(self, ctx, *, request: str):
user = ctx.message.author
guild_id = 000000000000000000 # fill it up with your support guild id
support_server = self.client.get_guild(guild_id)
# try to match with a channel name
match = False
for channel in support_server.text_channels:
await asyncio.sleep(0)
if channel.name == user.name.lower(): # it's a match
match = True
user_support = get(support_server.text_channels, name = user.name.lower()) # get the user support channel as lower case, just like channels name
break
# end for
if not match: # if the channel doesn't exist, we create it
support_category_name = 'Support' # defines the Support category name, case sensitive
support_category = get(support_server.categories, name = support_category_name) # get the support category
user_support = get(support_server.text_channels, name = user.name.lower()) # get the user support channel as lower case, just like channels name
if support_category == None: # if the category is not found, we create it
# setting up the category permissions
support_category_permissions = {
support_server.default_role : discord.PermissionOverwrite(send_messages = False)
}
await support_server.create_category(name = support_category_name, overwrites = support_category_permissions)
support_category = get(support_server.categories, name = support_category_name) # redefine the variable with the new category
if user_support == None: # if the channel doesn't exist
# setting up the permissions for the channel
user_channel_permissions = {
support_server.default_role : discord.PermissionOverwrite(read_messages = False, send_messages = False), # othe users cannot see the channels
support_server.me : discord.PermissionOverwrite(read_messages = True, send_messages = True),
user : discord.PermissionOverwrite(read_messages = True, send_messages = True)
}
# now we create the channel
await support_server.create_text_channel(name = user.name, overwrites = user_channel_permissions, category = support_category)
user_support = get(support_server.text_channels, name = user.name.lower()) # redefine the support channel
# now send the message to the new channel
await user_support.send(request) # sends what the user sent to the command
def setup(client):
client.add_cog(Cmd_support(client))
Using DMs :
It's slightly different from the command solution, request is just replaced by message.content and we add the condition :
Please note that it will create a channel and send message into it no matter the message content. You can filter it by using a condition like : if message.content.startswith("something"):
if type(message.channel) == discord.DMchannel:
Here's the code :
#commands.Cog.listener() # equivalent to discord.Event
async def on_message(self, message):
if type(message.channel) == discord.DMChannel:
user = message.author
guild_id = 000000000000000000 # fill it up with your support guild id
support_server = self.client.get_guild(guild_id)
# try to match with a channel name
match = False
for channel in support_server.text_channels:
await asyncio.sleep(0)
if channel.name == user.name.lower(): # it's a match
match = True
user_support = get(support_server.text_channels, name = user.name.lower()) # get the user support channel as lower case, just like channels name
break
# end for
if not match: # if the channel doesn't exist, we create it
support_category_name = 'Support' # defines the Support category name, case sensitive
support_category = get(support_server.categories, name = support_category_name) # get the support category
user_support = get(support_server.text_channels, name = user.name.lower()) # get the user support channel as lower case, just like channels name
if support_category == None: # if the category is not found, we create it
# setting up the category permissions
support_category_permissions = {
support_server.default_role : discord.PermissionOverwrite(send_messages = False)
}
await support_server.create_category(name = support_category_name, overwrites = support_category_permissions)
support_category = get(support_server.categories, name = support_category_name) # redefine the variable with the new category
if user_support == None: # if the channel doesn't exist
# setting up the permissions for the channel
user_channel_permissions = {
support_server.default_role : discord.PermissionOverwrite(read_messages = False, send_messages = False), # othe users cannot see the channels
support_server.me : discord.PermissionOverwrite(read_messages = True, send_messages = True),
user : discord.PermissionOverwrite(read_messages = True, send_messages = True)
}
# now we create the channel
await support_server.create_text_channel(name = user.name, overwrites = user_channel_permissions, category = support_category)
user_support = get(support_server.text_channels, name = user.name.lower()) # redefine the support channel
# now send the message to the new channel
await user_support.send(message.content) # sends what the user sent to the command
def setup(client):
client.add_cog(Event_on_message(client))
As you can see it's not that different from the first solution I've proposed.
Get the latest discord.py
Well, you should really update your discord.py version to the latest, the old async branch doesn't exist anymore and has been replaced by the rewrite version they were working on.
Try this little script in your terminal :
>>> import discord
>>> discord.__version__
'1.2.3'
If your version is like 0.X.X you're not up to date !
Think about pip install discord.py --upgrade to get the latest version (it's so much better than the old async one)
Usefull link : discord.py - Migrating to v1.0 (I've migrated to the new version in few hours, it wasn't too long. You should better migrate now, before your code becomes too big)
Hope it helped !
Have a nice day !

Related

How can I find different parts of the same mongodb entry in discord.py

I am currently working on a system that deletes a channel when the user leaves. This system works by, when a user asks for it, a command is used to create the channel and the member id and channel id are stored in mongodb, as shown in the picture below:
My current code for this is:
#commands.Cog.listener()
async def on_member_remove(self, member):
channelfind = cluster["Channels"]["info"]
if member.guild.id == testid:
joinedat = diskord.utils.utcnow() - member.joined_at
time = humanize.precisedelta(joinedat, format="%0.0f")
embed = diskord.Embed(title="\u200b", color=0xfc8eac)
embed: Embed = diskord.Embed(
description= f'**{member.mention} left the server**\n:timer: **Joined:**\n{time} ago',
color=0xfc8eac
)
embed.set_author(name=member, icon_url=member.avatar.url)
embed.set_thumbnail(url=member.avatar.url)
embed.timestamp = datetime.datetime.utcnow()
embed.set_footer(text=f'ID: {member.id} \u200b ')
memberid = channelfind.find_one({"member_id": member.id})
if memberid is not None:
log = testlog
await self.bot.get_channel(log).send(embed=embed)
else:
pass
However, I am not sure how I can find the channel_id that is with the member_id of the user who has left
Any help would be appreciated thank you!
.find_one() returns a dictionary representing the document. So, you can do
memberid = channelfind.find_one({"member_id": member.id})
print(memberid['channel_id'])
to get the channel_id.

Delete channel on reaction // Ticket Bot discord.py

Im trying to make a ticket bot, and this is currently what i have. Now that i can make the bot create a channel after detecting a reaction, i need to be able to make it close the ticket on another reaction, as well as other features iw ant to add. But this reaction is in the channel created so i dont know how to get that channel id created. Someone suggested me to use cogs, but have no idea where to start.
async def on_raw_reaction_add(payload):
channel = payload.channel_id
if channel == 862711339298455563:
guildid = payload.guild_id
guild = client.get_guild(guildid)
channel = guild.get_channel(payload.channel_id)
message = channel.get_partial_message(payload.message_id)
emoji = '🎟️'
member = payload.member
payloaduserid = payload.user_id
clientuserid = client.user.id
if payloaduserid != clientuserid:
await message.remove_reaction(emoji, member)
category = client.get_channel(861003967000215583)
channel1 = await guild.create_text_channel(f'ticket {payload.user_id}', category = category )
ticketembed = discord.Embed(
title = f'Ticket - {payload.user_id}',
description = '- If you want to tag the admins please react with :telephone: \n - To report this message please react with :warning: \n - To close the ticket react this mesaage with :x: ',
color = discord.Color.red())
ticketembed.set_footer(text = 'All of the conversation will be held in the archives, eventhought a moderator can delete a message in this channel, a copy of it will be held in a location where only the owner can access.')
user = client.get_user(payload.user_id)
await channel1.set_permissions(target=user, read_messages=True , send_messages=True)
ticketembed1 = await channel1.send(embed= ticketembed)
await ticketembed1.add_reaction('☎️')
await ticketembed1.add_reaction('⚠️')
await ticketembed1.add_reaction('❌')
await channel1.send(f'{user.mention}', delete_after = 0.1) ```
One method is checking the channel name format. sometimes getting the channel from cache is not an option so we might need to fetch it.
async def on_raw_reaction_add(payload):
channel = client.get_channel(payload.channel_id)
# if channel is not in cache then fetch is using a API request
if not channel:
channel = await client.fetch_channel(channel_id)
# The user is the owner of the ticket since it is his id
if channel.name == f"ticket {payload.user_id}":
emoji = payload.emoji.name
# delete
if emoji == "❌":
await channel.delete()
It is better to save channel id and then use it since channel name can be changed. You can get the id like this.
channel1 = await guild.create_text_channel(f'ticket {payload.user_id}', category = category )
print(channel1.id)

How to prevent bot from spamming? | Twitch.py | Discord.py

I'm new to python and i'm making discord bot. So here i have twitch notification function, but when someone is live bot just starts spamming, i think because idk how to get content out of an embed. please help me. the code:
import os
import json
import discord
import requests
from discord.ext import tasks, commands
from discord.utils import get
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='$', intents=intents)
TOKEN = os.getenv('token')
# Authentication with Twitch API.
client_id = os.getenv('client_id')
client_secret = os.getenv('Dweller_token')
body = {
'client_id': client_id,
'client_secret': client_secret,
"grant_type": 'client_credentials'
}
r = requests.post('https://id.twitch.tv/oauth2/token', body)
keys = r.json()
headers = {
'Client-ID': client_id,
'Authorization': 'Bearer ' + keys['access_token']
}
'''user_info = twitch.get_users(logins=['turb4ik'])
user_id = user_info['data'][0]['id']
print(user_info)'''
# Returns true if online, false if not.
def checkuser(streamer_name):
stream = requests.get('https://api.twitch.tv/helix/streams?user_login=' + streamer_name, headers=headers)
stream_data = stream.json()
if len(stream_data['data']) == 1:
return True, stream_data
else:
return False, stream_data
# Executes when bot is started
#bot.event
async def on_ready():
# Defines a loop that will run every 10 seconds (checks for live users every 10 seconds).
#tasks.loop(seconds=10)
async def live_notifs_loop():
# username = stream_data['data'][0]['user_name']
# stream_title = stream_data['data'][0]['title']
# game_being_played = stream_data['data'][0]['game_name']
# Opens and reads the json file
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Makes sure the json isn't empty before continuing.
if streamers is not None:
# Gets the guild, 'twitch streams' channel, and streaming role.
guild = bot.get_guild(690995360411156531)
channel = bot.get_channel(798127930295058442)
role = get(guild.roles, id=835581408272580649)
# Loops through the json and gets the key,value which in this case is the user_id and twitch_name of
# every item in the json.
for user_id, twitch_name in streamers.items():
print("checking" + " " + str(twitch_name))
# Takes the given twitch_name and checks it using the checkuser function to see if they're live.
# Returns either true or false.
status, stream_data = checkuser(twitch_name)
# Gets the user using the collected user_id in the json
user = bot.get_user(int(user_id))
# Makes sure they're live
if status is True:
# Checks to see if the live message has already been sent.
async for message in channel.history(limit=200):
print("yes")
twitch_embed = discord.Embed(
title=f":red_circle: **LIVE**\n{user.name} is now streaming on Twitch! \n \n {stream_data['data'][0]['title']}",
color=0xac1efb,
url=f'\nhttps://www.twitch.tv/{twitch_name}'
)
twitch_embed.add_field(
name = '**Game**',
value = stream_data['data'][0]['game_name'],
inline = True
)
twitch_embed.add_field(
name = '**Viewers**',
value = stream_data['data'][0]['viewer_count'],
inline = True
)
twitch_embed.set_author(
name = str(twitch_name),
icon_url = stream_data['data'][0]['thumbnail_url']
)
twitch_embed.set_image(url = f'https://www.twitch.tv/{twitch_name}')
print("yes2")
try:
embed_title = twitch_embed.title
embed_description = twitch_embed.description
except Exception as e:
break
print("yes3")
# If it has, break the loop (do nothing).
if embed_title == True:
break
# If it hasn't, assign them the streaming role and send the message.
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and
# they're live, give them the streaming role.
if member.id == int(user_id):
await member.add_roles(role)
# Sends the live notification to the 'twitch streams' channel then breaks the loop.
await channel.send(
content = f"Hey #everyone! {user.name} is now streaming on Twitch! Go check it out: https://www.twitch.tv/{twitch_name}", embed=twitch_embed)
print(f"{user} started streaming. Sending a notification.")
break
# If they aren't live do this:
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and they're not
# live, remove the streaming role.
if member.id == int(user_id):
await member.remove_roles(role)
# Checks to see if the live notification was sent.
async for message in channel.history(limit=200):
try:
embed_title = message.embeds[0].title
embed_description = message.embeds[0].description
except Exception as e:
break
# If it was, delete it.
if user.mention in embed_title and "is now playing" in embed_title:
print(f"{user} stopped streaming. Removing the notification.")
await message.delete()
# Start your loop.
live_notifs_loop.start()
# Command to add Twitch usernames to the json.
#bot.command(name='addtwitch', help='Adds your Twitch to the live notifs.', pass_context=True)
async def add_twitch(ctx, twitch_name):
# Opens and reads the json file.
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Gets the users id that called the command.
user_id = ctx.author.id
# Assigns their given twitch_name to their discord id and adds it to the streamers.json.
streamers[user_id] = twitch_name
# Adds the changes we made to the json file.
with open('streamers.json', 'w') as file:
file.write(json.dumps(streamers))
# Tells the user it worked.
await ctx.send(f"Added {twitch_name} for {ctx.author} to the notifications list.")
print('Server Running')
bot.run(TOKEN)
Just for others context, he had shown me the error on discord, so I am answering him through this question!
So mate, I found the error.
The line 106 in your code is:
await message.channel.send(msg)
Now this msg variable send all the details but we just want the content of it and not anything else.
So change that to:
await message.channel.send(msg.content)
Thank You! :D

get only information of a specific guild

Hey so this code works completely fine but my problem is that it gets info of all the servers that the bot is in.
#commands.Cog.listener()
async def on_user_update(self, before, after):
logs = self.bot.get_channel(810977833823240262)
embed = discord.Embed(colour=0x7289da)
embed.description = f"{after.name} has changed his avatar"
if before.avatar_url != after.avatar_url:
embed.add_field(name="New avatar")
embed.set_image(url=after.avatar_url)
if before.name != after.name:
embed.add_field(name="Previous name",value=before.name,inline=False)
embed.add_field(name="New name ",value=after.name,inline=False)
if before.status != after.status:
embed.add_field(name="Previous Status",value=before.status,inline=False)
embed.add_field(name="New Status ",value=after.status,inline=False)
await logs.send(embed=embed)
This code is for logs so I want it to have different logs for each server. For example, I don't want to show a server that I'm not in if I changed avatar or anything.
Any help is appreciated
You can simply check if you have any mutual guilds with the user that updated it's info
#commands.Cog.listener()
async def on_user_update(self, before, after):
my_id = YOUR_ID_HERE # Obviously put your ID here
mutual_guilds = [g for g in self.bot.guilds if g.get_member(my_id) and g.get_member(after.id)]
if mutual_guilds: # Checking if the list is not empty
# The user is in one of your guilds
logs = self.bot.get_channel(810977833823240262)
embed = discord.Embed(colour=0x7289da)
embed.description = f"{after.name} has changed his avatar"
if before.avatar_url != after.avatar_url:
embed.add_field(name="New avatar")
embed.set_image(url=after.avatar_url)
if before.name != after.name:
embed.add_field(name="Previous name",value=before.name,inline=False)
embed.add_field(name="New name ",value=after.name,inline=False)
if before.status != after.status:
embed.add_field(name="Previous Status",value=before.status,inline=False)
embed.add_field(name="New Status ",value=after.status,inline=False)
await logs.send(embed=embed)
To explain a bit more the list comprehension:
mutual_guilds = []
for g in self.bot.guilds: # Looping though every guild
if g.get_member(my_id) and g.get_member(after.id): # Checking if both you and the user are in the guild
mutual_guilds.append(g)

How to get the sum and the names of all the users from all voice channels Disocrd?

I use :
import discord
I need to get from each voice channel amount all users and then get their names (usernames). How to do it?
You need to access the voice channel object. I recommend you use the voice channel's id. The command could look as follows:
#client.command(pass_context = True)
async def vcmembers(ctx, voice_channel_id):
#First getting the voice channel object
voice_channel = discord.utils.get(ctx.message.server.channels, id = voice_channel_id)
if not voice_channel:
return await client.say("That is not a valid voice channel.")
members = voice_channel.voice_members
member_names = '\n'.join([x.name for x in members])
embed = discord.Embed(title = "{} member(s) in {}".format(len(members), voice_channel.name),
description = member_names,
color=discord.Color.blue())
return await client.say(embed = embed)
And would work like this:
Where the number at the end is the channel id. If you don't know how to get the channel id, right click the channel and click Copy ID.
If you can't see the Copy ID, turn on Developer Mode in your Settings > Appearance > Developer Mode
You can also get all the members of a voice channel like this (updated for discord.py versions 1.0.0+):
#client.command(brief="returns a list of the people in the voice channels in the server",)
async def vcmembers(ctx):
#First getting the voice channels
voice_channel_list = ctx.guild.voice_channels
#getting the members in the voice channel
for voice_channels in voice_channel_list:
#list the members if there are any in the voice channel
if len(voice_channels.members) != 0:
if len(voice_channels.members) == 1:
await ctx.send("{} member in {}".format(len(voice_channels.members), voice_channels.name))
else:
await ctx.send("{} members in {}".format(len(voice_channels.members), voice_channels.name))
for members in voice_channels.members:
#if user does not have a nickname in the guild, send thier discord name. Otherwise, send thier guild nickname
if members.nick == None:
await ctx.send(members.name)
else:
await ctx.send(members.nick)

Categories