I am working on a script to introduce a non blocking async fire and forget call. Thought of using the asyncio for that. I am running a below POC code before get into a full fledge implementation.It appears that the async function is still running on the main thread. Why is the below async function not creating a new thread name/id? I am expecting a new thread id like the threading module generates.
import asyncio
import threading
from threading import Thread
#Asyncio function
async def async_func():
print(f'async_func thread name: %s', threading.current_thread().name)
print(f'async_func thread id: %s', threading.current_thread().ident)
await asyncio.sleep(1)
# Threading
def non_async_func():
print(f'non_async_func thread name: %s', threading.current_thread().name)
print(f'non_async_func thread id: %s', threading.current_thread().ident)
def main():
print(f'main name: %s', threading.current_thread().name)
print(f'main id: %s', threading.current_thread().ident)
asyncio.run(async_func())
t = Thread(target=non_async_func)
print(f'main thread name: %s', threading.current_thread().name)
print(f'main thread id: %s', threading.current_thread().ident)
t.start()
if __name__ == '__main__':
main()
I am getting the below output:
main name: %s **MainThread**
main id: %s **4691527168**
**async_func thread name: %s MainThread**
**async_func thread id: %s 4691527168**
main thread name: %s **MainThread**
main thread id: %s **4691527168**
non_async_func thread name: %s **Thread-1 (non_async_func)**
non_async_func thread id: %s **123145474224128**
Related
I downloaded bot which copy messages from telegram group and sending to my discord channel.
But when I am trying to send a message in a group, I am getting this error.
2022-12-31 08:25:19,877 - telegram.ext.dispatcher - ERROR - No error handlers are registered, logging exception.
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\telegram\ext\dispatcher.py", line 557, in process_update
handler.handle_update(update, self, check, context)
File "C:\Python310\lib\site-packages\telegram\ext\handler.py", line 199, in handle_update
return self.callback(update, context)
File "C:\Discord-Telegram-Bot-main 1\main.py", line 46, in getTgAnnouncement
textUpdate = update.channel_post.text
AttributeError: 'NoneType' object has no attribute 'text'
I am using python 3.10
Python-telegram-bot 13.15
Below i will add the code
import requests
import json
import asyncio
import os
import json
import logging
from dotenv import load_dotenv
import discord
from discord.ext import commands
from telegram import Update
from telegram.ext import Updater,CallbackContext,MessageHandler,Filters
from telegram.utils import helpers
from telegram.ext.dispatcher import run_async
load_dotenv('token.env')
discordToken = os.getenv('DCTOKEN')# discord bot token
telegramToken = os.getenv('TGTOKEN')# telegram bot token
discordChannelId = os.getenv('DCCID')# discord announcement channel id
loop = asyncio.get_event_loop()
#--------------------------------------------------------------------------------------------------------------
bot = commands.Bot(
command_prefix="k!",
case_insensitive=True,
intents=discord.Intents.all(),
help_command=None
)
async def sendDcAnnouncement(textUpdate,nonTextUpdate):
announcementChannel = bot.get_channel(int(discordChannelId))
if textUpdate != None and nonTextUpdate != None:
await announcementChannel.send(textUpdate,file=discord.File(nonTextUpdate))
os.remove(nonTextUpdate)
elif textUpdate == None:
await announcementChannel.send(file=discord.File(nonTextUpdate))
os.remove(nonTextUpdate)
elif nonTextUpdate == None:
await announcementChannel.send(textUpdate)
def getTgAnnouncement(update: Update, context: CallbackContext):
textUpdate = None
nonTextUpdate = None
updateType = helpers.effective_message_type(update)
if updateType == 'text':
textUpdate = update.channel_post.text
else:
textUpdate = update.channel_post.caption
if updateType == 'photo':
nonTextUpdate = update.channel_post.photo[-1].get_file()['file_path']
elif updateType == 'video':
nonTextUpdate = update.channel_post.video.get_file()['file_path']
elif updateType == 'document':
nonTextUpdate = update.channel_post.document.get_file()['file_path']
elif updateType == 'voice':
nonTextUpdate = update.channel_post.voice.get_file()['file_path']
loop.create_task(sendDcAnnouncement(textUpdate,nonTextUpdate))
#bot.event
async def on_ready():
print(f'logged in as {bot.user}')
updater = Updater(token=telegramToken,use_context=True)
dispatcher = updater.dispatcher
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
getTgAnnouncement_handler = MessageHandler((~Filters.command),getTgAnnouncement)
dispatcher.add_handler(getTgAnnouncement_handler)
updater.start_polling()
bot.run(discordToken)
#--------------------------------------------------------------------------------------------------------------
I expect that this bot will send messages and that I will not get these errors.
MessageHandler handles all updates that have one of the fields message, edited_message, channel_post or edited_channel_post. So in getTGAnnouncement, it may very well be the case that update.channel_post is None.
If you want that handler to only handle channel_post update, please use Filters.update.channel_post.
As a side note: I see that you're using python-telegram-bot v13.x. Support for that version is ending soon in favor of the new v20.x.
Disclaimer: I'm currently the maintainer of python-telegram-bot.
I have an async FastApi application with async sqlalchemy, source code:
database.py
from sqlalchemy import (
Column,
String,
)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.decl_api import DeclarativeMeta
from app.config import settings
engine = create_async_engine(settings.DATABASE_URL, pool_per_ping=True)
Base: DeclarativeMeta = declarative_base()
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
class Titles(Base):
__tablename__ = "titles"
id = Column(String(100), primary_key=True)
title = Column(String(100), unique=True)
async def get_session() -> AsyncSession:
async with async_session() as session:
yield session
routers.py
import .database
from fastapi_utils.cbv import cbv
from fastapi_utils.inferring_router import InferringRouter
router = InferringRouter()
async def get_titles(session: AsyncSession):
results = await session.execute(select(database.Titles)))
return results.scalars().all()
#cbv(router)
class TitlesView:
session: AsyncSession = Depends(database.get_session)
#router.get("/titles", status_code=HTTP_200_OK)
async def get(self) -> List[TitlesSchema]:
results = await get_titles(self.session)
return [TitlesSchema.from_orm(result) for result in results]
main.py
from fastapi import FastAPI
from app.routers import router
def create_app() -> FastAPI:
fast_api_app = FastAPI()
fast_api_app.include_router(router, prefix="/", tags=["Titles"])
return fast_api_app
app = create_app()
manage.py
import asyncio
import sys
from .database import async_session, Base, engine
async def init_models():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all, checkfirst=True)
if __name__ == "__main__":
asyncio.run(init_models())
sys.stdout.write("Models initiated\n")
It runs with docker:
python manage.py
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000", "--limit-max-requests", "10000"]
And right after i see message Models initiated, after init_models() func i see couple of warnings:
app_1 | Models initiated
app_1 | /usr/local/lib/python3.9/site-packages/asyncpg/connection.py:131: ResourceWarning: unclosed connection <asyncpg.connection.Connection object at 0x7efe5a613c80>; run in asyncio debug mode to show the traceback of connection origin
app_1 | /usr/local/lib/python3.9/asyncio/sslproto.py:320: ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x7efe5a631700>
app_1 | /usr/local/lib/python3.9/asyncio/selector_events.py:704: ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6>
app_1 | INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
app_1 | INFO: Started reloader process [15] using statreload
app_1 | INFO: Started server process [17]
app_1 | INFO: Waiting for application startup.
app_1 | INFO: Application startup complete.
And after i make changes, i see a bunch of warnings:
app_1 | WARNING: StatReload detected file change in 'ref_info/main.py'. Reloading...
app_1 | INFO: Shutting down
app_1 | INFO: Waiting for application shutdown.
app_1 | INFO: Application shutdown complete.
app_1 | INFO: Finished server process [15]
app_1 | sys:1: ResourceWarning: unclosed file <_io.TextIOWrapper name=0 mode='r' encoding='UTF-8'>
app_1 | INFO: Started server process [16]
app_1 | INFO: Waiting for application startup.
app_1 | INFO: Application startup complete.
Is it ok, and i need to hide it? Or i setted up smth wrong?
Ok, i solved it.
engine = create_async_engine(
settings.DATABASE_ASYNC_URI,
echo="debug" if settings.DEBUG else False,
)
async_session = sessionmaker(
bind=engine,
class_=AsyncSession,
autoflush=True,
autocommit=False,
expire_on_commit=False,
)
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
assert isinstance(session, AsyncSession)
yield session
async def connect() -> None:
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all, checkfirst=True)
async def disconnect() -> None:
if engine:
await engine.dispose()
Add connect and disconnect to your FastApi app startapp and shutdown events
The relevant parts of my code are as follows:
#app.on_event("startup")
async def startup():
es = Translator()
app.state.es_conn = es.connect()
#app.on_event('shutdown')
async def shutdown_event():
es = Translator()
await es.close(app.state.es_conn)
#app.post('/query')
async def query(req: Request):
es = Translator()
es.set_conn(req.app.state.es_conn)
return await es.search()
from elasticsearch import AsyncElasticsearch, AsyncTransport
class Translator(object):
def __init__(self):
self.__es = None
def connect(self):
return AsyncElasticsearch(
hosts='http://192.168.0.2:9200/',
transport_class=AsyncTransport)
def set_conn(self, conn):
self.__es = conn
async def close(self, conn):
await conn.close()
self.__es = None
async def search(self):
return await self.__es.search(index=index, body=body)
It runs in the following environment:
PyPy 7.3.7
fastapi 0.73.0
Elasticsearch 7.10.1
My question are:
Although the code can work normally, how can I detect whether it uses connection pool?
How to use connection pool correctly when asynchronous?
Can anyone help me? Thank you!
i am stuck in my code as i do not know how to input/derive the message_id of the outgoing message forwarded by my bot.
Background: This is just a part of my code which i would subsequently integrate into the main code. Here, i am testing the functionality of forwarding messages + deleting them. I am able to successfully forward them out but i am stuck at deleting them. i am able to give the input of the chat_id but not able to do so for the message_id to delete. Is there a way to do it especially when i am gonna integrate to my main script which can have a few groups to manage. Please assist the noob in me. Thank you!
My script:
import logging
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
TOKEN = "PUT TOKEN HERE" #INPUT HERE
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
MSG, DELETE_MSG = range(2)
def start(update: Update, context: CallbackContext) -> int:
update.message.reply_text(
'Hi! Please post the message you would like to share:')
return MSG
def send(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("Message of %s: %s", user.first_name, update.message.text)
print(update.message.message_id)
send = update.message.forward(chat_id= 'PUT CHAT ID OF OUTGOING GROUP HERE') #INPUT HERE
update.message.reply_text("Please delete")
return DELETE_MSG
def delete_msg(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("edit of %s: %s", user.first_name, update.message.text)
update.message.delete(chat_id='PUT CHAT ID OF OUTGOING GROUP HERE',
message_id='IM STUCK HERE') #INPUT HERE
return ConversationHandler.END
def cancel(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.', reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main() -> None:
updater = Updater(TOKEN, use_context=True)
dispatcher = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
MSG: [MessageHandler(~Filters.command, send)],
DELETE_MSG: [MessageHandler(~Filters.command, delete_msg)]
},
fallbacks=[CommandHandler('cancel', cancel)],
)
dispatcher.add_handler(conv_handler)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
update.message.reply_text("Please delete") must be a variable and, then, you'll be able to context.bot.deleteMessage the message_id of that. Just like this:
must_delete = update.message.reply_text("Please delete")
context.bot.deleteMessage (message_id = must_delete.message_id,
chat_id = update.message.chat_id)
Give it a try and let me know if this worked for you!!
I am trying out the python telegram bot. I am trying to pass the state from button to questionTwo. However, when I run it, it can successfully pass through /start and answerOne. Yet, it will stop at answerOne by displaying it.
It will not display the second question and inline keyboard for me to select the answer, I believe the state didn't pass to questionTwo, but I am not sure which part is wrong. Thank you in advanceb
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove,InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler,CallbackQueryHandler)
answerOne, answerTwo = range(2)
def start(update, context):
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2'),
],
[InlineKeyboardButton("Option 3", callback_data='3')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerOne
def button(update, context):
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text="Selected option: {}".format(query.data))
return questionTwo
def questionTwo(update, context):
keyboard = [
[
InlineKeyboardButton("Option 4", callback_data='4'),
InlineKeyboardButton("Option 5", callback_data='5'),
],
[InlineKeyboardButton("Option 6", callback_data='6')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerTwo
def answerTwo(update, context):
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text="Selected option: {}".format(query.data))
def cancel(update, context):
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main():
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater('TOKEN', use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
answerOne: [CallbackQueryHandler(button)],
questionTwo:[MessageHandler(Filters.text & ~Filters.command, questionTwo)],
answerTwo:[CallbackQueryHandler(answerTwo)],
},
fallbacks=[CommandHandler('cancel', cancel)]
)
dp.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove,InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler,CallbackQueryHandler)
answerOne, questionTwo = range(2)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def start(update, context):
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2'),
],
[InlineKeyboardButton("Option 3", callback_data='3')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerOne
def button(update, context):
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text="Selected option: {}".format(query.data))
return questionTwo
def questionTwo(update, context):
keyboard = [
[
InlineKeyboardButton("Option 4", callback_data='4'),
InlineKeyboardButton("Option 5", callback_data='5'),
],
[InlineKeyboardButton("Option 6", callback_data='6')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerOne
def cancel(update, context):
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main():
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater('Token', use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
answerOne: [CallbackQueryHandler(button)],
questionTwo:[MessageHandler(Filters.text & ~Filters.command, questionTwo)]
},
fallbacks=[CommandHandler('cancel', cancel)]
)
dp.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()