ConversationHandler State did not pass in Python telegram bot - python-telegram-bot

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()

Related

How to fix "ERROR - No error handlers are registered, logging exception." with telegram.ext.dispatcher?

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.

How to connect VUE 3.x frontend websocket to DJANGO 4.x consumer using DJANGOCHANNELSRESTFRAMEWORK?

I have a DJANGO project with the following files.
Basically I am using token authentication in my settings which works well for javascript fetch/axios since I can send the token in the headers. I use djoser to manage the token authentication; however when I try to run any custom action in my consumer.py file the user in self.scope.get('user') is always AnonymousUser and then it returns with an 'Not allowed' message.
Do I need to authenticate the user over websocket protocol?
Also worth noting. I am using DJANGOCHANNELSRESTFRAMEWORK and using DEMULTIPLEXER to handle the websocket/consumer routing.
UPDATE: I tried using using the 'list' action in my json data and in consumers.py I user permissions.AllowAny and it works as expected; however I still cannot use any of my own custom actions.
UPDATE 2: When using permissions.AllowAny I can actually use all my custom actions as well; however I'm not sure if this is safe. I would like for my user to be authenticated to do this through websocket in this DJANGO decoupled project.
SETTINGS.PY
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-=1l1i2&3mw2!5^zhchewl#ziv1=ip0i$jc+8n7ikryjufwsh9e'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['http://127.0.0.1:8000','127.0.0.1:8000','127.0.0.1','http://localhost:8080','ws://localhost:8080']
CORS_ALLOWED_ORIGINS = (
'http://127.0.0.1:8000',
'http://localhost:8080'
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.BasicAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
],
'DEFAULT_PERMISSION_CLASSES':[
'rest_framework.permissions.IsAuthenticated'
]
}
CSRF_TRUSTED_ORIGINS=['http://127.0.0.1:8000','http://localhost:8080']
AUTH_USER_MODEL='users.User'
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'djoser',
'corsheaders',
'daphne',
'channels',
'users',
'students',
'instructors',
'groups',
'materials',
'operation',
'channels_demultiplexer',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
# 'middleware.websocket_auth.TokenAuthMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'InterAct.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR,'templates/'),
# os.path.join(BASE_DIR,'frontend/','dist/','templates/'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
# WSGI_APPLICATION = 'InterAct.wsgi.application'
ASGI_APPLICATION = 'InterAct.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
},
},
}
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
MEDIA_URL='/media/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = 'static/'
# STATIC_ROOT= os.path.join(BASE_DIR,'publick/static/')
# STATICFILES_DIRS = [
# BASE_DIR / "static",
#
# ]
# CORS_ORIGIN_ALLOW_ALL = True
ASGI.PY
#ASGI config for InterAct project.
#It exposes the ASGI callable as a module-level variable named ``application``.
#For more information on this file, see
#https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
import os
from channels.auth import AuthMiddlewareStack
from middleware.websocket_auth import TokenAuthMiddleware
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
import operation.routing
from django.urls import re_path
from operation.consumers import DMultiplexer
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'InterAct.settings')
django_application = get_asgi_application()
application = ProtocolTypeRouter(
{
"http": django_application,
"websocket": AllowedHostsOriginValidator(
# TokenAuthMiddleware(URLRouter([
AuthMiddlewareStack(URLRouter([
# operation.routing.websocket_urlpatterns,
# re_path(r"^/$", DMultiplexer.as_asgi()),
re_path(r"^ws/$", DMultiplexer.as_asgi()),
]))
),
}
)
CONSUMERS.PY
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
from users.models import User
from users.serializers import UserSerializer
from channels.auth import login as channels_login
from django.contrib.auth import login
from instructors.models import Instructor
from instructors.serializers import InstructorSerializer
from groups.models import Group
from groups.serializers import GroupSerializer
from students.models import Student
from students.serializers import StudentSerializer
from djangochannelsrestframework.decorators import action
from djangochannelsrestframework.observer import model_observer
from djangochannelsrestframework import permissions
from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin
from djangochannelsrestframework.mixins import (
ListModelMixin,
RetrieveModelMixin,
PatchModelMixin,
UpdateModelMixin,
CreateModelMixin,
DeleteModelMixin,
)
from channels.db import database_sync_to_async
import secrets
class Instructors_Consumer(
ListModelMixin,
# RetrieveModelMixin,
PatchModelMixin,
UpdateModelMixin,
CreateModelMixin,
DeleteModelMixin,
ObserverModelInstanceMixin,
GenericAsyncAPIConsumer):
permission_classes = (permissions.AllowAny,)
queryset=Instructor.objects.all()
serializer_class=InstructorSerializer
#action()
async def subscribe_to_instructors_feed(self, request_id, **kwargs):
print('are we all getting called?')
await self.instructors_feed.subscribe(request_id=request_id)
#model_observer(Instructor,serializer_class=InstructorSerializer)
async def instructors_feed(self, data, action, subscribing_request_ids=[], **kwargs):
# print(data,action,subscribing_request_ids)
for request_id in subscribing_request_ids:
await self.reply(data=data, action=action, request_id=request_id)
#action()
async def create_model(self, *args, **kwargs):
data=kwargs.get('data')
object=await self.create_model_db(data)
return object,200
#database_sync_to_async
def create_model_db(self,data):
print(data)
email=f'{secrets.SystemRandom().randint(1,1000000)}#{secrets.SystemRandom().randint(1,1000000)}.com'
username=secrets.SystemRandom().randint(1,1000000)
new_user=User.objects.create(first_name='first_name',last_name='last_name',role="Instructor",email=email,username=username)
new_instructor=new_user.instructor_set.first()
new_instructor=InstructorSerializer(new_instructor).data
print(new_user)
print(new_instructor)
return new_instructor
class Groups_Consumer(
ListModelMixin,
# RetrieveModelMixin,
PatchModelMixin,
UpdateModelMixin,
CreateModelMixin,
DeleteModelMixin,
ObserverModelInstanceMixin,
GenericAsyncAPIConsumer):
permission_classes = [permissions.AllowAny]
queryset=Group.objects.all()
serializer_class=GroupSerializer
#action(detail=False)
async def subscribe_to_groups_feed(self, request_id, **kwargs):
print('hello from another domain2222')
print(request_id)
print(dir(self))
# [print(thing) for thing in dir(self)]
print(self.scope.get('user'))
# login(request, user) # <- This was missing
await self.groups_feed.subscribe(request_id=request_id)
#model_observer(Group,serializer_class=GroupSerializer)
async def groups_feed(self, data, action, subscribing_request_ids=[], **kwargs):
for request_id in subscribing_request_ids:
await self.reply(data=data, action=action, request_id=request_id)
#action()
async def create_and_add_student(self, *args, **kwargs):
data=kwargs.get('data')
new_group=await self.create_and_add_student_db(data)
return new_group,200
#database_sync_to_async
def create_and_add_student_db(self,data):
pk=int(data.get('pk'))
group=Group.objects.get(pk=pk)
email=f'{secrets.SystemRandom().randint(1,1000000)}#{secrets.SystemRandom().randint(1,1000000)}.com'
username=secrets.SystemRandom().randint(1,1000000)
first_name=secrets.SystemRandom().randint(1,1000000)
new_user=User.objects.create(first_name=first_name,role="Student",email=email,username=username)
new_student=new_user.student_set.first()
group.student.add(new_student)
group.save()
new_student=StudentSerializer(new_student).data
response={
'new_student':new_student,
'parent':pk,
}
return response
class Students_Consumer(
ListModelMixin,
# RetrieveModelMixin,
PatchModelMixin,
UpdateModelMixin,
CreateModelMixin,
DeleteModelMixin,
ObserverModelInstanceMixin,
GenericAsyncAPIConsumer):
permission_classes = (permissions.AllowAny,)
queryset=Student.objects.all()
serializer_class=StudentSerializer
#action()
async def subscribe_to_students_feed(self, request_id, **kwargs):
await self.students_feed.subscribe(request_id=request_id)
#model_observer(Student,serializer_class=StudentSerializer)
async def students_feed(self, data, action, subscribing_request_ids=[], **kwargs):
# print(data,action,subscribing_request_ids)
for request_id in subscribing_request_ids:
await self.reply(data=data, action=action, request_id=request_id)
class Users_Consumer(
ListModelMixin,
# RetrieveModelMixin,
PatchModelMixin,
UpdateModelMixin,
CreateModelMixin,
DeleteModelMixin,
ObserverModelInstanceMixin,
GenericAsyncAPIConsumer):
queryset=User.objects.all()
serializer_class=UserSerializer
permission_classes = (permissions.AllowAny,)
#action()
async def subscribe_to_users_feed(self, request_id, **kwargs):
print('hello from another domain')
await self.users_feed.subscribe(request_id=request_id)
#model_observer(User,serializer_class=UserSerializer)
async def users_feed(self, data, action, subscribing_request_ids=[], **kwargs):
# print(data,action,subscribing_request_ids)
for request_id in subscribing_request_ids:
await self.reply(data=data, action=action, request_id=request_id)
from channels_demultiplexer.demultiplexer import WebsocketDemultiplexer
class DMultiplexer(WebsocketDemultiplexer):
consumer_classes={
"users":Users_Consumer,
"groups":Groups_Consumer,
"instructors":Instructors_Consumer,
"students":Students_Consumer,
}
in my VUEX STORE index.js I have this action:
async openEndpoint(state,params){
console.log(params);
let ws=params.ws
let endpoint=params.endpoint
ws.onopen=async ()=>{
console.log('connection established');
console.log(endpoint);
let data={
stream:endpoint,
payload:{
action:'subscribe_to_groups_feed',
request_id: new Date().getTime()
}
}
console.log(ws);
ws.onmessage=(ws_event)=>{
let response=JSON.parse(ws_event.data)
console.log(response);
console.log(response.payload.errors);
}
ws.send(JSON.stringify(data))
}
}
After hours of researching it turns out that I can write middleware to incercept the websocket requests. I had to pass the token as a query_string parameter and then decode it in the middleware so that I could then get the user from the Token.objects provided by Djoser.
However now all my normal fetch requests are failing. I provide the Middleware for anyone trying to advance in this topic.
from channels.auth import AuthMiddleware
from channels.db import database_sync_to_async
from channels.sessions import CookieMiddleware, SessionMiddleware
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
#database_sync_to_async
def get_user(scope_token):
token = scope_token.decode('utf8')
token= token.split('=')[1]
print(token)
if not token:
return AnonymousUser()
try:
user = Token.objects.get(key=token).user
except Exception as exception:
return AnonymousUser()
if not user.is_active:
return AnonymousUser()
return user
class TokenAuthMiddleware(AuthMiddleware):
def __init__(self, app):
# Store the ASGI application we were passed
self.app = app
async def __call__(self, scope,receive,send,*args,**kwargs):
# Look up user from query string (you should also do things like
# checking if it is a valid user ID, or if scope["user"] is already
# populated).
print('MIDDLEWARING')
scope['user'] = await get_user(scope["query_string"])
return await self.app(scope,receive,send)
async def resolve_scope(self, scope,receive,send):
scope['user']._wrapped = await get_user(scope)
def TokenAuthMiddlewareStack(inner):
return CookieMiddleware(SessionMiddleware(TokenAuthMiddleware(inner)))
UPDATE:
On applying the above solution I stopped being able to get my axios/fetch requests to work. I suppose this has to do with the fact that I used the middleware to intercept websocket requests. When I undo all the middleware changes everything works normal. The 'trick' around this was to just disable the middleware in settings.py somehow this allows the axios/fetch requests to go through django application normally and still incercepts the websocket requests. This is how the settings look like now.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 'middleware.websocket_auth.TokenAuthMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

discord.py v2 app_command permissions

I am creating a bot discord in discord.py v2 and I have created app commands. I would like to make some commands reserved to administrators or to a certain role, but I didn't find how to do it. #commands.has_permissions does not work for app commands.
Can someone tell me how to do this please ?
My code :
#app_commands.command(name="embed_message", description="Create a embed message")
#app_commands.describe(
title="The title of your message",
message="Your message",
color="Embed color",
image="Add an image to your message (with image's url : http(s)://...)",
url="Add an url to your message (http(s)://....)"
)
#app_commands.choices(color=[Choice(name="🔴 Red", value=0xff0000),
Choice(name="🔵 Blue", value=0x0000ff),
Choice(name="🟢 Green", value=0x00ff00),
Choice(name="🟣 Purple", value=0x883af1),
Choice(name="🟡 Yellow", value=0xffe34d),
Choice(name="🟠 Orange", value=0xff8000),
Choice(name="🟤 Brown", value=0x845321),
Choice(name="⚫️ Black", value=0xffffff),
Choice(name="⚪️ White", value=0x000000),
])
#commands.has_permissions(administrator=True)
async def embed_message(self, interaction = discord.Interaction, title:str="", message:str="", image:str="", url:str="", color:int=0xff0000):
await interaction.response.defer()
if title!="" or message!="":
embed = discord.Embed(title = title, description = message, color = color)
if image!="":
embed.set_thumbnail(url=image)
if url!="":
embed.url=url
await interaction.followup.send(embed=embed)
else:
await interaction.followup.send("You must enter at least one title or message", ephemeral=True)
Using this decorator before the slash command worked for me:
#app_commands.checks.has_permissions(moderate_members=True)
(swap in your required permissions)

Kubernetes Pods Restart Notification alerts on my team's channel

My Pods are running on AKS Cluster. Whenever my pods restarted, I had to get a notification on my team's channel, are there any articles or commands to configure the notification?
For that same, you can use tools or application like botkube : https://www.botkube.io/
Also check the Kubewatch : https://github.com/bitnami-labs/kubewatch
You can also implement the Grafana with the Prometheus and Alert manager for monitoring and getting the alert system. : https://github.com/grafana-operator/grafana-operator
However if you can not looking for any tools or applications you can write down the custom script of python, node or any language you are good with and monitor any pod restart event and send the slack hook event.
Sharing one example python code with check the POD running or crashing and send a notification to slack you can update the logic as per need.
from kubernetes import client, config, watch
import json
import requests
import time
logger = logging.getLogger('k8s_events')
logger.setLevel(logging.DEBUG)
# If running inside pod
#config.load_incluster_config()
# If running locally
config.load_kube_config()
v1 = client.CoreV1Api()
v1ext = client.ExtensionsV1beta1Api()
w = watch.Watch()
mydict={}
webhook_url = '';
while True:
pod_list= v1.list_namespaced_pod("default");
for i in pod_list.items:
for c in i.status.container_statuses:
if(c.ready == True):
if i.metadata.name in mydict:
print("Inside mydict If");
print("Pod updated : ",i.metadata.name);
print("My dict value : ",mydict);
mydict[i.metadata.name]['end_time'] = i.status.conditions[1].last_transition_time;
dt_started = mydict[i.metadata.name]['start_time'].replace(tzinfo=None);
dt_ended = mydict[i.metadata.name]['end_time'].replace(tzinfo=None);
duration = str((dt_ended - dt_started).total_seconds()) + ' Sec';
fields = [{"title": "Status", "value": "READY", "short": False }, {"title": "Pod name", "value": i.metadata.name, "short": False }, {"title": "Duration", "value": duration, "short": False }, {"title": "Service name", "value": c.name, "short": False } ]
if c.name not in ('conversation-auto-close-service-scheduler','admin-service-trail-fllow-up-scheduler','bot-trial-email-scheduler','conversation-service-scheduler','faq-service-scheduler','nlp-service-scheduler','refresh-add-on-scheduler','response-sheet-scheduler'):
text = c.name + " Pod is started";
data = {"text": text, "mrkdwn": True, "attachments" : [{"color": "#FBBC05", "title": "Pod Details", "fields" : fields, "footer": "Manvar", "footer_icon": "https://cdn.test.manvar.com/assets/manvar-icon.png"}, ], }
print("Final data to post: ",data);
response = requests.post(webhook_url, data=json.dumps(data),headers={'Content-Type': 'application/json'});
del mydict[i.metadata.name]
if response.status_code != 200:
raise ValueError('Request to slack returned an error %s, the response is:\n%s' % (response.status_code, response.text));
time.sleep(1);
else:
mydict[i.metadata.name] = {"start_time": i.status.conditions[0].last_transition_time,"end_time": i.status.conditions[1].last_transition_time};
I tried out Botkube but I did not want to publicly expose my cluster endpoint, so I wrote the following script based on the code from #Harsh Manvar. You can connect this to Teams using the Incoming Webhook Teams app from Microsoft.
from kubernetes import client, config
import json
import requests
import time
def monitorNamespace(namespace: str, webhookUrl: str):
v1 = client.CoreV1Api()
pod_list= v1.list_namespaced_pod(namespace);
podsNotRunning = {"Namespace": namespace, "Pods": []}
for pod in pod_list.items:
status = getPodStatus(pod)
if status != "Running":
podsNotRunning["Pods"].append({"Podname": pod.metadata.name, "status": status})
if len(podsNotRunning)>0:
sendAlert(podsNotRunning, webhookUrl)
def sendAlert(podsNotRunning, webhookUrl):
print(podsNotRunning)
response = requests.post(webhookUrl, data=json.dumps(podsNotRunning),headers={'Content-Type': 'application/json'});
if response.status_code != 200:
print('Response error:', response)
def getPodStatus(pod: client.models.v1_pod.V1Pod) -> str:
status = pod.status.phase
containerStatus = pod.status.container_statuses[0]
if containerStatus.started is False or containerStatus.ready is False:
waitingState = containerStatus.state.waiting
if waitingState.message is not None:
status = waitingState.reason
return status
if __name__ == "__main__":
# If running inside pod:
#config.load_incluster_config()
# If running locally:
config.load_kube_config()
webhookUrl = 'http://webhookurl'
namespace='default
interval = 10
while True:
monitorNamespace(namespace, webhookUrl)
time.sleep(interval)

Delete outgoing message sent by telegram bot (Telegram, python)

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!!

Resources