Discord.py random post from specific user - python

I have a function that pulls a random embed placed in a channel by a bot and reposts the embed the channel where the function is called. I would like to extend the functionality here and allow the user to call the function with a filter for the user of their choosing -
!bestpost - selects a random embed from specific channel and posts it to the channel where the function is called
!bestpost $user - selects a random embed from a specific user in a specific channel and posts the embed to the channel where the function is called
My issue here is that the embeds that the bot has created are posted by the bot NOT the user so I can't check by author. However the user's name is always present in the title field of the bot-created embed.
#client.command()
async def bestpost(ctx, poster: Optional[str.lower]):
channel = client.get_channel(123)
best_posts = []
# if poster is specified, then only pulls messages from that user
if poster is not None:
async for message in channel.history(limit=None):
if poster in message.embeds[0].title:
best_posts.append(message)
# if poster is not specified, then pulls all messages
else:
async for message in channel.history(limit=None):
best_posts.append(message)
random_bestpost = random.choice(best_posts).embeds[0]
await ctx.send(embed=random_bestpost)
Here is the most recent error it's been spitting at me
TypeError: argument of type 'NoneType' is not iterable
I've been dinking with this for a few hours and I'm pretty confident I'm just misunderstanding something simple or or just developing brain lesions from staring at such a simple problem for so long. I appreciate any feedback or assistance anyone can provide.

Here is the simple solution for the thing that you want to achieve:
import discord
import random
import re
def bestpost(user, channel):
all_posts = []
for post in channel.history(limit=1000):
if user in post.title:
all_posts.append(post)
if all_posts:
return random.choice(all_posts)
Async Solution
I wrote a solution for you that uses the content of the message title to decide which user to filter by. If a message title has the text $user in it, it means you want to filter the posts by that user.
Feel free to ask for clarifications in the comments.
import discord
import random
TOKEN = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
client = discord.Client()
#client.event
async def on_message(message):
if message.content.startswith('!bestpost'):
args = message.content.split()
if len(args) >= 1:
user = args[1]
else:
# Here you can handle the error of not specifying a user
continue
# This line gets all the messages from the channel
async for message in message.channel.history():
# This line checks if the author of the message is the bot. You can replace this condition with any other condition you like to filter the messages by.
if message.author.id != client.user.id:
continue
if user in message.title:
# This line gets the embeds from all the messages
embeds = message.embeds
# This line checks if there are any embeds in the message. You can replace this condition with any other condition you like to filter the messages by.
if embeds:
# This line gets a random embed from the message
embed = random.choice(embeds)
# This line sends the embed to the channel where the command was used
await message.channel.send(embed=embed)
client.run(TOKEN)

Related

Is there a way to see if a member is being mentioned in the embed?

I am using discord.py and I am trying to get the member object from the command, and when a button is pressed, it edits the message and display's user mod log data. I got it working but in a sort of odd way, it pings the person in the message, and then the event checks to see if there was a ping in (interaction) I am wondering if there is a way to look inside the embed for a mention like instead of interaction.message.mentions is there something like that for embeds instead of message? Thanks in advance!
#client.command()
async def modlogs(ctx, member: discord.Member):
main=discord.Embed(title=" ", description=f"{ctx.author.mention} please use the buttons below to navigate {member.mention}'s modlogs.", color=0x76dba8)
main.set_author(name=f"{member}", icon_url = member.avatar_url)
ram_member = member
await ctx.send(f"{member.mention}",
embed = main,
components=[[
Button(style=ButtonStyle.blue, label="Warnings", custom_id= "blue"),Button(style=ButtonStyle.blue, label="Kicks", custom_id= "red"),Button(style=ButtonStyle.blue, label="Bans", custom_id= "green")
]],
)
#client.event
async def on_button_click(interaction):
print(interaction.message)
if interaction.message.mentions:
if (interaction.message.mentions.__len__() > 0):
for member in interaction.message.mentions:
if collection2.count_documents({"_id": member.id}) == 0:
embed1=discord.Embed(description=f"{member.mention} has no warnings!", color=0xff0000)
embed1.set_author(name="Error")
else:
Get a Message
Msg = await Bot.get_message(channel, id)
Get an Embed
Look at the first answer to [this question][1].
Emb = discord.Embed.from_data(Msg.embeds[0])
Get the fields
Look at the answer to [this question][2].
for field in Emb.fields:
print(field.name)
print(field.value)
Get the description
Look at the first answer to [this question][3].
Des = Emb.description
Search for the mention
>>> Member.mention in Des
True
Searching a mention
A mention begins with ```#!``` and ends with ```>```.
So you might use this fact to search a mention into a string (Embed.description returns a string-type object).
Like this:
Men = Des[Des.find("<#!"):Des.find(">")]
UserID = int(Men[3:])
Member = ctx.fetch_user(UserID)
Links
Discord.py get message embed
How to align fields in discord.py embedded messages
discord.py Check content of embed title
How do I mention a user using user's id in discord.py?
In conclusion
I guess it will work.

Praw & Discord.py: The bot keep sending the same meme. I want the bot to send different meme whenever it is asked

I am trying to create a listener for meme. The bot will respond to the message when it has "meme" in it. It has to send a meme. I am using Praw for that purpose.
This is the code I have written:
#nd.listen()
async def on_message(message):
if message.author == nd.user:
return
def findCoindences(w):
return re.compile(r'\b({0})\b'.format(w)).search
content = message.content
reaction = "👍"
subreddit = reddit.subreddit("memes")
for submission in subreddit.random():
submission_link = submission.url
if findCoindences('meme')(content.lower()):
await message.reply(submission_link)
await message.add_reaction(f"<{reaction}>")
Note: I have defined reddit already above some lines. That is not the cause I guess.
The problem:
Whenever I send meme in the chat it sends the same meme every time. I want it to be different but don't know where I am having this issue.
This is what it looks like:
There is no need for you to iterate over anything. Praw provides a method which allows you to grab a random submission from a subreddit.
Documentation:
random()
Code:
#nd.listen()
async def on_message(message):
if message.author == nd.user:
return
def findCoindences(w):
return re.compile(r'\b({0})\b'.format(w)).search
content = message.content
reaction = "👍"
if findCoindences('meme')(content.lower()):
random_submission = reddit.subreddit('memes').random()
await message.reply(random_submission.url)
await message.add_reaction(f"<{reaction}>")
Note: I have move the random_submission inside the If-statement because there is no need for you to look up a submission if the message doesn't say "meme". It would just waste rate limit.

Check if the message channel is in that category

Can someone help me? I'm trying to make a modmail every thing is working fine, user sends a DM, bot creates a channel under "category" and if someone message inside that "category" it will be delivered to the user via DM. However I get this annoying error every time someone replies inside that "category" or user DMs the bot. I'm trying to make a category check to only do something if it's the mod mail category. Thank you in advance!
Here's my code:
async def on_message(self, message):
if isinstance(message.channel, discord.DMChannel):
# User DM the bot and the bot will make a textchannel under "category" and will send his DMs there. #
if message.channel.category_id == 123456789101112:
if isinstance(message.channel, discord.TextChannel):
# Message the user who DMed the bot using the textchannel under "category", your replies will be sent to the user by the bot via DM. #
Everything is working, but I'm getting this error every time someone replies inside that textchannel "category" or user DMs the bot
Error:
if message.channel.category_id == 123456789101112:
AttributeError: 'DMChannel' object has no attribute 'category_id'
Your if-statement doesn't make much sense, you should first check for the type of the channel and then for it's category and compare it.
async def on_message(message):
# Checking if the channel is a dm or a text one
if isinstance(message.channel, discord.DMChannel):
# do your thing
elif isinstance(message.channel, discord.TextChannel):
if message.channel.category is not None:
if message.channel.category.id == your_id:
# do your thing
You should format your code properly.
Also you can check whether message.author.id is your bots id client.user.id or a user.
Read the documentation for further information.

How do I grab and send the profile picture of anyone on my server using a discord.py bot?

I've been working on a discord bot using discord.py for the past few days. Now i'm trying to implement a command that displays the profile picture of anyone you mention on the server.
The problem is I can only get it to give the profile picture of the person who sends the command. I can't get it to take in a second parameter without encountering errors or the bot ignoring it.
#client.command(aliases=['getprofilepic','getprofilepicture','pfp','profilepic','profilepicture','avatar'])
async def getpfp(ctx):
await ctx.send(ctx.author.avatar_url)
The code above is a command to send the profile picture of the user who sends the command into the chat.
It ignores any extra parameters and thus can not display the profile picture of anyone else except the sender.
Changing getpfp(ctx): to getpfp(ctx, usernamepfp): and ctx.send(ctx.author.avatar_url) to ctx.send(ctx.usernamepfp.avatar_url) didn't work.
I also tried ctx.send(usernamepfp.avatar_url).
How would I implement the command such that when one types .getpfp #username in the chat it would send the profile picture of #username and not the sender?
from discord import Member
#bot.command()
async def getpfp(ctx, member: Member = None):
if not member:
member = ctx.author
await ctx.send(member.avatar_url)
You can use getpfp to display the avatar of user by passing 1) Mention/Tag 2) Display name/ Nick Name 3) Discord ID.
eg .getpfp #Someone

Discord.py - How would I go about making a command that will allow server admins to block the bot from responding in specified channels?

My bot is in a few servers now and one of the main bits of feedback I've gotten is that server admins would like to block the bot from responding in certain channels without having to go through Discord's permissions manager. However I am unsure of where to start with this so I thought I'd reach out here and see if I can get any advice or code snippets to use!
Basically the admin would use like !fg ignore 'channel name or id' and then somewhere the bot would store this and not respond, and then similarly if they use !fg unignore 'channel name or id' it would then remove that from the list or where ever its stored.
Any help would be greatly appreciated, thanks!
Here's an example I've made to get this working:
import discord
from discord.ext import commands
ignoredChannels = [] # List of all the ignored channels, you can use a text file instead if you prefer
client = discord.ext.commands.Bot(command_prefix = "fg!");
#client.event
async def on_message(message):
if message.channel.id in ignoredChannels:
return # If the channel is in the ignored list - return
else:
await client.process_commands(message) # Otherwise process the commands
#client.command()
async def ignore(ctx, channelID):
if int(channelID) not in ignoredChannels:
ignoredChannels.append(int(channelID)) # Add the channel if it hasn't been added yet
await ctx.send("Successfully added the channel to the ignored list!")
else:
await ctx.send("Channel was already inside the ignored list!") # Otherwise warn user that the channel is already ignored
#client.command()
async def unignore(ctx, channelID):
try:
ignoredChannels.remove(int(channelID)) # Attempt to remove the channel from the list
await ctx.send("Successfully removed the channel from the ignored list!")
except:
await ctx.send("This channel is already removed!") # If fails, warn the user that the channel is already removed
client.run(your_bot_token) # Run the bot with your token
How it works is it checks if the channel ID exists in the list every time a message gets sent, if it finds the channel in the list it will return and do nothing otherwise if the channel is not in the list, it will continue processing the commands in that channel.
If you want to only allow admins to use the command you can add #commands.has_permissions(administrator=True) under each of the #client.command() lines.
Hope it helps and happy coding :)
You'll need to keep the channels ids in a list then in the bots on_message function check if the message is not in that channel and if not then run your commands.

Categories