'AsyncTeleBot' object has no attribute 'register_next_step_handler' - python-asyncio

Trying to create simple ask, answer, remember telegram bot with pyTelegramBot.
Everything were normal, when TeleBot were used. TeleBot.register_next_message_handler helped me a lot.
Example:
...
#bot.message_handler(func=lambda msg: msg.text is not None and '/start' in msg.text)
def send_welcome(msg):
global cur_user
cur_user = msg.from_user.id
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton('Да', callback_data='old'),
types.InlineKeyboardButton('Нет', callback_data='new'))
keyboard.add(types.InlineKeyboardButton('Пора остановиться', callback_data='stop_bot'))
greet = 'Бла...бла...бла\nМы знакомы?'
bot.send_message(msg.chat.id, greet, reply_markup=keyboard)
...
#bot.callback_query_handler(func=lambda call: True)
def query_processing(call):
global user
global cur_user
if call.data == 'new':
user = dict.fromkeys(user, None)
nxt = bot.send_message(call.message.chat.id, 'Как звать?')
bot.register_next_step_handler(nxt, get_name_ask_goal)
...
Still I need my bot to be asynchronous because of sleep time till message to be send from bot and with simultaneous possibility for user to send messages.
Tried to use AsyncTeleBot, but there is no register_next_step_handler function. I did't find out how to make bot wait user for the name typing and it is almost impossible to me to add register_next_step_handler function into particular files. Although found issue in gitHub and no solution from 2017.
Tried
...
bot = AsyncTeleBot(os.getenv('token2'))
...
async def beep(chat_id) -> None:
"""Send the beep message."""
await bot.send_message(chat_id, text='Beep!')
aioschedule.clear(chat_id)
async def scheduler():
global chat_id
aioschedule.every(5).seconds.do(beep, chat_id).tag(chat_id)
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
async def main():
await asyncio.gather(scheduler(), bot.polling(non_stop=True))
if __name__ == "__main__":
asyncio.run(main(), debug=True)
...
Result:
TypeError: An asyncio.Future, a coroutine or an awaitable is required
I'm wondering:
is there other appropriate libraries for my task?
is there any simple solution to save user's message?
is it possible to use asyncio with TeleBot that made Thread-style?
Hoping for any help.

Related

How do you send a private message on_ready aka on a #client.event. Discord.py

I had multiple attempts.
# One of the attempts
ch = client.get_user(USERID)
await ch.send("IDK")
#Another
client = discord.Client()
#client.event
async def on_ready():
user = "#name"
user = discord.Member
await user.send(user, "here")
#Another
client = discord.Client()
#client.event
async def on_ready():
user = discord.utils.get(client.get_all_members(), id='USERID')
if user is not None:
await client.send(user, "A message for you")
else:
await client.send(user, "A message for you")
#Another
#client.event
async def on_ready():
ch = client.get_all_members()
await ch.send("Hello")
# Another
ch = client.start_private_message(USERID)
await ch.send("IDK")
As you can see I messed with the variables because I noticed that you can send a message by channel like this
channel = client.get_channel(CHANNELID)
await channel.send("Something)
But that doesn't work with get_user. Thanks in advance also sorry how bad my post/code is.
Allow me to inform you on what is wrong with the pieces of code you have provided.
await client.send(user, "A message for you") is older syntax, and has not been used since the v1.0 migration.
client.get_all_members() shouldn't be used in this case, as you are only getting all the members the bot shares a server with rather than a single user.
client.start_private_message(USERID) does not exist as far as I have read, and therefore we can assume that this wouldn't work.
My recommendation would be to use one of the two methods I will detail.
The first method would be to use client.fetch_user(), which sends a request to the Discord API instead of its internal cache (as the second method will). The only problem you will face with this method is if you retrieve too many users in a small amount of time, which will get you ratelimited. Other than that, I recommend this method.
The second method is to get a server through client.get_guild() and getting your user through there via guild.get_member(). This method will require the user to be in its cache, however, so this shouldn't be your go-to method in my opinion.
Do view both of these methods in the code below.
#client.event
async def on_ready():
# Method 1: Using fetch
user = await client.fetch_user(USER_ID)
await user.send("A message!")
# Method 2: Using 'get'
guild = client.get_guild(GUILD_ID)
user = guild.get_member(USER_ID)
await user.send("A message!")
Other Links:
Send DM to specific User ID - Stackoverflow
DM a specific user - Stackoverflow
Sending DM through python console (Fetch user) - Stackoverflow
DPY v1.0 Migration - Discord.py Documentation

How to change channel permissions without having a message sent first?

I have a program that changes what channel members can see at certain times of the day. To do this, I can either change the roles that every member has, or change the permissions of each channel. However, I have looked all over the web and all of the ways for either method require a message to be sent so that the data from that message can be read and put into the function, such as:
#client.command()
async def perm(ctx):
await ctx.channel.set_permissions(ctx.guild.default_role, send_messages=False
Change the permissions of a discord text channel with discord.py
or
async def addrole(ctx):
member = ctx.message.author
role = get(member.server.roles, name="Test")
await bot.add_roles(member, role)
Discord.py | add role to someone
My current program looks like this:
import discord
client = discord.Client()
import datetime
async def live_day(schedule):
current_place = "the void"
while True:
current_time = str((datetime.datetime.now() -datetime.timedelta(hours=7)).time())
int_time = int(current_time[:2] + current_time[3:5])
for x in range(len(schedule)):
try:
start_time = schedule[x][1]
end_time = schedule[x + 1][1]
except IndexError:
end_time = 2400
if current_place != schedule[x][0] and int_time >= start_time and int_time < end_time:
current_place = schedule[x][0]
#Change the channel permissions of the current place to allow viewing
#Change the channel permissions of the last channel to disallow viewing
#client.event
async def on_ready():
print("{0.user} has arrived for duty".format(client))
client.loop.create_task(live_day([
("Home", 0),
("Work", 900),
("Home", 1700),
("Nightclub", 2000),
("Home", 2200),
]))
client.run(my_secret)
Never mind the badly written code, how would I do this or where should I go to figure this out? Any help is appreciated. Thanks!
Edit: I could get the channels individually by using this,
channel1=client.get_channel(channelid)
discord.py, send message by channel id without on_message() event?
but then I can't use this for more than one server. How can I get channels by name?
Note on on_ready():
This function is not guaranteed to be the first event called. Likewise, this function is not guaranteed to only be called once. This library implements reconnection logic and thus will end up calling this event whenever a RESUME request fails.
Using this function may crash the bot.
Instead create background tasks and use wait_until_ready().
You can find examples in https://github.com/Rapptz/discord.py/blob/v1.7.3/examples/background_task.py
If you want to change only one channel's permissions (per guild) this might fit your needs:
#changing "Home" channel visibility, every day at 5 pm.
#client.event
async def on_ready():
while True:
await asyncio.sleep(1)
current_time = datetime.datetime.now()
five_pm = current_time.replace(hour=17, minute=0, second=0)
if current_time == five_pm:
for guild in client.guilds: #looping through all the guilds your bot is in
channel = discord.utils.get(client.get_all_channels(), name="Home") #getting channel by name by using "discord.utils"
await channel.set_permissions(guild.default_role, view_channel=True) #changing permissions
By using the on_ready() event you can loop through the time check without sending any message.
Remember to import discord.utils.

is there another command should i use?

So, I can't use the "?roast" command and "?roast #(user)" at the same time...
#client.event
async def on_ready():
await client.change_presence(status=discord.Status.dnd, activity=discord.Game(' "?" Booe'))
print('Bot is ready to play!')
#client.command()
async def roast(ctx, member:discord.Member):
roast_messages = [
f'{ctx.message.author.mention}You are useless as the UEUE in Queue {member.mention}',
]
await ctx.send(random.choice(roast_messages))
There is always a way to solve that , discord gives us a way like this
#client.command()
async def roast(ctx, member:discord.Member=None):#None is if nothing is given then its None
if member == None:
member = ctx.author
roast_messages = [
f'{ctx.message.author.mention}You are useless as the UEUE in Queue {member.mention}',]
await ctx.send("imagine roasting yourself")#Your Choice Absolutely :)
await ctx.send(random.choice(roast_messages))
here if the member isnt specified , the function is called taking member as none , later gives the none value to the ctx.author itself (dont try to write ctx.author instead of none in the async def roast it gives error as ctx aint defined. also i forgot your roast messages wont make sense then so make sure to edit them too TY :)

Command to spam something discord-bot

so basically I am trying to make a spam command for my discord bot, which takes in a custom message to spam. Here's the code:
#client.command(name='spam')
async def spam(ctx):
global stop
stop = 0
content = ctx.message.content[11:]
if ctx.author.guild_permissions.administrator or ctx.author.id in admins:
if lock == 1:
await ctx.send('Jesus bot is currently locked.')
elif lock == 0:
await ctx.send('Beginning spam..')
while not stop:
await ctx.send(content)
else:
await ctx.send('Sorry, but you do not have admin permissions in this server, or you are not a verified admin.')
For some reason, whenever I try to use this command, the bot doesn't respond. I'm not sure why this happens, and could use some help please.
Picture of bot not responding:
I have a spam command, but I only use it to mess around with my friends. I would not recommend using this as a public command, as you may get rate limited or banned for abuse or something like that. Anyway here is the code I have used for it.
#commands.command()
#commands.is_owner()
# If you want to use admin only, use this below
# #commands.has_permissions(administrator=True)
async def spam(self, ctx, amount, *, word):
int(amount)
await asyncio.sleep(2)
print(f"Starting to spam {word} in {ctx.guild.name}")
await ctx.message.delete()
await ctx.send(f"{ctx.author.mention}\nPlease note that this will clog up the bot's reaction time")
await asyncio.sleep(3)
count = 0
counting=True
while counting:
await ctx.send(word)
count = count + 1
if count == amount:
await asyncio.sleep(2)
await ctx.send("Spam complete")
print(Fore.GREEN + "Spam complete")
counting = False
At the top of your code, make sure you import asyncio as time.sleep will cause the whole bot to pause. Also the Fore.GREEN stuff is just colorama (import colorama).
Try using tasks instead of asyncio. It is made for such repetetive operations and it is easier and nicer because it is made by discord and is included in discord.ext. Something like this:
from discord.ext import tasks
#client.command(name='spam')
async def spam(ctx):
#get message and do all the ifs that you have there
spamLoop.start()
#client.command(name='stopSpam')
async def spamStop(ctx):
# stop the loop
spamLoop.cancel()
#tasks.loop(seconds=1)
async def spamLoop():
print("The message")
Actually quite a simple way of adding spam
import asyncio
#bot.command(name='spam', help= "Spam to your heart's delight")
async def spam(ctx, thing, amount):
count = 0
while count < int(amount):
await ctx.send(thing)
count += 1
if count < amount:
await asyncio.sleep(1)

get channel name and send a message over at that channel

So I am working on a little project here, and pretty much, I want to have one of those "Please type the name of a channel in this server" feature.
So pretty much, the bot asks for a channel name, and I put in for example "#changelog" - and then it will ask for what it should write in that channel, etc etc.
So need to get the channel id (I am guessing), but I don't want users to write the ID, instead only writing the #server-name. And then whenever I have done that, the bot shall write in that channel.
Here is my current code!
class Changelog(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
print('Changelog is loaded')
#commands.command()
async def clhook(self, ctx):
await ctx.send('Write text-channel: ')
text_channel = await self.client.wait_for("message", check=lambda message: message.author == ctx.author, timeout=300)
clhook = self.client.get_channel(text_channel)
def setup(client):
client.add_cog(Changelog(client))
Edit:
The channel ID shall be saved "forever", meaning that I do not have to re-write the channel name where the message should go!
You can use discord.utils.get() with this example:
text_channel = await self.client.wait_for("message", check=lambda message: message.author == ctx.author, timeout=300)
channel = discord.utils.get(ctx.guild.text_channels, name=text_channel)
await channel.send('Bla Bla')
So when you type (prefix)clhook then only the channel name, for example general, it will send Bla Bla to the channel named general .
There is another way to do this and I think it's simple than the first option, here it is:
#commands.command()
async def clhook(self, ctx, channel: discord.TextChannel):
await channel.send('Bla Bla')
So in this command, usage is changed. You can use that with this: (prefix)clhook #general(mention the channel). I suggest this solution and I think it's more useful.
You can use message.channel_mentions. This will return a list of all channels that were mentioned using the #channel-name notation. That way, you can just use channel.id to get the id of the channel they mentioned.
Don't forget, however, to check if the user did in fact tag a channel (which you can also put in your check). I put it in a separate function to make it a bit more readable for the sake of this reply, but you can fit that in your lambda if you really want to.
Also, make sure to check if it's a Text Channel and not a Voice Channel or Category Channel.
#commands.command()
async def clhook(self, ctx):
def check(self, message):
author_ok = message.author == ctx.author # Sent by the same author
mentioned_channel = len(message.channel_mentions) == 1 and isinstance(message.channel_mentions[0], discord.TextChannel)
return author_ok and mentioned_channel
await ctx.send("Write text-channel: ")
text_channel = await self.client.wait_for("message", check=check)
chlhook = text_channel.channel_mentions[0]
I put two conditions on the mentioned_channel line, because if the first one fails, the second one could cause an IndexError. Alternatively you can also use an if-statement to return sooner at that place to solve the same issue.

Resources