How to make my telegram-bot multi-threading? - async-await

I have a trouble. I need make my telegram-bot multi-threading. My bot will help users to buy films and will work with database. I use Webhooks-method for receiving requests from Telegram-server and Stripe(module request). I read a lot about threading module in python and about async functions but I am not sure for all 100% about how to make my bot multi-threading. I will very appreciated for help, because I am stuck on this question.
For now I give you main function of my app, if you need more, tell me:
#app.route('/', methods=["POST"])
def process():
print(request.json) # receiving requests (messages) in json format that are sent to the Flask server from the Telegram server and Stripe
if check_if_successful_payment(request) == True:
# Processing a request from Stripe
# chat_id = request.json["data"]["object"]["metadata"]["chat_id"]
stripe.api_key = get_from_env("PAYMENT_TOKEN")
webhook_list = stripe.WebhookEndpoint.list()
chat_id = webhook_list.data[0].metadata.chat_id
send_message(chat_id, "The payment was successful! Enjoy watching the movie!")
print("The payment was successful!")
webhook_id = webhook_list.data[0].id
stripe.WebhookEndpoint.delete(
webhook_id,
)
else:
# Processing a request from Telegram
chat_id = request.json["message"]["chat"]["id"]
send_message(chat_id, check_message(chat_id, request.json["message"]["text"]))
send_pay_button(chat_id=chat_id, text="Test payment",
price_id=check_price_id(request.json["message"]["text"]))
return {"ok": True}
if __name__ == '__main__':
app.run(debug=True)

If your bot works on Webhooks, you can use Aiogram instead of Flask for receiving messages from users. Aiogram has special decorator for simultaneous messages processing from many users - async_task: https://docs.aiogram.dev/en/latest/_modules/aiogram/dispatcher/dispatcher.html#Dispatcher.async_task

Related

'AsyncTeleBot' object has no attribute 'register_next_step_handler'

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.

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

Discord dm bot that can dm back

I am trying to make a donation bot, where in the channel when a person types !donate the bot will Dm them.
I got all that down but I am trying to make so that the person decides on how to donate like !cashapp, !paypal, etc., in the dms. It would send them that specific way to send money, for example once the bot Dms the use user what service they would like to pay with and the user says !cashapp it will send another message with my cashtag, or if they say !paypal it would send my paypal link.
Here's a simple example:
import discord
from discord.ext import commands
client = commands.Bot(command_prefix = "!")
#client.command()
async def donate(ctx, paymentMethod: str):
if (paymentMethod.lower() == "paypal"):
await ctx.send("PayPal link: ...")
elif (paymentMethod.lower() == "cashapp"):
await ctx.send("Cashapp Link: ...")
else:
await ctx.send("The provided payment method is not available.")
#donate.error
async def donate_error(ctx, error):
if isinstance(error, commands.errors.MissingRequiredArgument):
await ctx.send("Incorrect use of command!")
client.run("your bot token here")
Usage:
!donate {payment method} - Without the '{}'
Check the documentation for more information.
You can use user.send to send a message to the user dm and use it in the same way as ctx.send and channel.send.
await ctx.author.send("paypal link: ...") # ctx.author is the member that call the command
And 2 command will have different function so you just need to write both of them in a different way.
If you planning to make it into 1 command you can use if else check
#client.command(pass_context=True)
async def steam(ctx):
if ctx.author.send():
await ctx.send("you not boster")
else:
await ctx.author.send(random.choice(list(open('steam.txt'))))
i want if someone send a dm
its not Returns a dm
someone know how to do this?

How to make a Reaction role command with discord.py

So I'm trying to make a Reaction role via discord.py but i don't know how to do it. Also there is no tutorial on YT, I want the bot to send a message and react to that message so the user when react to that get the role.
Okay,
This uses cogs and a bot instead of a client.
message = await ctx.send(" Click the ✅ reaction to verify yourself")
This piece sends the message (You can change it to whatever you want).
if not get(ctx.guild.roles, name="Verified"):
perms = discord.Permissions()
perms.general()
perms.text()
perms.voice()
await ctx.guild.create_role(name="Verified", permissions=perms, colour=discord.Colour(0x00bd09))
This piece will just check whever the role exists or not. If it doesnt it will create one.
await message.add_reaction("✅")
This adds this reaction to the message(So the user doesnt have to scroll around for it). If you dont want the bot have the role, you can add a check (If the user is a bot or not) in the on_raw_reaction_add() function.
#commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
guild = self.bot.get_guild(payload.guild_id)
role = discord.utils.get(guild.roles, name="Verified")
await payload.member.add_roles(role, reason="Verified Self", atomic=True)
This is the function to check when someone adds a reaction to the message. We want to use on_raw_reaction_add() instead of on_reaction_add() because the second one check the cache for adding a reaction, and if the thing isnt in chache it wont work. Thus the 1st one is less probable to create problems.
Here's the whole piece.
from discord.ext import commands
from discord.utils import get
import discord
from discord.ext.commands import has_permissions, CheckFailure
class Verification(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(name='generate_verification', help='Generates verification message')
async def generate_verification(self, ctx):
message = await ctx.send(" Click the ✅ reaction to verify yourself")
verification_message_id = message.id
# Check if Verification role already exists
if not get(ctx.guild.roles, name="Verified"):
perms = discord.Permissions()
perms.general()
perms.text()
perms.voice()
await ctx.guild.create_role(name="Verified", permissions=perms, colour=discord.Colour(0x00bd09))
await message.add_reaction("✅")
#commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
guild = self.bot.get_guild(payload.guild_id)
role = discord.utils.get(guild.roles, name="Verified")
await payload.member.add_roles(role, reason="Verified Self", atomic=True)
def setup(bot):
bot.add_cog(Verification(bot))
If you're creating a verification command, then you probably would like to store the amount of verification messages sent in a file. This would stop people from generating the message a lot of times. To this you also probably want a command to clear the data from the file, so if the adming wants to create the message again he just needs to clear the file.

How to make a restart command for my discord.py bot

so far what I've got is:
def restart_program():
python = sys.executable
os.execl(python, python, * sys.argv)
#bot.command()
async def restart(ctx):
message = await ctx.send("Restarting... Allow up to 5 seconds")
restart_program()
This works, however I'm wondering how I can make it edit the "Restarting... Allow up to 5 seconds" message to say something like "Bot is back up" after it's restarted. Is this possible, and if so, how could I do this?
I would use Client.logout() and Client.login() to restart a bot instead of doing it with os.execl, but if you really need to do it this way, here is a how you can send a message every time it is started:
#client.event
async def on_ready():
# First get the channel where the message should be sent
channel = discord.utils.get(client.get_all_channels(), name='general')
await channel.send("Bot is back up!")

Resources