How to make Dispatcher work for all aiogram tokens - python

import logging
from aiogram import Bot, types
from aiogram.contrib.middlewares.logging import LoggingMiddleware
from aiogram.dispatcher import Dispatcher
from aiogram.dispatcher.webhook import SendMessage
from aiogram.utils.executor import start_webhook
API_TOKEN = ''
# webhook settings
WEBHOOK_HOST = 'https://xxxxx211.eu.ngrok.io'
WEBHOOK_PATH = ''
WEBHOOK_URL = f"{WEBHOOK_HOST}{WEBHOOK_PATH}"
# webserver settings
WEBAPP_HOST = 'localhost' # or ip
WEBAPP_PORT = 5000
logging.basicConfig(level=logging.INFO)
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
dp.middleware.setup(LoggingMiddleware())
#dp.message_handler()
async def echo(message: types.Message):
# Regular request
# await bot.send_message(message.chat.id, message.text)
# or reply INTO webhook
return SendMessage(message.chat.id, message.text)
async def on_startup(dp):
await bot.set_webhook(WEBHOOK_URL.format(token=API_TOKEN))
with bot.with_token("token"):
await bot.set_webhook(WEBHOOK_URL.format(token="token"))
with bot.with_token("token2"):
await bot.set_webhook(WEBHOOK_URL.format(token="token2"))
async def on_shutdown(dp):
logging.warning('Shutting down..')
# insert code here to run it before shutdown
# Remove webhook (not acceptable in some cases)
await bot.delete_webhook()
with bot.with_token("token"):
await bot.delete_webhook()
with bot.with_token("token2"):
await bot.delete_webhook()
# Close DB connection (if used)
await dp.storage.close()
await dp.storage.wait_closed()
logging.warning('Bye!')
if __name__ == '__main__':
start_webhook(
dispatcher=dp,
webhook_path=WEBHOOK_PATH,
on_startup=on_startup,
on_shutdown=on_shutdown,
host=WEBAPP_HOST,
port=WEBAPP_PORT,
)
Here I took the code for multibot from the FAQ aiogram gabbhack repository But there is a problem with the Dispatcher there is a concept
Major token and minor
Because of this, when sending code using await message.answer and the like on the aiogram dock using the second bot, the answer comes to the first bot
And the problem is solved with the help of return SendMessage but this is not an option because 2 requests or more cannot be sent and asynchrony is lost
How can I make it so that the Dispatcher is for each token separately (tokens in an unlimited number)
So that you can turn to #dp and await message.answer and the answer comes to this very bot

Related

Why telegram-bot on Python with Webhooks can't process messages from many users simultaneously unlike a bot with Long Polling?

I use aiogram. Logic of my bot is very simple - he receive messages from user and send echo-message after 10 seconds. This is a test bot, but in general, I want to make a bot for buying movies with very big database of users. So, my bot must be able to process messages from many users simultaneously and must receive messages using Webhooks. Here are two python scripts:
Telegram-bot on Long Polling:
import asyncio
import logging
from aiogram import Bot, Dispatcher, executor, types
from bot_files.config import *
# Configure logging
logging.basicConfig(level=logging.INFO)
# Initialize bot and dispatcher
bot = Bot(token=bot_token)
dp = Dispatcher(bot)
#dp.message_handler()
async def echo(message: types.Message):
await asyncio.sleep(10)
await message.answer(message.text)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Telegram-bot on Webhooks:
import asyncio
import logging
from aiogram import Bot, Dispatcher, executor, types
from bot_files.config import *
# Configure logging
logging.basicConfig(level=logging.INFO)
# Initialize bot and dispatcher
bot = Bot(token=bot_token)
dp = Dispatcher(bot)
WEBHOOK_HOST = f'https://7417-176-8-60-184.ngrok.io'
WEBHOOK_PATH = f'/webhook/{bot_token}'
WEBHOOK_URL = f'{WEBHOOK_HOST}{WEBHOOK_PATH}'
# webserver settings
WEBAPP_HOST = '0.0.0.0'
WEBAPP_PORT = os.getenv('PORT', default=5000)
async def on_startup(dispatcher):
await bot.set_webhook(WEBHOOK_URL, drop_pending_updates=True)
async def on_shutdown(dispatcher):
await bot.delete_webhook()
#dp.message_handler()
async def echo(message: types.Message):
await asyncio.sleep(10)
await message.answer(message.text)
if __name__ == '__main__':
executor.start_webhook(
dispatcher=dp,
webhook_path=WEBHOOK_PATH,
skip_updates=True,
on_startup=on_startup,
on_shutdown=on_shutdown,
host=WEBAPP_HOST,
port=WEBAPP_PORT
)
In the first case, if two users send messages simultaneously, both of messages are processed also simultaneously(acynchrony) - 10 seconds. In the second case messages are processed linearly(not asynchrony) - one of two users must wait 20 seconds. Why telegram-bot on Python with Webhooks can't process messages from many users simultaneously unlike a bot with Long Polling?
Actually telegram-bot on Python with Webhooks can process messages from many users simultaneously. You need just to put #dp.async_task after handler
#dp.message_handler()
#dp.async_task
async def echo(message: types.Message):
await asyncio.sleep(10)
await message.answer(message.text)

How to get updates from webhook telegram bot with python

Trying to connect Dialogflow to telegram bot to use it with webhook, to avoid using .json file credentials.
A part of code from bot.py
from aiogram.dispatcher.webhook import SendMessage
API_HOST = os.getenv("API_HOST")
BOT_TOKEN = os.getenv("BOT_TOKEN")
APP_NAME = os.getenv("APP_NAME")
WEBHOOK_HOST = f"https://{APP_NAME}.herokuapp.com"
WEBHOOK_PATH = f"/webhook/{BOT_TOKEN}"
WEBHOOK_URL = f"{WEBHOOK_HOST}{WEBHOOK_PATH}"
WEBAPP_HOST = "0.0.0.0"
WEBAPP_PORT = int(os.environ.get("PORT", "5000"))
logging.basicConfig(level=logging.INFO)
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(bot, storage=MemoryStorage())
async def scheduler():
"""Scheduler for sending messages."""
# aioschedule.every(10).seconds.do(prepare_message)
# keep in mind that this is remote server time
aioschedule.every().day.at("17:00").do(prepare_message)
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
async def on_startup(dispatcher):
"""Startup function."""
# add commands to MENU button
await set_commands(bot)
asyncio.create_task(scheduler())
if not DEVELOP:
await bot.set_webhook(url=WEBHOOK_URL)
async def on_shutdown(dispatcher):
"""Shutdown function."""
await bot.delete_webhook()
#dp.message_handler(content_types=["text"])
async def reply_to_user(message: types.Message):
# this code should reply to user with data from a webhook, but I do not khow how to retrieve it
return SendMessage(message.chat.id, message.text)
if __name__ == "__main__":
if DEVELOP:
executor.start_polling(dispatcher=dp, skip_updates=True, on_startup=on_startup)
else:
executor.start_webhook(
dispatcher=dp,
webhook_path=WEBHOOK_PATH,
on_startup=on_startup,
on_shutdown=on_shutdown,
skip_updates=True,
host=WEBAPP_HOST,
port=WEBAPP_PORT,
)
Bot hosted on Heroku, it works, but but I do not khow how to retrieve payload from webhook url. Aiogram docs tells to use from aiogram.dispatcher.webhook import SendMessage and nothing more.

I want my bot to process commands sent by other bots

This isn't something most people want, but I do.
Code:
#imports
import discord
import os
from keep_alive import keep_alive
from discord.ext.commands import has_permissions, MissingPermissions
from discord.ext import commands
from discord.utils import get
#client name
client = discord.Client()
#log-in msg
#client.event
async def on_ready():
print("Successfully logged in as")
print(client.user)
#prefix and remove default help cmd
client = commands.Bot(command_prefix='H')
client.remove_command("help")
#client.command(pass_context=True)
async def ere(ctx, *, args=None):
await ctx.send("hi")
if discord.utils.get(ctx.message.author.roles, name="MEE6") != None:
if args != None:
await ctx.send("mee6 just spoke!")
else:
await ctx.send("nope")
#client.event
async def on_message(message):
print(message.author)
if "Here" in message.content:
if discord.utils.get(message.author.roles, name="MEE6") != None:
channel = await client.fetch_channel(870023245892575282)
await channel.send("yes")
await client.process_commands(message)
I figured adding "await client.process_commands(message)" to the bottom of on_message would process commands sent by other bots such as MEE6 but no luck. It appears by default on_message can hear bots but commands can not. Any way to get around this?
Any help would be greatly appreciated!
It is a feature of the Bot class to ignore other bot's messages, but #Daniel O'Brien solved it in this thread. The solution is to subclass the bot and override the function which ignores other bots, like this:
class UnfilteredBot(commands.Bot):
"""An overridden version of the Bot class that will listen to other bots."""
async def process_commands(self, message):
"""Override process_commands to listen to bots."""
ctx = await self.get_context(message)
await self.invoke(ctx)
Use ID
for commands:
# from discord.ext import commands as cmds
def is_mee6():
def predicate(ctx: cmds.Context):
return ctx.message.author.id == 159985870458322944:
# 159985870458322944 is ID of MEE6
return cmds.check(predicate)
# Use like it:
# #cmds.command()
# #is_mee6()
# async def ...
for events:
# from discord.ext import commands as cmds
mee6_id = 159985870458322944
# and use it for checks. for Example
#cmds.event
async def on_message(message):
if message.author.id == mee6_id:
# ANY

Chatbot in Discord (W. Python)

I am making a Discord bot with Python.
I would like to add a feature in where, there is a channel called "chatbot", and any text typed there would be replied to by Cleverbot
I am currently trying with the cleverbotfree library, but it doesn't work and I can't find good documentation on it. My problem is that I am trying to run this async command:
#CleverbotAsync.connect
async def async_chat(bot, message, user_input, bot_prompt):
"""Example code using cleverbotfree async API with decorator."""
reply = await bot.single_exchange(user_input)
await message.channel.send(f'{0}'.format(reply))
await bot.close()
However, I can't do this in on_message, because it needs to be run in this snippet of code:
if chnl == 'chatbot':
#whatever running that command
So, I'm not sure how do it.
And yes, I have looked into and tried Selenuim, but it appears that whenever I click the accept button, nothing loads. (This must be a way for Cleverbot to make people actually pay for their API, which I can't do).
Here is my full code(I know it's not the best):
import discord
from discord import channel
from discord.flags import MessageFlags
from webserver import keep_alive
import os
from discord.ext import commands
import youtube_dl
import time
import asyncio
from cleverbotfree import CleverbotAsync
from cleverbotfree import Cleverbot
requests = []
def send(message,user_input,bot_prompt):
asyncio.run(async_chat(message,user_input,'Cleverbot:'))
client = commands.Bot(command_prefix="!")
#client.command()
async def request(ctx, request: str,member: discord.Member):
requests.append('{0}: {1}'.format(member,request))
await ctx.send(f'Thanks {member.metion} for the request, my dev will (hopefully) get right to work!')
#client.command(description="See what my dev is working on")
async def workingon(ctx):
ctx.send("My Dev is currently working on adding a feature in where, in a channel called 'chatbot', it will respond like a human.(though he has spent hours on it, it still doesn't work)")
#client.event
async def on_ready():
#confirming login
print('Logged on as {0.user}'.format(client))
#client.event
async def on_member_join(member):
print(f'Greetings {member}, welcome to server')
#CleverbotAsync.connect
async def async_chat(bot, message, user_input, bot_prompt):
"""Example code using cleverbotfree async api with decorator."""
reply = await bot.single_exchange(user_input)
await message.channel.send(f'{0}'.format(reply))
await bot.close()
#client.event
async def on_message(message):
#getting variables
usrnm = str(message.author).split('#')[0]
msg = str(message.content)
chnl = str(message.channel.name)
if usrnm != str(client.user).split('#')[0] and msg.lower() == 'hello bot':
await message.channel.send(f'Hello {usrnm}, how do you do?')
return
if str(msg).split(' ')[0].lower() == 'im':
print(str(msg).split(' ')[0].lower())
await message.channel.send(f'Hi, {msg}, im a dad!')
if msg.lower() == '!newfeatures':
await message.channel.send(f'{message.author.mention}, The newest feature added to the bot is that if a new member joins, it will greet them')
if msg.lower() == '!help':
await message.channel.send(f'{message.author.mention}, Type !help for help. \n\n - Typing "Hello Bot" will make the bot answer\n - typing "im" before anything will make the bot answer\n - Typing !newfeatures will tell you the newest feature added to the bot.')
if chnl == 'chatbot':
send(message,str(message.content),'Cleverbot:')
#muting system
keep_alive()
TOKEN = os.environ.get("DISCORD_BOT_SECRET")
client.run(TOKEN)
Any help would be appreciated!

Running Flask & a Discord bot in the same application

I am building a Discord bot in Python and would like to receive HTTP requests from Twitch.tv's API (See Webhooks Guide & Webhooks Reference) (To subscribe to events like; X streamer has gone live) and based on the content of the HTTP (POST or GET) request received from Twitch, do something on the Discord bot, e.g: Output a message on a text channel.
I am using the discord.py Python Discord API/Library.
I've looked into the matter and found that Flask seemed like a good minimalist choice for a webserver to receive these requests on.
I should preface this by saying I'm very new to Python and I've never used Flask before.
Now. The problem is I can't seem to figure out a way to run the Flask server inside of my discord bot.
I've tried adding this simple code into my discord.py script:
from flask import Flask, request
app = Flask(__name__)
#app.route('/posts', methods=['POST'])
def result():
print(request.form['sched'])
# Send a message to a discord text channel etc...
return 'Received !'
When I run my discord.py script which looks something like this:
(Stripped away some commands and features for the sake of keeping this shorter)
import discord
import asyncio
from flask import Flask, request
app = Flask(__name__)
#app.route('/posts', methods=['POST'])
def result():
print(request.form['sched'])
# Send a message to a discord text channel etc...
return 'Received !'
client = discord.Client()
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
#client.event
async def on_message(message):
if message.author == client.user:
return
content = message.content
fullUser = message.author.name+'#'+message.author.discriminator
print(str(message.timestamp)+" #"+message.channel.name+" "+fullUser+": "+str(content.encode('ascii', 'ignore').decode('ascii')))
if content.startswith('!'):
content = content[1:]
if content.startswith('test'):
counter = 0
tmp = await client.send_message(message.channel, 'Calculating messages...')
async for log in client.logs_from(message.channel, limit=100):
if log.author == message.author:
counter += 1
await client.edit_message(tmp, 'You have {} messages.'.format(counter))
client.run('MyTokenHere')
It seems like if I point flask to discord.py (the above) and run it, it'll start the code, get to the "client.run('MyTokenHere')" part for discord, and just stop at that and run the discord bot. It's not until I exit out of the bot by doing Ctrl+C that the actual Flask server starts, but now the discord bot is disconnected and no longer does any processing.
The same problem persists if I were to for example add "app.run()" somewhere in my code (before calling "client.run()" which starts the Discord bot part) to launch the Flask server; It'll just run the flask, get stuck on that until I Ctrl+C out of the Flask server, then it'll proceed to start the Discord bot.
Ultimately, I need to use the Discord API and I need to be connected to the Discord API gateway and all that good jazz to actually send messages to a channel with the bot, so I don't really know what to do here.
So. I think I've tried my best to explain what I'm ultimately trying to achieve here, and hopefully someone can help me find a way to either make this work with Flask, or if there's a better and easier way, provide a different solution.
This is cog example in discord.py
I made this thing for dbl (Discord Bot Lists) you can implement, the thing you need.
Note: run your heroku app with webprocess
example :
web: python main.py
Then go on https://uptimerobot.com/
and setup to ping your web app after every 5 minutes
from aiohttp import web
from discord.ext import commands, tasks
import discord
import os
import aiohttp
app = web.Application()
routes = web.RouteTableDef()
def setup(bot):
bot.add_cog(Webserver(bot))
class Webserver(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.web_server.start()
#routes.get('/')
async def welcome(request):
return web.Response(text="Hello, world")
#routes.post('/dbl')
async def dblwebhook(request):
if request.headers.get('authorization') == '3mErTJMYFt':
data = await request.json()
user = self.bot.get_user(data['user']) or await self.bot.fetch_user(data['user'])
if user is None:
return
_type = f'Tested!' if data['type'] == 'test' else f'Voted!'
upvoted_bot = f'<#{data["bot"]}>'
embed = discord.Embed(title=_type, colour=discord.Color.blurple())
embed.description = f'**Upvoter :** {user.mention} Just {_type}' + f'\n**Upvoted Bot :** {upvoted_bot}'
embed.set_thumbnail(url=user.avatar_url)
channel = self.bot.get_channel(5645646545142312312)
await channel.send(embed=embed)
return 200
self.webserver_port = os.environ.get('PORT', 5000)
app.add_routes(routes)
#tasks.loop()
async def web_server(self):
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, host='0.0.0.0', port=self.webserver_port)
await site.start()
#web_server.before_loop
async def web_server_before_loop(self):
await self.bot.wait_until_ready()
As the kind commenters informed me; threading seems like the way to go.
Thanks guys!
Another cool way if you aren't going to use flask extensions, use quart instead of flask, then it will super easy for you.
Note: Run your heroku app with webprocess
example :
web: python main.py
Then go on https://uptimerobot.com/ and setup to ping your web app after every 5 minutes
# Code Example
from discord.ext import commands
from quart import Quart
import os
app = Quart(__name__)
bot = commands.Bot('!')
#bot.command()
async def something(ctx):
...
"""
Note: On Heroku you cant bind your webserver with 5000 port as they aren't static.
To fix above problem you will have to get dynamic port from the environment variable and you are good to go.
"""
PORT = os.environ.get('PORT')
bot.loop.create_task(app.run_task('0.0.0.0', PORT))
bot.run('Token')
Or you can use the Terminal Multiplexer, tmux to run them independently!.
If you are running on a Linux platform, tmux python3 flaskapp.py would run the flask app, while you can independently run the discord bot.
You can use asyncio with flask to perform this
import discord
import asyncio
class PrintDiscordChannelsClient(discord.Client):
def __init__(self, *args, **kwargs):
self.guild_id = kwargs.pop('guild_id')
super().__init__(*args, **kwargs)
async def on_ready(self):
try:
await self.wait_until_ready()
guild = self.get_guild(int(self.guild_id))
print(f'{guild.name} is ready!')
channels = guild.text_channels
print(f'{(channels)} channels found')
await self.close()
except Exception as e:
print(e)
await self.close()
async def print_discord_channels(guild_id):
client = PrintDiscordChannelsClient(guild_id=guild_id)
await client.start('DISCORD_BOT_TOKEN')
#application.route("/discord-api", methods=["GET"])
def discord_api():
guild_id = request.args.get('guild_id')
asyncio.run(print_discord_channels(guild_id=guild_id))
return make_response("Success", 200)

Categories

Resources