I just implemented my very first telegram bot with aiogram. I used the finite state example provided in their API documentation. However, I have a pretty major issue. I also need to integrate webhook. How could I do this without making major changes to everything? My version of the code needs to send an image to the user, which is created by utilising the data that the user inputs.
I need to deploy it on Heroku, so there's that.
Here is the example.
import logging
import aiogram.utils.markdown as md
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters import Text
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.types import ParseMode
from aiogram.utils import executor
logging.basicConfig(level=logging.INFO)
API_TOKEN = 'BOT TOKEN HERE'
bot = Bot(token=API_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
class Form(StatesGroup):
name = State()
age = State()
gender = State()
#dp.message_handler(commands='start')
async def cmd_start(message: types.Message):
await Form.name.set()
await message.reply("Hi there! What's your name?")
#dp.message_handler(state='*', commands='cancel')
#dp.message_handler(Text(equals='cancel', ignore_case=True), state='*')
async def cancel_handler(message: types.Message, state: FSMContext):
current_state = await state.get_state()
if current_state is None:
return
logging.info('Cancelling state %r', current_state)
await state.finish()
await message.reply('Cancelled.', reply_markup=types.ReplyKeyboardRemove())
#dp.message_handler(state=Form.name)
async def process_name(message: types.Message, state: FSMContext):
Process user name
async with state.proxy() as data:
data['name'] = message.text
await Form.next()
await message.reply("How old are you?")
#dp.message_handler(lambda message: not message.text.isdigit(), state=Form.age)
async def process_age_invalid(message: types.Message):
return await message.reply("Age gotta be a number.\nHow old are you? (digits only)")
#dp.message_handler(lambda message: message.text.isdigit(), state=Form.age)
async def process_age(message: types.Message, state: FSMContext):
await Form.next()
await state.update_data(age=int(message.text))
markup = types.ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
markup.add("Male", "Female")
markup.add("Other")
await message.reply("What is your gender?", reply_markup=markup)
#dp.message_handler(lambda message: message.text not in ["Male", "Female", "Other"], state=Form.gender)
async def process_gender_invalid(message: types.Message):
return await message.reply("Bad gender name. Choose your gender from the keyboard.")
#dp.message_handler(state=Form.gender)
async def process_gender(message: types.Message, state: FSMContext):
async with state.proxy() as data:
data['gender'] = message.text
markup = types.ReplyKeyboardRemove()
await bot.send_message(
message.chat.id,
md.text(
md.text('Hi! Nice to meet you,', md.bold(data['name'])),
md.text('Age:', md.code(data['age'])),
md.text('Gender:', data['gender']),
sep='\n',
),
reply_markup=markup,
parse_mode=ParseMode.MARKDOWN,
)
await state.finish()
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Related
I am writing a telegram bot using the aiogram library. I have a FSM module that shows the inline keyboard on every step except the last one. At each step, the keyboard message from the previous step is deleted, but the last message cannot be deleted. This is due to the fact that in the previous steps I use the callback_query_handler handlers, and in the last one I use message_handler.
Is there any way to remove the await callback.message.answer('Введите максимальную цену:') message after the user enters a number?
from aiogram import types, Dispatcher
from create_bot import dp, bot
from keyboards.client_keyboards import city_keyboard, district_keyboard, rooms_keyboard, floor_keyboard
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
import datetime
from databases.users_db import sql_add_users_flat
class FSMAdmin(StatesGroup):
city = State()
district = State()
# neighborhood = State()
rooms = State()
floor = State()
# heating = State()
price = State()
async def cm_start(message: types.Message):
await FSMAdmin.city.set()
await message.answer('Выбери город:', reply_markup=city_keyboard)
# await message.delete()
async def canceling(callback: types.CallbackQuery,
state: FSMContext):
current_state = await state.get_state()
if current_state is None:
return
await state.finish()
await callback.message.answer('ОК')
await callback.answer()
async def city_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['datetime'] = datetime.datetime.now().strftime("%d-%m-%Y %H:%M")
data['name'] = callback.from_user.username
data['city'] = callback.data
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Выбери район:', reply_markup=district_keyboard)
async def district_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['district'] = callback.data
data['neigborhood'] = None # заполнитель для таблицы, пока не написана функция
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Выбери количество комнат:', reply_markup=rooms_keyboard)
async def rooms_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['rooms'] = callback.data
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Выбери этаж:', reply_markup=floor_keyboard)
async def floor_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['floor'] = callback.data
data['heating'] = None # заполнитель для таблицы, пока не написана функция
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Введите максимальную цену:')
# async def heating_choose(callback: types.CallbackQuery,
# state: FSMContext):
# async with state.proxy() as data:
# data['heating'] = callback.data
# await callback.message.delete()
# await FSMAdmin.next()
# await callback.message.answer('Введи макcимальную цену:')
async def max_price_choose(message: types.Message,
state: FSMContext):
async with state.proxy() as data:
try:
data['price'] = int(message.text)
except Exception:
await message.reply('Введи число нормально')
await message.answer(
f"Выбранные параметры:\nГород: {data['city']}\nРайон: {data['district']}"
f"\nКоличество комнат: {data['rooms']}\nЭтаж: {data['floor']}\nМаксимальная цена: {data['price']}")
await message.delete()
try:
await sql_add_users_flat(state)
await message.answer('Выбор сохранен')
except Exception:
await message.answer('Произошла ошибка, изменения не сохранены')
await state.finish()
def register_handlers_survey(dp: Dispatcher):
dp.register_message_handler(cm_start, text='Выбрать параметры', state=None)
dp.register_callback_query_handler(canceling, text='Отменить', state='*')
dp.register_callback_query_handler(city_choose, state=FSMAdmin.city)
dp.register_callback_query_handler(district_choose, state=FSMAdmin.district)
# dp.register_callback_query_handler(neighbohood_choose, state=FSMAdmin.neighbourhood)
dp.register_callback_query_handler(rooms_choose, state=FSMAdmin.rooms)
dp.register_callback_query_handler(floor_choose, state=FSMAdmin.floor)
# dp.register_callback_query_handler(heating_choose, state=FSMAdmin.rooms)
dp.register_message_handler(max_price_choose, state=FSMAdmin.price)
This is my code
User insert some values. Just for not spamming him with many messages, I want to pause sending them in loop and wait user tap inline button. So how can I pause them and wait for user tap "Next result" button? Code beneath just simplified, but works for better understanding scenario.
import os
from aiogram.dispatcher import FSMContext
from aiogram import Bot, Dispatcher, executor, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher.filters.state import StatesGroup, State
bot = Bot(token=os.environ["TOKEN"])
dispatcher = Dispatcher(bot, storage=MemoryStorage())
class Searches(StatesGroup):
by_id = State()
#dispatcher.message_handler(state='*', commands='cancel')
async def cancel_state(message: types.Message, state: FSMContext):
if await state.get_state():
await state.finish()
await message.reply("Search was canceled")
#dispatcher.message_handler(commands="search")
async def answer_search_message(message: types.Message):
find_by_id_button = types.InlineKeyboardButton("по ID", callback_data="by_id")
keyboard = types.InlineKeyboardMarkup().row(find_by_id_button)
await message.answer("Choose search type", parse_mode="HTML", reply_markup=keyboard)
#dispatcher.callback_query_handler(text="by_id")
async def callback_id(callback: types.CallbackQuery):
await callback.message.answer("Insert IDs, split them with ','")
await Searches.by_id.set()
#dispatcher.message_handler(state=Searches.by_id)
async def find_by_id(message: types.Message, state: FSMContext):
ids_from_message = [item for item in range(5)] # get IDs from user message
for index, item in enumerate(ids_from_message):
photo, caption, url = ("https://www.google.ru/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", "some text", "https://www.example.org/") # get some data by id
button_to_site = types.InlineKeyboardButton("More on site", url)
keyboard = types.InlineKeyboardMarkup().row(button_to_site)
if len(ids_from_message) > index + 1: # if this is not last result - show button "Next result"
keyboard.add(types.InlineKeyboardButton("Next result", callback_data="show_next"))
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
# loop should pause here, awaiting for user tap inline button "Next result"
else:
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
await state.finish()
executor.start_polling(dispatcher)
Well, internet, you always helps me, so here what I found as solution for this problem. You can just save some data for user and send it to him, then it trigger button. Full code below.
import os
from aiogram.dispatcher import FSMContext
from aiogram import Bot, Dispatcher, executor, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher.filters.state import StatesGroup, State
def get_record_by_id(doc_id):
# blah-blah some data generating
return "https://www.google.ru/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", "текст документа", "https://www.example.org/"
bot = Bot(token=os.environ["TOKEN"])
storage = MemoryStorage()
dispatcher = Dispatcher(bot, storage=storage)
class Searches(StatesGroup):
by_id = State()
#dispatcher.message_handler(state='*', commands='cancel')
async def cancel_state(message: types.Message, state: FSMContext):
if await state.get_state():
await state.finish()
await message.reply("Search was canceled")
#dispatcher.message_handler(commands="search")
async def answer_search_message(message: types.Message):
find_by_id_button = types.InlineKeyboardButton("by ID", callback_data="by_id")
keyboard = types.InlineKeyboardMarkup().row(find_by_id_button)
await message.answer("Choose search type", parse_mode="HTML", reply_markup=keyboard)
#dispatcher.callback_query_handler(text="by_id")
async def callback_id(callback: types.CallbackQuery):
await callback.message.answer("Insert IDs, split them with ','")
await Searches.by_id.set()
await callback.answer()
#dispatcher.message_handler(state=Searches.by_id)
async def find_by_id(message: types.Message, state: FSMContext):
user_id = message.from_user.id
ids_from_message = [item for item in range(5)]
photo, caption, url = get_record_by_id(ids_from_message[0])
button_to_site = types.InlineKeyboardButton("More on site", url)
keyboard = types.InlineKeyboardMarkup().row(button_to_site)
if len(ids_from_message) > 1:
del ids_from_message[0]
await storage.set_data(user=user_id, data={"ids_to_send": ids_from_message})
keyboard.add(types.InlineKeyboardButton("Next result", callback_data="show_next"))
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
else:
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
await state.finish()
#dispatcher.callback_query_handler(state=Searches.by_id, text="show_next")
async def callback_id(callback: types.CallbackQuery, state: FSMContext):
user_id = callback.from_user.id
chat_id = callback.message.chat.id
data = await storage.get_data(user=user_id)
photo, caption, url = get_record_by_id(data["ids_to_send"][0])
button_to_site = types.InlineKeyboardButton("More on site", url)
keyboard = types.InlineKeyboardMarkup().row(button_to_site)
if len(data["ids_to_send"]) > 1:
del data["ids_to_send"][0]
await storage.set_data(user=user_id, data={"ids_to_send": data["ids_to_send"]})
keyboard.add(types.InlineKeyboardButton("Next result", callback_data="show_next"))
await bot.send_photo(chat_id=chat_id, photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
else:
await bot.send_photo(chat_id=chat_id, photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
await state.finish()
await callback.answer()
executor.start_polling(dispatcher)
I've programmed several Discord bots and am back after a long break. I am currently struggling with stuff that I used to be able to accomplish in no time. I set up my bot and added a single cog, a single test command, and a listener to feel if there is anything new but now I can't get anything to work properly. My prefix commands aren't working, messages don't seem to carry .content anymore and I can't trigger functions in my cogs.
Main file:
import nextcord as discord
from nextcord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
bot = commands.Bot(command_prefix = "!", intents = intents)
#bot.event
async def on_ready():
bot.load_extension("Button Roles")
print("Ready")
#bot.event
async def on_message(message):
if message.author.bot == False:
#await message.channel.send(message.content)
print(message)
#bot.command()
async def ping2(ctx):
print("Pong2!")
await ctx.send("Pong!")
bot.run("Token placeholder")
Cog:
import nextcord as discord
from nextcord.ext import commands
from nextcord import Interaction
class RoleButton(discord.ui.View):
def __init__(self):
super().__init__(timeout = None)
self.value = None
#discord.ui.button(label = "Role1", style = discord.ButtonStyle.green)#Green, Gray, Blurple, Danger
async def Role1(self, button: discord.ui.Button, i: Interaction):
await i.response.send_message("Role 1", ephemeral = True)
self.value = 1
self.stop()
#discord.ui.button(label = "Role2", style = discord.ButtonStyle.danger)#Green, Gray, Blurple, Danger
async def Role2(self, button: discord.ui.Button, i: Interaction):
await i.response.send_message("Role 2", ephemeral = True)
self.value = 2
self.stop()
class Roles(commands.Cog):
def __init__(self, bot):
self.bot = bot
print("Cog ready")
testServerId = 946874409753403422
#commands.command()
async def ping(self, ctx):
await ctx.send("Pong!")
#commands.command()
async def roleMessage(self, ctx):
view = RoleButton()
await ctx.send("You have two options:", view = view)
await view.wait()
if view.i.value == None:
return
elif view.i.value == 1:
print("Role 1!")
elif view.i.value == 2:
print("Role 2!!")
def setup(bot):
bot.add_cog(Roles(bot))
Outputs:
Cog ready
Ready
<Message id=993515538649202748 channel=<TextChannel id=946874410286084129 name='general' position=1 nsfw=False news=False category_id=946874410286084127> type=<MessageType.default: 0> author=<Member id=355727891180290070 name='ℭruenie' discriminator='8426' bot=False nick=None guild=<Guild id=946874409753403422 name="Project" shard_id=0 chunked=True member_count=4>> flags=<MessageFlags value=0>>
Expected Output (Using message.content instead of message):
Cog ready
Ready
'Hi'
#When I use !ping or !ping2 I don't get any outputs/messages
I have enabled the Intents from the Developer Portal.
I checked all codes what I can find but they don't work with my bot.What am I doing wrong?
They’re not much different and they all work, but not in my code. No mistakes in the cmd.
my friend can’t do it either. Bot has administrator privileges (I checked)
role = "NEWS"
#bot.event
async def on_member_join(member):
rank = discord.utils.get(member.guild.roles, name=role)
await member.add_roles(rank)
print(f"{member} was given the {rank} role.")
Second
#bot.event
async def on_member_join( member ):
role = discord.utils.get( member.guild.roles, id = 889869064997068810)
await member.add_roles( role )
Third
#bot.event
async def on_member_join(member):
role = discord.utils.get(member.guild.roles, name='Unverified')
await member.add_roles(role)
All code without this auto role
import random
import json
import discord
import datetime
from lol import an
from zdar import answer_hello
from zdar import hello_words
from zdar import maternie
from zdar import answer_maternie
from discord.ext import commands
from config0 import settings
bot = commands.Bot(command_prefix= settings['prefix'])
bot.remove_command('help')
#bot.command()
async def clear(ctx, amount = 100):
await ctx.channel.purge( limit = amount )
#bot.command()
async def hello(ctx):
await ctx.send(f',{ctx.message.author.mention}!')
#bot.command()
async def cat(ctx):
await ctx.send(random.choice(an))
#bot.command()
async def time (ctx):
emb = discord.Embed(title = 'Титульник', colour = discord.Color.green(), url = 'https://www.timeserver.ru/cities/by/minsk')
emb.set_author(name = bot.user.name, icon_url = bot.user.avatar_url)
emb.set_footer ( text = ctx.author.name, icon_url= ctx.author.avatar_url)
emb.set_image( url= 'https://i.pinimg.com/originals/3f/82/40/3f8240fa1d16d0de6d4e7510b43b37ba.gif')
emb.set_thumbnail( url= 'https://static.wikia.nocookie.net/anime-characters-fight/images/9/90/Eugo_La_Raviaz_mg_main.png/revision/latest/scale-to-width-down/700?cb=20201114130423&path-prefix=ru')
now_date = datetime.datetime.now()
emb.add_field( name = 'Time', value='Time:{}'.format( now_date))
await ctx.send(embed = emb)
bot.run(settings['token'])
You have to specify the member intent to receive on_member_join events. You can do this while creating your bot object by adding an intent option:
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix=settings['prefix'], intents=intents)
Note
If your bot is verified you require the SERVER MEMBERS INTENT which you can request on the bot page of your application.
I am trying to make a bot that once given a command will take away a persons roles and give them back after a certain amount of time. right now im getting the eror unexpected indent.
here is the code:
import discord
from discord.ext import commands
import ctx
import re
import time
from time import sleep
from discord.ext import rolelist, roles, role
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as', self.user)
async def on_message(self, message):
if message.author == self.user:
return
messageContent = message.content
if len(messageContent) > 0:
if re.search("^send.*court$", messageContent):
user_id = message.mentions[0].id
user = message.mentions[0]
await message.channel.send(
f"sending <#{user_id}> to court!"
)
async def listroles(ctx):
user_roles = [role.mention for role in ctx.message.author.roles if role.mentionable]
await message.channel.send(user_roles)
async def roles(ctx):
print(", ".join([str(r.id) for r in ctx.guild.roles]))
role = discord.utils.get(message.guild.roles, name="Jail")
await user.add_roles(role)
async def roles(user.roles):
rolelist = [r.mention for r in user.roles if r != ctx.guild.default_role]
roles = ", ".join(rolelist)
print(rolelist)
client = MyClient()
client.run('TOKEN_GOES_HERE')
Python don't use {} like C# or Java, it works with indentation. So your code should be this:
await user.add_roles(role)
async def get_roles(user):
rolelist = [r.mention for r in user.roles if r != ctx.guild.default_role]
roles = ", ".join(rolelist)
print(rolelist)