Im not able to automate discord webhook python - python

What does it need to do?:
A fairly simple script pulls info from RSS feed and posts it via discord_webhook, in a discord channel. This works fine. However, the entries must be "updated" after a certain time. Thus, the old webhook message is deleted and the new message sent or edited.
What is the problem?:
The problem is that the message will be removed however on the "repost" it posted the previous one too. I have researched this and found some answers but nothing that could resolve the issue in a code sense. (I'm not too experienced with coding).
I tried:
Reviewing my code from a logic perspective it doesn't seem to be caused by the code itself, but more like a thing with the discord_webhook.py, I'm not doing right when sending multiple messages(Loop).
As I said, I have seen some solutions but not able to use them to change my code or get what needs to be changed.
The code:
from discord_webhook import DiscordWebhook, DiscordEmbed
import feedparser
from time import sleep
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Vars
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
webhook = DiscordWebhook(url='')
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Functies
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
def main(webhook):
# Main function , calling other functions
# Send first message
message_send(webhook)
# Time to wait before refresh
sleep(10)
# Remove the old message
message_remove(webhook)
def message_send(webhook):
# Send message thru webhook
# Get the info from the RSS feed function
entry = info_get()
# Create embed message for the webhook
embed = DiscordEmbed(title="Report", description="Repeatable report", color="14177041")
embed.set_author(name='NCSC')
embed.set_footer(text='https://www.ncsc.nl/')
for i in entry:
embed.add_embed_field(name=i[0], value=i[1], inline=False)
embed.set_timestamp()
webhook.add_embed(embed)
# Send message
response = webhook.execute()
def message_remove(webhook):
# Removes the old message
webhook.delete(webhook)
def info_get():
list = []
# Get the information thru RSS feed
info = feedparser.parse("https://advisories.ncsc.nl/rss/advisories")
# Use the first 4 entries
entry = info.entries[: 4]
# List only the title and link, rest is not needed
for i in entry:
list.append([i.title, i.link])
return list
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Runs
#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Loop the main function
while True:
main(webhook)
The result in discord after the first refresh:
This is the result of the script that keeps stacking messages.
Note: sleep(10) is normally sleep(3600)
https://cdn.discordapp.com/attachments/490928227163045888/795927426122121266/unknown.png
If anything comes to your mind please let me know, even things that simply could improve my code since I'm trying my best to learn.
Thank you,
Greetings.

If you want to use the official discord.py library.
Note: Unfornatelly you CANNOT delete messages with a webhook, the simplest way would be to edit the message (you need discord.py v. 1.6 or above)
import aiohttp
import discord
from discord.ext import tasks
intents = discord.Intents.default() # Enabling default intents, change it accordingly to your needs
client = discord.Client(intents=intents) # If you also want commands use `commands.Bot`
previous_message_id = None # Just a placeholder
#tasks.loop(seconds=10) # Or whatever interval you want
async def bg_task(webhook):
global previous_message_id
"""Will edit a message every 10 seconds (if there is one)"""
if previous_message_id is None:
# Sending a new message
message = await webhook.send('Hello World', username='Foo', wait=True)
previous_message_id = message.id
else:
# Editing the previous message sent by the webhook
await webhook.edit_message(previous_message_id, content='Whatever')
#client.event
async def on_ready():
await client.wait_until_ready()
print('Logged in as {0.user}'.format(client))
# Creating a session and getting the webhook
client.session = aiohttp.ClientSession()
webhook = discord.Webhook.from_url('url-here', adapter=discord.AsyncWebhookAdapter(client.session))
# Starting the task
bg_task.start(webhook)
if __name__ == '__main__':
client.run('TOKEN')
Reference:
tasks.loop
discord.Webhook.from_url
discord.Webhook.send
discord.Webhook.edit_message

Related

Automatically sending gifs in discord

I added a bot to Discord and added various functions in Python, but just as some work without any problems, the automatic sending of gifs at a specific time and on a specific channel doesn't work. After configuring everything and starting the bot, nothing happens, no error is displayed, and the console is also silent. What could be wrong? Below I'm sharing a part of the code responsible for this function, thank you in advance for your response.
import asyncio
import datetime
import discord
from discord.ext import commands
client = commands.Bot(command_prefix='.', intents=discord.Intents.all())
async def send_gif(channel):
print(f"Sending gif to channel {channel}")
gif_id = "Here's the gif id"
gif_url = f"Here's url"
await channel.send(gif_url)
#client.event
async def on_ready():
channel_id = "Here's the text channel ID"
channel = client.get_channel(channel_id)
await client.wait_until_ready()
while not client.is_closed():
now = datetime.datetime.now()
if now.hour == sample hour and now.minute == sample minutes:
await send_gif(channel)
await asyncio.sleep(60)
I would like the bot to send specific gifs at a specified time and on a specified channel, but I'm out of ideas. I tried using chatGPT, but it was unable to help. I also checked several times to make sure I was entering the correct gif IDs, channel IDs, and URLs, but it didn't help.
Check that you didn't miss any symbols on on_ready event.
Maybe the error is in
...
if now.hour == sample_hour and now.minute == sample_minutes:
...
Also, check that you defined sample_hour and sample_minutes variables before referring them in the code.
If still doesn't work try using the ext.tasks.loop decorator, if you don't know how to, take a look to the documentation
If it's not the thing causing the error, comment in this message and I will try to edit this message as fast as possible for solving doubts.

How to send a message to a specific channel as part of a slash command

When I receive a slash command with my bot, I send a modal to a user asking for information.
All this works, however, as part of that, I would also like to send a message to a specific channel on a specific server (guild) to say that a request has been made.
I am having trouble with that second part.
import discord
bot = discord.Bot()
client = discord.Client()
#bot.slash_command(name = "create-trial-request", description = "Create a new trial request from a bbcode template.")
async def trial_request(ctx):
modal = my_modal(title="Fill this in please")
await ctx.send_modal(modal)
class my_modal(discord.ui.Modal):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.add_item(discord.ui.InputText(label="Some Label Name"))
async def callback(self, interaction: discord.Interaction):
request_number = request_number_generator()
# here is where I want to send my message to a specific channel.
# I know the ID of that channel, I just don't know how to send the message there.
code_snippet = format_to_code(bbcode)
request_response = "Created request #" + str(request_number)
await interaction.response.send_message(request_response, ephemeral=True)
I have tried the following (placed where my comments are in the code above):
channel = client.get_channel(6648250855168XXXXX)
await channel.send("Created trial request #" + str(request_number))
...but I get: AttributeError: 'NoneType' object has no attribute 'send'
Obviously the bot has access to the channel, and if I write to it as part of the response to the slash command, it successfully writes there, but I can't seem to make it work on its own.
Is there any way to do what I am trying to?
Thanks for any help.
To do so, 1. import commands aka from discord.ext import commands
Then remove your bot = discord.Bot and edit client do it is client = commands.Bot()
thats what i do
Thanks to #3nws, I now know the answer to this question.
The issue in my case was that I was using both client and bot and should have only been using one of the two (in this instance, bot, since I am using slash commands.
There is a usable bot.getchannel command that does what I wanted.
I hope this helps anyone else with the same issue.
First of all: Don't create 2 bot/client instances. Just use commands.Bot once. That's the reason you made a mistake. You used client instead of bot. Replace it and it should work.
Otherwise, if it would be still None, here are some possible reasons why:
• Not subscribed to the relevant intent.
• Wrong key
 ◦ Remember, IDs are ints, not strings
 ◦ If you're trying to copy an emoji ID, right-clicking the emoji in a message will copy message ID
• Bot not logged in, and trying to grab objects from cache
 ◦ Subvairant, using two client objects.
• Bot cannot "see" the object.
 ◦ It has to be on the server, share a server with the member, etc
 ◦ If you're sharded on separate processes, each process will only have objects for that shard.
• Objects retuned by fetch_x will not have a populated cache.
It doesn't make a huge difference if you do bot.get_channel() or bot.get_guild().get_channel(). You can use both.

How do I make a bot to message every hour on discord without using a discord bot?

I want to make a python bot that sends a message to a specific discord channel every hour, I searched up online but I only got tutorials using discord bots. I want to send the message from my OWN account
and not a discord bot, I am a newbie too so i cant understand too complex codes!
Thanks!
This is a basic bot you could use to post every hour; as Taku mentioned Discord TOS would be violated using a userbot.
import random
import discord
from discord.ext import tasks
# Init the discord client
discord_client = discord.Client()
# The channel ID you want the bot to send your message to (Right click channel -> Copy ID)
channel_id = 000077777777770000
# Set the channel object using our channel ID number
channel = discord_client.get_channel(channel_id)
# List we will use these in the example to send random messages to the server
messages = [ "Hi I'm a bot!", "I'm just talking so you don't think I'm inactive!", "Blah blah blah blah blah!", "Add however many you like me to say in here!" ]
# Single message to get sent to the server string
single_message = "This will send over and over if multi_message = False."
# We will use this boolean to determine if we are just sending message string or a random one from messages list
multi_message = False
# Create a loop task for every 60 minutes [1 hour]
#tasks.loop(minutes = 60)
async def send_message():
# Call channel so we can modify it's value
global channel
# Make sure channel isn't null
if channel == None:
channel = discord_client.get_channel(channel_id)
# Wait for the discord bot to load before we start posting
await discord_client.wait_until_ready()
# Check to see if we are going to be sending different messages every hour or not
if multi_message:
# Print to output
print("Posted random message.")
# Send message to Discord channel
await channel.send(f"{random.choice(messages)}")
else:
print("Posted single message")
await channel.send(f"{single_message}")
# On bot ready
#discord_client.event
async def on_ready():
# Check to see if we are going to be sending different messages every hour or not
if multi_message:
print("* Sending random messages to the channel...")
else:
print("* Sending single message to the channel...")
# Start the message sending loop
send_message.start()
# Finished setting up
print("Bot ready.")
# Launch the Discord bot
print("+ Loading Discord message posting bot...")
discord_client.run("GET YOUR DISCORD TOKEN FROM DISCORD DEVELOPER PORTAL")
In the #comments it's pretty self explanatory.

Discord.py Bot Takes too Long to Respond

Goal:
I'm developing a discord bot which scans a url every 5 seconds or so, checks for a specified change on that webpage, and will send a message in the discord channel if that change occurs. I've done this by sending the url to the bot using an if statement in on_message. The url is then passed to a tasks.loop() function, where it is scanned and processed in another function for the change.
Problem:
I'd like to be able to send a message in the discord channel which quickly ends the process taking place in the tasks.loop(), so that I can pass it a different url to scan using the on_message function. In its current form, it works-- just very slowly. From the time the cancel trigger is sent, it takes around 3 minutes to send the verification message that the process has been cancelled. I need to make this 5 seconds or less. For what its worth, the bot is kept running using replit and uptime robot, but I am sure that the long response time is not related to the frequency the repl is awoken by uptime robot.
Code:
My code is much more complex and riddled with obscurely named variables, so here is a much simpler snippet of code with the same general structure.
client = discord.Client()
channel = client.get_channel(CHANNEL_ID)
#tasks.loop()
async def myloop(website, dataframe):
channel = client.get_channel(CHANNEL_ID)
try:
# iteratively scrape data from a website for
# a predefined change in the dataframe
if change = True:
await channel.send(notification)
except:
pass
#client.event
async def on_message(message):
channel = client.get_channel(CHANNEL_ID)
msg = message.content
if msg.startswith('track'):
website = msg[6:]
await channel.send('Now tracking '+str(website))
myloop(website,df)
if msg.starswith('stop'):
myloop.cancel()
await channel.send('Done tracking, awaiting orders.')
Attempted Solutions:
I have tried using some forms of threading, which I am very new to, and I haven't found a way to make it work any faster. Any suggestions or solutions would be greatly appreciated! I've been combing the web for help for quite some time now.
Looks like you could use client.loop.create_task to create asyncio task objects, and their cancel method to immediately cancel those asyncio tasks at the right time, e.g.
import asyncio
from replit import db
_task = None
async def myloop():
website = db['website']
dataframe = db['dataframe']
channel = client.get_channel(CHANNEL_ID)
while not client.is_closed():
await asyncio.sleep(5)
try:
# iteratively scrape data from a website for
# a predefined change in the dataframe
if change:
await channel.send(notification)
except:
pass
#client.event
async def on_message(message):
global _task # This gives the function access to the variable that was already created above.
msg = message.content
if msg.startswith('track'):
website = msg[6:]
await message.channel.send('Now tracking '+str(website))
db['website'] = website
db['dataframe'] = df
if _task is not None:
_task.cancel()
_task = client.loop.create_task(myloop())
if msg.startswith('stop'):
if _task is not None:
_task.cancel()
_task = None
await message.channel.send('Done tracking, awaiting orders.')
The argument create_task takes is a coroutine that takes no arguments, so the website URL and dataframe need to be accessible to the function a different way (I'm not sure which way you would prefer or would be best; using replit's db is just an example).
With this approach, you should be able to use track again to change which website is being monitored without using stop in between.
More details in the docs:
discord.Client.loop
loop.create_task
Task.cancel
asyncio.sleep
discord.Client.is_closed
Replit db

Discord bot gathering info with JSON/Python Displays Same Data

I want to make a Discord bot. Everything works fine until I want to gather some information from a website using JSON and Python.
The data gets collected, but everytime I type my command in Discord in order to get my information, it displays the same data it used, like it's only getting it ONE TIME - when I connect the bot to my server. Which is exactly what it does.
How can I make it so it gathers the data again every time someone types the command?
Example: I have the command !status, and whenever I type it, my bot takes the status of a Game Server from a website using JSON. Let's say the server is online so it displays "Status: Online" but if the server closes and I type !status again, it still says the status is Online. I have to re-connect the bot to the Discord server so it picks the status up.
I get it that my python script only works one time.
Here's a snippet:
r = requests.get('http://etcthewebsite')
json_data = json.loads(r.text)
status_server = json_data['status']
def on_message(message):
if message.content == "!status":
yield from client.send_message(message.chanel, "Status is: " + status_server)
Hope you guys understand what I mean. I repeat: the code works fine and whatnot, but it doesn't pick the information every time I type the command; only once - when THE BOT enters the server.
You should be getting the external data from the server every time you serve it to a user. Depending on how often you expect it to change, you could try to keep a copy of it and keep track of how long ago you accessed it, but that's probably unnecessary.
def on_message(message):
if message.content == '!status':
r = requests.get('http://etcthewebsite')
json_data = json.loads(r.text)
status_server = json_data['status']
yield from client.send_message(message.chanel, "Status is: " + status_server)
Edit:
As an aside, you can use discord.ext.commands to make writing bot commands a little simpler.
from discord.ext import commands
import requests
bot = commands.Bot(command_prefix='!')
#bot.command()
async def status():
r = requests.get('http://etcthewebsite')
json_data = json.loads(r.text)
status_server = json_data['status']
await bot.say('Status is {}'.format(status_server))

Categories