Telegram games bot - python

I am trying to build a telegram bot to play games for my son.
Basically, I want to get a response from the bot by using a command handler.
when I request a command handler it should give me a random response from 2 different lists.
The game is predicting food dish name like "apple pie", apple will be in list 1 and pie will be in list 2
and the bot should get different values from the list and give a response as one message via command handler.
Will appreciate your guidance/help
Below is the python code:
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
from telegram import error, update
import sys
import os
import random
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
def start(update, context):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hello, play games!')
def shuffle2d(arr2d, rand_list=random):
"""Shuffes entries of 2-d array arr2d, preserving shape."""
reshape = []
data = []
iend = 0
for row in arr2d:
data.extend(row)
istart, iend = iend, iend+len(row)
reshape.append((istart, iend))
rand_list.shuffle(data)
return [data[istart:iend] for (istart,iend) in reshape]
def show(arr2d):
"""Shows rows of matrix (of strings) as space-separated rows."""
show ("\n".join(" ".join(row) for row in arr2d))
A = A.rand_list['APPLE, PUMKIN, STRAWBERRY, BANANA,CHOCOLATE']
B = B.rand_list['PIE, ICE-CREAM, CAKE,SHAKE']
arr2d = []
arr2d.append([A+(j) for j in range(1,B+1)])
show(shuffle2d(arr2d))
print(show)
return show
def play(update, context):
"""Send a message when the command /play is issued."""
update.message.reply_text(shuffle2d)
def main():
"""Start the bot."""
# 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("1XXXXXXX:XXXXXXXXXXXXXXXXXXY", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("play", play))
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, play))
# 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()

In this code you use a function called shuffleDish() that creates a string containing two random words chosen from separated list and it's called from the commandhandler "play"
from telegram.ext import Updater, CommandHandler
from telegram import error, update
import random
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def shuffleDish():
A = ['APPLE', 'PUMKIN', 'STRAWBERRY', 'BANANA', 'CHOCOLATE']
B = ['PIE', 'ICE-CREAM', 'CAKE', 'SHAKE']
dish = random.choice(A)+" "+random.choice(B)
return dish
def play(update, context):
update.message.reply_text(shuffleDish())
def error(update, context):
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
updater = Updater(tgtoken, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("play", play))
dp.add_error_handler(error)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()

Related

How can I listen and speak simultaneously using Azure Cognitive services

I am trying to build a chatbot, that is capable of speaking and listening simultaneously. I am working with azure cognitive services and currently use two functions to listen and speak:
Speaking:
def speak(input,voice="en-US-ChristopherNeural"):
audio_config = speechsdk.audio.AudioOutputConfig(use_default_speaker=True)
speech_config.speech_synthesis_voice_name=voice
speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)
speech_synthesis_result = speech_synthesizer.speak_text_async(input).get()
if speech_synthesis_result.reason == speechsdk.ResultReason.Canceled:
cancellation_details = speech_synthesis_result.cancellation_details
print("Azure Speech synthesis canceled: {}".format(cancellation_details.reason))
return True
Listening:
def listen(language):
speech_config.speech_recognition_language=language
audio_config = speechsdk.audio.AudioConfig(use_default_microphone=True)
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config, audio_config=audio_config)
print("Speak into your microphone.")
speech_recognition_result = speech_recognizer.recognize_once_async().get()
if speech_recognition_result.reason == speechsdk.ResultReason.RecognizedSpeech:
print("Recognized: {}".format(speech_recognition_result.text))
return speech_recognition_result.text
I want to be capable of interrupting a spoken text, if the user starts speaking. This means I have to constantly listen to the microphone input to determine an attempt in speech.
I am currently trying multithreading, however the examples I tried are all getting blocked by the line:
speech_synthesis_result = speech_synthesizer.speak_text_async(input).get()
This is what I have so far, however it does not speak anything:
import asyncio
import os
import azure.cognitiveservices.speech as speechsdk
from azure.cognitiveservices.speech import SpeechConfig, SpeechSynthesisOutputFormat, SpeechSynthesizer
# Replace with your own subscription key and region identifier
speech_config = speechsdk.SpeechConfig(subscription=os.environ.get('SPEECH_KEY'), region=os.environ.get('SPEECH_REGION'))
# Set up the speech synthesizer
# Define the phrase to be spoken
phrase = "Hello, I'm a chatbot. How can I help you today? You can interrupt me whenever you want"
async def listen_for_user_input():
speech_config.speech_recognition_language="en-US-ChristopherNeural"
audio_config = speechsdk.audio.AudioConfig(use_default_microphone=True)
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config, audio_config=audio_config)
result = await speech_recognizer.start_continuous_recognition_async()
if result.reason == speechsdk.ResultReason.RecognizedSpeech:
print("Recognized: {}".format(result.text))
await speech_recognizer.stop_continuous_recognition_async()
pass
async def speak_phrase(phrase):
audio_config = speechsdk.audio.AudioOutputConfig(use_default_speaker=True)
synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)
# Speak the defined phrase
result = await synthesizer.speak_text_async(phrase)
if result.reason == speechsdk.ResultReason.Canceled:
cancellation_details = result.cancellation_details
print("Azure Speech synthesis canceled: {}".format(cancellation_details.reason))
# Start the program
async def main():
task1 = asyncio.create_task(speak_phrase(phrase))
task2 = asyncio.create_task(listen_for_user_input())
done, pending = await asyncio.wait([task1, task2], return_when=asyncio.FIRST_COMPLETED)
for task in pending:
task.cancel()

Discord.py Never proceeds to ready

I am rewriting my discord bot wrapper to update it and make it more asynchronous, its been almost two years since I made it, I am using discord.py 2.0.0 I was using 1.6 i believe.
My main file is Helix.py:
"""
asynchronous Helix Bot Wrapper class
"""
### Imports ###
import os
import colorlog
import datetime
import discord
import sys
import helix_bot.bot.utility.config as cfg
import logging
from datetime import datetime
from discord.ext import commands
from sqlalchemy_utils import database_exists, create_database
## Logging set up and format with colorlog
log = logging.getLogger("HELIX_BOT")
# Change recursion limit to avoid errors
sys.setrecursionlimit(100000)
### HELIX BOT CLASS ###
class Helix(commands.AutoShardedBot):
def __init__(self, config=None):
# Set config
self.aio_session = None
if config is None:
config = cfg.ConfigDefaults.Config_file
self.description = """
Helix - A Custom Bot for SinLess Games Official Discord Server and more!
"""
self.config = cfg.Config(config)
#####################################
### SETUP VARIABLES FOR HELIX ###
#####################################
### GENERAL VARIABLES ###
self.Version = self.config.version
self.owner_id = self.config.owner_id
self.owner = self.config.owner
### DISCORD VARIABLES ###
self.TOKEN = self.config.login_token
self.applicationID = self.config.applicationID
self.invite_url = self.config.InviteURL
self.PublicKey = self.config.PublicKey
self.ClientID = self.config.ClientID
self.ClientSecret = self.config.ClientSecret
self.Prefix = self.config.prefix
### SQL VARIABLES ###
self.sql_host = self.config.sql_host
self.sql_user = self.config.sql_user
self.sql_pass = self.config.sql_passwd
self.sql_ddb = self.config.sql_ddb
self.Model_Version = self.config.model_version
self.db_connect = f'mysql+pymysql://{self.sql_user}:{self.sql_pass}#{self.sql_host}/{self.sql_ddb}'
### DEBUG VARIABLES ###
self.debug_level = self.config.debug_level
self.show_config = self.config.Show_Config
self.log_channel = self.config.Log_Channel
### SPOTIFY VARIABLES ###
self.spotify_client_id = self.config.spotify_client_id
self.spotify_client_secret = self.config.spotify_client_secret
### PERMISSIONS ###
self.dev_ids = self.config.devids
self.bot_exception_ids = self.config.bot_exception_ids
########################
### LOGGING SETTINGS ###
########################
### SET LOG LEVEL ###
self.debug_level = 'DEBUG'
log.setLevel(self.debug_level)
self.log_file = 'logs/helix.log'
### CREATE HANDLER ###
self.helix_h = logging.FileHandler(self.log_file)
self.helix_h.setLevel(self.debug_level)
### CONSOLE HANDLER ###
CH = logging.StreamHandler()
CH.setLevel(self.debug_level)
### FORMATTER ###
self.formatter = colorlog.ColoredFormatter(
"%(log_color)s[%(asctime)s] [%(levelname)s] %(message)s",
datefmt=None,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red',
'EVERYTHING': 'white',
'NOISY': 'white',
'FFMPEG': 'bold_purple',
'VOICEDEBUG': 'purple',
},
secondary_log_colors={},
style='%'
)
self.helix_h.setFormatter(self.formatter)
CH.setFormatter(self.formatter)
log.addHandler(self.helix_h)
log.addHandler(CH)
log.info("Logging set up")
intents = discord.Intents.all()
log.info("Intents set up")
log.debug("Intents: {}".format(intents))
### LOAD BOT ###
super().__init__(
command_prefix=self.Prefix,
intents=intents,
description=self.description,
case_insensitive=True,
start_time=datetime.utcnow(),
)
### Remove help command ###
super().remove_command('help')
### Build Database ###
self.build_database()
def build_database(self):
if not database_exists(self.db_connect):
log.info("Creating database...")
create_database(self.db_connect)
log.info("Database created")
else:
log.info("Database already exists")
### RUN Helix Bot Async ###
async def run(self, **kwargs):
log.info("Starting Helix...")
try:
await self.login(self.TOKEN)
except Exception as e:
log.error(f'{type(e).__name__} - {e}')
log.info("Helix Logged in as {}#{}".format(self.user.name, self.user.discriminator))
log.info("Waiting {}#{} for to be Ready...".format(self.user.name, self.user.discriminator))
try:
await self.wait_until_ready()
except Exception as e:
log.error(f'{type(e).__name__} - {e}')
log.info("{}#{} is ready".format(self.user.name, self.user.discriminator))
try:
await self.change_presence(activity=discord.Game(name="Helix"))
except Exception as e:
log.error(f'{type(e).__name__} - {e}')
log.info("{}#{} is ready!".format(self.user.name, self.user.discriminator))
try:
await self.load_extensions()
except Exception as e:
log.error(f'{type(e).__name__} - {e}')
log.info("{}#{}is loaded!".format(self.user.name, self.user.discriminator))
### LOAD_EXTENSIONS Async ###
async def load_extensions(self):
for filename in os.listdir('./bot/cogs'):
if filename.endswith('.py'):
log.info("Loading {}...".format(filename))
self.load_extension(f'bot.cogs.{filename[:-3]}')
log.info("Loaded Cog {}".format(filename))
### command to unload a cog ###
#commands.command(name='unload', aliases=['unload_cog'])
#commands.is_owner()
async def unload_cog(self, ctx, *, cog: str):
"""Unloads a cog"""
try:
await self.unload_extension(cog)
await ctx.send(f'Unloaded {cog}')
except Exception as e:
await ctx.send(f'{type(e).__name__} - {e}')
### command to reload a cog ###
#commands.command(name='reload', aliases=['reload_cog'])
#commands.is_owner()
async def reload_cog(self, ctx, *, cog: str):
"""Reloads a cog"""
try:
await self.unload_extension(cog)
self.load_extension(cog)
await ctx.send(f'Reloaded {cog}')
except Exception as e:
await ctx.send(f'{type(e).__name__} - {e}')
### LOAD_EXTENSION ###
def load_extension(self, extension, **kwargs):
try:
self.load_extension(extension)
except Exception as e:
log.error(f'{type(e).__name__} - {e}')
and the file i use to run it is :
import threading
import asyncio
from bot.helix import Helix
async def main():
bot = Helix()
await bot.run()
if __name__ == '__main__':
threading.Thread(target=asyncio.run(main())).start()
My main question is even after 2 hours of letting it run, it never reaches ready status that last thing it will say is waiting for helix to be ready.
I have looked around and ready that it does take 75% longer to ready, according to the official documentation. But 2 hours and still nor ready. Dis I do something wrong or what is my problem. I have searched through what i could, and as far as i know it should work. Though i will continue to look.
The client will never properly become ready because you need to connect AND login. You can also simply use the [Client].start instead.
From the docs here:
await login(token)
Logs in the client with the specified credentials and calls the setup_hook().
await connect(*, reconnect=True)
Creates a websocket connection and lets the websocket listen to messages from Discord. This is a loop that runs the entire event system and miscellaneous aspects of the library. Control is not resumed until the WebSocket connection is terminated.
Using start will properly start the bot.
await start(token, *, reconnect=True)
A shorthand coroutine for login() + connect().

Processing event hub data using python

I am using azure event hub python SDK to send to and receive messages from event hub following this link.https://github.com/Azure/azure-event-hubs-python/tree/develop. I can successfully send and receive messages. But how do i parse the messages and retrieve the data from the event data object. Please find the code below.
import os
import sys
#import logging
from azure.eventhub import EventHubClient, Receiver, Offset
ADDRESS = 'sb://####.servicebus.windows.net/#####'
USER = '##########'
KEY = '##################################'
CONSUMER_GROUP = "$default"
OFFSET = Offset("-1")
PARTITION = "1"
total = 0
last_sn = -1
last_offset = "-1"
try:
if not ADDRESS:
raise ValueError("No EventHubs URL supplied.")
client = EventHubClient(ADDRESS, debug=False, username=USER, password=KEY)
receiver = client.add_receiver(CONSUMER_GROUP, PARTITION, prefetch=5000,
offset=OFFSET)
client.run()
try:
batched_events = receiver.receive(timeout=20)
except:
raise
finally:
client.stop()
for event_data in batched_events:
last_offset = event_data.offset.value
last_sn = event_data.sequence_number
total += 1
print("Partition {}, Received {}, sn={} offset={}".format(
PARTITION,
total,
last_sn,
last_offset))
except KeyboardInterrupt:
pass
if i try to view the event_data received i can see the below message.
event_data
<azure.eventhub.common.EventData at 0xd4f1358>
event_data.message
<uamqp.message.Message at 0xd4f1240>
Any help on the above on how to parse this message to extract the data
As of 1.1.0, there are new utility methods to extract the actual data of the message:
body_as_str
body_as_json
So, what used to be
import json
event_obj = json.loads(next(event_data.body).decode('UTF-8'))
Is now:
event_obj = event_data.body_as_json()
For people using the Event Hub version 5.2.0 -- latest as of today (GitHub, Reference Docs), it's the same as the 1.1.0 version, i.e. use body_as_str() or body_as_json(). But the client has changed -- there's an EventHubProducerClient and an EventHubConsumerClient in the new version. To print the body of an event received:
from azure.eventhub import EventHubConsumerClient
connection_str = '<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>'
consumer_group = '<< CONSUMER GROUP >>'
eventhub_name = '<< NAME OF THE EVENT HUB >>'
client = EventHubConsumerClient.from_connection_string(
connection_str, consumer_group, eventhub_name=eventhub_name
)
def on_event_batch(partition_context, events):
partition_context.update_checkpoint()
for e in events:
print(e.body_as_str())
with client:
client.receive_batch(
on_event_batch=on_event_batch,
starting_position="-1", # "-1" is from the beginning of the partition.
)

Change custom keyboard by sending commands on telepot Telegram Bot

I'm using this line for a custom keyboard on my Telegram bot:
markup = ReplyKeyboardMarkup(keyboard=[['Time', KeyboardButton(text='NewKey')],["File", "Audio"]])
I want my bot to change custom keyboard to another when user sends NewKey. And it will send show up a new custom keyboard but it's layout will be same as the default keyboard.
I have tried this but it is not working:
elif command == 'NewKey':
markup = ReplyKeyboardMarkup(keyboard=[['More', KeyboardButton(text='More2')],["More3", "More5"]])
Here is my full code of my bot:
import time, datetime
import telepot
from telepot.loop import MessageLoop
from telepot.namedtuple import ReplyKeyboardMarkup, KeyboardButton
now = datetime.datetime.now()
def action(msg):
chat_id = msg['`chat']['id']
command = msg['text']
print('Received: %s' % command)
markup = ReplyKeyboardMarkup(keyboard=[['Time', KeyboardButton(text='NewKey')],["File", "Audio"]])
if command == '/start':
telegram_bot.sendMessage (chat_id, str("Hi! Which one do you want? choose from the below keyboard buttons."), reply_markup=markup)
telegram_bot.sendMessage(chat_id, str(now.hour)+str(":")+str(now.minute))
elif command == 'NewKey':
markup = ReplyKeyboardMarkup(keyboard=[['More', KeyboardButton(text='More2')],["More3", "More5"]]))
elif command == 'Time':
telegram_bot.sendMessage(chat_id, str(now.hour)+str(":")+str(now.minute))
elif command == '/file':
telegram_bot.sendDocument(chat_id, document=open('/home/pi/Aisha.py'))
elif command == '/audio':
telegram_bot.sendAudio(chat_id, audio=open('/home/pi/test.mp3'))
telegram_bot = telepot.Bot('MY-BOT-TOKEN')
print((telegram_bot.getMe()))
MessageLoop(telegram_bot, action).run_as_thread()
print('Up and Running....')
while 1:
time.sleep(10)
`

Create custom logging function for pythons logging module

I have been trying for some time to figure out how to create a custom function for the python logging module. My goal is, with the usual function such as logging.debug(...) a log message over several channels, such as Telegram or MQTT, to publishing. So my idea is to add extra arguments to the normal log methode. For example logging.debug ("a log", telegram=True, mqtt=False) and maybe other arguments. All I find is the inheritance of the class logging.StreamingHandler and then using the method emit, but this only passes the argument record. So how can I implement my problem in a meaningful way? Do I have a thinking error or the wrong approach?
I solved my problem by creating a interface for the logging module.
A small view on my code:
# ulogging.py
import logging
import telegram
def uloggingConfig(*args, **kwargs):
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# general logging config section
fmt = kwargs.pop("fmt", "%(asctime)s %(levelname)s %(message)s")
datefmt = kwargs.pop("datefmt", "%m.%d.%Y %I:%M:%S %p")
streamHandler = logging.StreamHandler()
streamHandler.setLevel(logging.DEBUG)
formater = logging.Formatter(fmt=fmt, datefmt=datefmt)
streamHandler.setFormatter(formater)
logger.addHandler(streamHandler)
# telegram config section
telegramBot = kwargs.pop("teleBot", None)
telegramChatID = kwargs.pop("teleChatID", None)
telegramLevel = kwargs.pop("teleLevel", logging.INFO)
telegramFmt = kwargs.pop("telefmt", "%(message)s")
telegramDatefmt = kwargs.pop("teledatefmt", None)
if telegramBot is not None and telegramChatID is not None:
telegramStream = TelegramStream(telegramBot, telegramChatID)
formater = logging.Formatter(fmt=telegramFmt, datefmt=telegramDatefmt)
telegramStream.setFormatter(formater)
telegramStream.setLevel(telegramLevel)
logger.addHandler(telegramStream)
elif (telegramBot is not None and telegramChatID is None) or (telegramBot is None and telegramChatID is not None):
raise KeyError("teleBot and teleChatID have to be both given")
if kwargs:
keys = ', '.join(kwargs.keys())
raise ValueError('Unrecognised argument(s): %s' % keys)
return logger
def getLogger():
return logging.getLogger(__name__)
class TelegramStream(logging.StreamHandler):
def __init__(self, telegramBot, telegramChatID):
logging.StreamHandler.__init__(self)
self._bot = telegramBot
self._chatID = telegramChatID
def emit(self, record):
if record.levelname == "DEBUG":
self._bot.send_message(self._chatID, record.levelname + ": " + record.msg)
else:
self._bot.send_message(self._chatID, record.msg)
With the uloggingConfig() method I can now pass all settings for the different handlers (at the moment only for telegram, further handlers should follow). The uloggingConfig() method then takes over the configuration and returns a logger with which log messages can be created as usual.
A simple example:
logger = ulogging.uloggingConfig(fmt="format, datefmt="dateformat, teleBot=telegramBot, teleChatID=telegramChatID, teleLevel=logging.DEBUG)
logger.debug("A log message")

Categories