So, I'm trying to create a API with aiohttp that if you do /?state=start, it will call the my_Bot() function and start the bot itself. It is giving me multiple errors with a bunch of different ways I have tried. I have both the start/index function and my_Bot function async'ed and it is giving this error
RuntimeWarning: coroutine 'my_Bot' was never awaited my_Bot() RuntimeWarning: Enable tracemalloc to get the object allocation traceback
This is the code for the Python file. The index function is for loading the request and starting the bot and the my_Bot function is for actually loading/starting the Discord bot instance
import os
from dotenv import load_dotenv
import asyncio
load_dotenv()
app = web.Application()
async def index(request):
state = request.rel_url.query['state']
if state == 'start':
my_Bot()
return web.json_response({'state': 'Online'})
return web.FileResponse('index.html')
app.router.add_get('/', index)
app.router.add_get('/index', index)
app.router.add_get('/index.html', index)
async def get_token(request):
token = os.getenv('TOKEN')
return web.json_response({'token': token})
app.router.add_get('/token', get_token)
async def my_Bot():
import discord
from discord.ext import commands
bot = commands.Bot(command_prefix='>', self_bot=True)
#bot.event
async def on_ready():
print('Logged in as')
print(bot.user.name)
print(bot.user.id)
print('------')
#bot.command()
async def ping(ctx):
await ctx.send('pong')
bot.run(os.getenv('TOKEN'))
if __name__ == '__main__':
#my_Bot()
web.run_app(app)```
yeah because u typed async def my_bot so u should await it
you can make a function to run the bot asynchronously
async def main():
await my_Bot()
web.run_app(app)
if __name__ == '__main__':
asyncio.run(main())
Related
I'm developing discord bot with discord.py==2.1.0.
I use cog to write the main function that I wanna use, but I found when the whole bot is wrapped in async function and called by asyncio.run(), my terminal won't show any error message when there is any runtime error in my cog script.
Here is the example application. I stored my bot token in environment variable.
bot.py
import os
import discord
from discord.ext import commands
import asyncio
token = os.environ["BOT_TOKEN"]
class Bot(commands.Bot):
def __init__(self):
intents = discord.Intents.default()
intents.members = True
intents.message_content = True
description = "bot example."
super().__init__(
command_prefix=commands.when_mentioned_or('!'),
intents=intents,
description=description
)
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
print('------')
bot = Bot()
async def load_extensions():
for f in os.listdir("./cogs"):
if f.endswith(".py"):
await bot.load_extension("cogs." + f[:-3])
async def main():
async with bot:
await load_extensions()
await bot.start(token)
asyncio.run(main())
./cogs/my_cog.py
from discord.ext import commands
class Test(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
print("Ready")
#commands.command()
async def command(self, ctx):
test_error_message() # An runtime error example
print("Command")
async def setup(client):
await client.add_cog(Test(client))
Command that I run in terminal to start the bot.
python bot.py
When I type !command in the discord channel, there is no error message showing in the terminal, but there is no "Command" printed out so I'm sure the code stopped at the line I called test_error_message()
I expected that it should show error message normally, but I cannot find useful reference to make it work :(
There is one reason I need to use asyncio, I have a task loop to run in the bot, like the code below.
from discord.ext import tasks
#tasks.loop(seconds=10)
async def avatar_update():
# code here
async def main():
async with bot:
avatar_update.start()
await load_extensions()
await bot.start(token)
I would happy to know if there are some great practice to handle error in this situation.
Thanks!
Client.start() doesn't configure logging, so if you want to use that then you have to do it yourself (there's setup_logging to add a basic config). run() configures logging for you.
For more info, read the docs. https://discordpy.readthedocs.io/en/stable/logging.html?highlight=logging
I am trying to make a Discord bot with slash commands using Python's nextcord module. My invite link uses both bot and applications.commands scopes.
I've started with a ping command using examples I've found online. My code looks like this:
import nextcord, os
from dotenv import load_dotenv
from nextcord.ext import commands
from flask import Flask
from threading import Thread
load_dotenv()
app = Flask('')
#app.route('/')
def home() -> str:
return 'Running!'
def run() -> None:
app.run(port=int(os.environ.get('PORT', 33507)))
def keep_alive() -> None:
Thread(target=run).start()
TOKEN = os.environ['DISCORD_TOKEN']
description = '[bot name] [version]'
intents = nextcord.Intents.default()
intents.members = True
client = commands.Bot(command_prefix='/', description=description, intents=intents)
async def embed(title, description, reason) -> nextcord.Embed:
return nextcord.Embed(
title=title,
description=description,
color=0x00FF00
).set_author(
name='[bot name] [version]',
icon_url='[image link]'
).set_footer(
text=f'This message was sent because {reason}.'
)
#client.event
async def on_ready():
print(f'Logged in as {client.user} (ID: {client.user.id})')
await client.change_presence(activity=nextcord.Game(name='[version]'))
#client.slash_command(name='ping', description='Returns bot latency')
async def ping(interaction: nextcord.Interaction):
await client.process_application_commands(interaction)
await interaction.response.defer(with_message=True)
await interaction.followup.send(embed=embed(':ping_pong: Pong!', f'{client.latency * 100} ms', f'{interaction.user} used the "ping" command'))
if __name__ == '__main__':
keep_alive()
client.run(TOKEN)
I've used a function to return an embed object to use as message content.
When running /ping on Discord, it returns "[bot name] is thinking..." before eventually changing to "The application did not respond".
What am I doing wrong?
I've discovered the answer through this StackExchange post. I needed to use embed=await embed() as shown here:
await interaction.followup.send(embed=await embed(...))
I'm trying to record twitch chat messages while still recognizing commands, however, recording the messages using event_message seems to stop the command from working.
Anybody know how you would recognize/respond to commands and record message at the same time?
from twitchio.ext import commands
import time
from datetime import datetime
class Bot(commands.Bot):
def __init__(self):
super().__init__(
token='oauth:',
prefix='!',
initial_channels=['channelname']
)
# This function isn't running; event_message is stealing the input!
#commands.command(name='ping')
async def my_command(self, ctx):
print('Caught ping')
await ctx.send('pong')
# records the messages (or in this case, the time a message is sent)
async def event_message(self, message):
print(time.mktime(datetime.now().timetuple())) # prints the unix second
if __name__ == '__main__':
bot = Bot()
bot.run()
Reducing this down even more, the same issue occurs here; with the bot responding to the command, but not printing the message author or content:
from twitchio.ext import commands
import os
bot = commands.Bot(
token='', prefix="!", initial_channels=[""], client_id='', nick='', client_secret='')
#This isn't working; Should still grab the message author/name and print.
Ive tried twitchio versions between 1.3.0 and 2.3.0, but the issue persists.
#bot.event
async def event_message(ctx):
print(ctx.author.name)
print(ctx.content)
await bot.handle_commands(ctx)
#This is running
#bot.command(name='test')
async def test_command(ctx):
await ctx.send('this is a test response')
if __name__ == '__main__':
bot.run()
Issue was not including an "await bot.handle_commands(ctx)" command below the event_message function.
Attached below is a working code
from twitchio.ext import commands
class Bot(commands.Bot):
def __init__(self):
super().__init__(token='', prefix="!", initial_channels=[""])
#commands.command()
async def ping(self, ctx):
print('Caught Ping')
await ctx.send(f'Pong!')
async def event_message(self, message):
print(message.author.name, message.content)
await bot.handle_commands(message) # <--This is the new line
bot = Bot()
bot.run()
NOTE: An error is raised by the event_message function when the bot sees one of its' own messages in chat. This doesn't seem to inhibit function, but it can be fixed by changing the code to the following:
async def event_message(self, message):
if hasattr(message.author, 'name'): # <-- New Line
print(message.author.name, message.content)
await bot.handle_commands(message)
Update:
After few uncessful attempts to explain the problem, completely rewrote the question:
How to execute a function on startup?
from aiogram import Bot, Dispatcher, executor, types
API_TOKEN = 'API'
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
#dp.message_handler()
async def echo(message: types.Message):
await message.answer(message.text)
async def notify_message() # THIS FUNCTION
# await bot.sendMessage(chat.id, 'Bot Started')
await print('Hello World')
if __name__ == '__main__':
notifty_message() # doesn't work
executor.start_polling(dp, skip_updates=True)
Tried without success::
if __name__ == '__main__':
dp.loop.create_task(notify_message()) # Function to execute
executor.start_polling(dp, skip_updates=True)
AttributeError: 'NoneType' object has no attribute 'create_task'
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.create_task(notify_message()) # Function to execute
executor.start_polling(dp, skip_updates=True)
TypeError: object NoneType can't be used in 'await' expression
If you want to iniate a dialogue, it's impossible, telegram doesn't allow bots to send first message. But if you want to save your users ids, you can make a database or just a txt file and write there any new user id. You will be able to send to all of them messages by inputing id from database instead of chat.id
I give you an example of how to send a message without using message.answer just use:
#dp.message_handler()
async def echo(message: types.Message):
await bot.send_message(message.chat.id, message.text)
I don't understand why you need to print ('The bot is running') When running "aiogram" it tells you that the bot was started
2021-06-01 09:31:42,729:INFO:Bot: YourBot [#YourBot]
2021-06-01 09:31:42,729:WARNING:Updates were skipped successfully.
2021-06-01 09:31:42,729:INFO:Start polling.
It was much simpler than expected. facepalm
Working Solution:
from aiogram import Bot, Dispatcher, executor, types
API_TOKEN = 'API'
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
#dp.message_handler()
async def echo(message: types.Message):
await bot.send_message(message.chat.id, message.text)
def test_hi():
print("Hello World")
if __name__ == '__main__':
test_hi()
executor.start_polling(dp, skip_updates=True)
The second approach was correct, but asyncio event loop was not initialised. So, if it was important to call async function beside a bot, you would do:
from asyncio import get_event_loop
from aiogram import Bot, Dispatcher, executor, types
API_TOKEN = 'API'
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot=bot, loop=get_event_loop()) # Initialising event loop for the dispatcher
async def notify_message():
print('Hello World')
if __name__ == '__main__':
dp.loop.create_task(notify_message()) # Providing awaitable as an argument
executor.start_polling(dp, skip_updates=True)
you can call aiogram.executor.start(dispatcher, future) to run an async function (future)
from aiogram import Bot, Dispatcher, executor
bot = Bot(token='your_api_token')
dp = Dispatcher(bot)
async def notify_message() # THIS FUNCTION
# await bot.sendMessage(chat.id, 'Bot Started')
await print('Hello World')
#dp.message_handler()
async def echo(message: types.Message):
await message.answer(message.text)
if __name__ == '__main__':
executor.start(dp, notify_message())
executor.start_polling(dp, skip_updates=True)
I'm trying to use a listener event to activate a function I have in a cog using discord.py. This same code functions properly in the main bot.py file (removing self and using #client.event instead of a listener) but when used in the cog, it tells me that the repeater() function is an undefined variable when being used in the listener event
import discord
from discord.ext import commands, tasks
class Repeater(commands.Cog):
def __init__(self, client):
self.client = client
#tasks.loop(seconds = 10)
async def repeater(self):
channel = self.client.get_channel(834554679265329172)
await channel.send('test')
#commands.Cog.listener()
async def on_ready(self):
repeater.start()
def setup(client):
client.add_cog(Repeater(client))
EDIT:
So I changed the code match this after a comment recommended it, and the console throws this error
#commands.Cog.listener()
async def on_ready(self):
self.repeater()
> discord.ext.commands.errors.ExtensionFailed: Extension 'cogs.repeater'
> raised an error: TypeError: Cog.listener expected str but received
> 'function' instead.
EDIT 2:
Changed the code to match this, and it will run the loop exactly one time, but doesn't actually loop
#commands.Cog.listener()
async def on_ready(self):
await self.repeater()
In class you have to use self. to access its method self.repeater and self.repeater.start()
I tested it on this code and it works for me.
import discord
from discord.ext import commands, tasks
import os
import datetime
TOKEN = os.getenv('DISCORD_TOKEN')
CHANNEL = 834554679265329172
class Repeater(commands.Cog):
def __init__(self, client):
self.client = client
#tasks.loop(seconds=10)
async def repeater(self):
#channel = self.client.get_channel(CHANNEL)
await self.channel.send(datetime.datetime.now().strftime("It's %H:%M.%S"))
#commands.Cog.listener()
async def on_ready(self):
self.channel = self.client.get_channel(CHANNEL)
print('starting repeater')
self.repeater.start()
def setup(client):
client.add_cog(Repeater(client))
# --- main ---
client = commands.Bot(command_prefix='!')
setup(client)
print('starting bot')
client.run(TOKEN)
repeater is a function of the class. So you can call it with self.repeater. And to start a discord task use start attribute. self.repeater.start() is the answer.