I don't know how to exactly write a title for this, so let me explain my problem. I'm using Python 3 to implement a bot on Slack, but my doubt is about how classes can be used to solve it.
I'm using Slack's API to create a bot to send messages. When you use the API to send a message, it returns some parameters that indentifies this message (like an id). With this "id" I can change the message's text or delete it, for example.
I created a main class that will be used to handle the API requests and therefore, send messages. I also have another class called SlackMessage that keeps the message's "id" and will have some methods, like change it's text or delete itself, for example. So, when I send a message through the main class, an instance of SlackMessage class will be returned.
As the first class is handling the requests to the API, the SlackMessage class should use it when changing it's text. Would be right the SlackMessage class have the main class as reference when it's being created and use it to update itself?
Here's some code to explain what I mean:
class Slack:
def __init__(self, token):
self._token = token
def send_message(self, message):
result = requests.post(...)
#Get message's reference
ts = result["ts"]
channel = result["channel"]
text = result["text"]
reference = {"ts": ts, "channel": channel, "text": text}
return SlackMessage(self, reference)
def update_message(self, message_reference):
result = requests.post(...)
class SlackMessage:
def __init__(self, slack_client, reference):
self._slack_client = slack_client
self._reference = reference
def change_text(self, text):
self._reference["text"] = text
self._slack_client.update_message(self._reference)
And how would be the best method to create a way to delete the message? Should I do it the same way?
Sorry if this question is a duplicate, I searched for an answer but didn't know how to explain the problem with a few words.
Related
This implementation of the AWS MQTT broker is confusing me.
In the code, there is this function definition:
def on_message_received(topic, payload, **kwargs):
print("Received message from topic '{}': {}".format(topic, payload))
global received_count
received_count += 1
if received_count == args.count:
received_all_event.set()
And then the function is called like this:
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_result = subscribe_future.result()
There are two things which are confusing me:
Why the on_message_received function is called without parameters?
If I want to pass a variable to on_message_received and do something to it from within that function, which would be the correct approach?
About point (2), consider the following example, which seems to be working:
last_message = ''
def on_message_received(topic, payload, **kwargs):
print("Received message from topic '{}': {}".format(topic, payload))
last_message = payload
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_result = subscribe_future.result()
But I don't think it's the correct approach. But I don't know how I should pass the external variable to the function.
The example you posted doesn't actually work because last_message is a local variable -- you can't access it from outside the function. I'd suggest putting it in a handler object, like this:
class MessageHandler:
def __init__(self):
self.last_message = ''
def on_message_received(self, topic, payload, **_kwargs):
print(f"Received message from topic '{topic}': {payload}")
self.last_message = payload
handler = MessageHandler()
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=handler.on_message_received
)
subscribe_result = subscribe_future.result()
print("Last message:", handler.last_message)
The on_message_received function is not called by your code, it is passed as an argument to mqtt_connection.subscribe. The API is then what will call it (later, when there's a message), and at that time it should provide the topic and payload args to your function.
Calling subscribe_future.result() causes your program to stop and wait for a message to be received (i.e. it waits for the Future object to produce a result). Note that in real life you usually don't want to immediately block on a future (because the point of a future is that it's something that will happen in the future and you probably have other things to do while you wait for that future to happen).
I want to send a message to notify about something in one of my microservices, but I don't want to do that through a domain event, which requires to create, update or delete one of the entities of the microservice.
Is there another way to send a message such that another microservices can handle them?
Yes! You can do that directly using the BrokerPublisher instance injected in the corresponding service.
If you want to send a message you can do as follows:
from minos.common import ModelType
from minos.cqrs import Service
from minos.networks import Request, enroute
MyContent = ModelType.build("MyContent", {"text": str, "number": int})
class MySenderService(Service):
#enroute.rest.command("/send/my-channel", "POST")
async def handle_send_my_channel(self, request: Request) -> Response:
# First, create the message.
message = BrokerMessageV1(
"MyChannel", BrokerMessageV1Payload(MyContent("foo", 56))
)
# Then, send it!
await self.broker_publisher.send(message)
In this case, "MyChannel" refers to the channel (or topic) on which the message will be sent.
Note that MyContent is a convenient ModelType created just to give the message's content some structure (but it could be another type, like int, str, dict and so on).
Finally, if you want to handle it in another microservice, you can do that as any other message:
from minos.cqrs import Service
from minos.networks import Request, enroute
class MyReceiverService(Service):
#enroute.broker.event("MyChannel")
async def handle_my_channel(self, request: Request):
# Print the received message's content!
print(await request.content())
I have problem with bot CONVERSATION_REFERENCES, it only saves one conversation_reference (latest). I want save them all.
For example
Then I call or add bot in the chat in Microsoft teams, code save conversation_reference. If I add bot in another chat or call bot it looks like overwrite conversation_reference, and I only have latest conversation_reference, but for example if I want to send proactive message to all chats where was bot called or add, it only send in latest chat.
So I need change function to save conversation_reference to array or list, but i cant found solution how to do it correctly. Bot.py
def __init__(
self,
conversation_references: Dict[list, ConversationReference],
timestamp: str = None,
channel_id: str = None,
prompted_for_user_name: bool = False
):
self.timestamp = timestamp
self.channel_id = channel_id
self.prompted_for_user_name = prompted_for_user_name
self.conversation_references = conversation_references
def _add_conversation_reference(self, activity: Activity):
"""
This populates the shared Dictionary that holds conversation references. In this sample,
this dictionary is used to send a message to members when /api/notify is hit.
:param activity:
:return:
"""
conversation_reference = TurnContext.get_conversation_reference(activity)
print(f"Adding new conversation to the list: {conversation_reference}")
self.conversation_references[
conversation_reference.user.id
] = conversation_reference
In app.py I change it too, from str to list
CONVERSATION_REFERENCES: Dict[list, ConversationReference] = dict()
In def _add_conversation_reference(self, activity: Activity): function I try change this line.
conversation_reference = TurnContext.get_conversation_reference(activity)
After I try transform it like this
conversation_reference =[]
conversation_reference.append(TurnContext.get_conversation_reference(activity))
I getting error like this
return await callback(context)
File "C:\Program Files\Python38\lib\site-packages\botbuilder\core\activity_handler.py", line 71, in on_turn
await self.on_message_activity(turn_context)
File "C:\bots\proactive_bot.py", line 52, in on_message_activity
self._add_conversation_reference(turn_context.activity)
File "C:\bots\proactive_bot.py", line 67, in _add_conversation_reference
conversation_reference.user.id
AttributeError: 'list' object has no attribute 'user'
Why I need this? Because I want make bot who can send proactive messages to all chats where he was. But if it only saves latest conversation_reference, I can only send proactive message to one chat.
For proactive message send in app.py code
async def _send_proactive_message():
for conversation_reference in CONVERSATION_REFERENCES.values():
print(f"converstion reference: {conversation_reference}")
await ADAPTER.continue_conversation(
conversation_reference,
lambda turn_context: turn_context.send_activity(split),
APP_ID,
)
print(CONVERSATION_REFERENCES.values())
Any ideas how to solve my problem? Thank you.
Hope you are looking code like this.
Define in app.py
CONVER_REF : Dict[str,ConversationReference] = dict()
Ctor assgin
self.conv_ref = conver_ref -> pass as ctor argu
3. async def on_members_added_activity store the get_conversation_reference
con_obj = TurnContext.get_conversation_reference(turn_context.activity)
self.conv_ref[con_obj.user.id] = TurnContext.get_conversation_reference(turn_context.activity)
4.Proactive function execute
for conversation_reference in self.conv_ref.values():
await self.adapter.continue_conversation(
conversation_reference,
lambda turn_context: turn_context.send_activity("proactive hello"),APPID,)
In my sample I have tested the use case , If new user connect to the bot I send message to others users ( Just call proactive function for testing purpose)
here video sample : output video
I have created a bot (using python-telegram-bot) that upon choosing a type of query, the bot should randomly choose one of the available strings as the reply.
My function to create replies is as follows:
def generate_reply():
replies = """
Hello
Goodbye
Thanks!
Your welcome!
See you around!""".splitlines()
r = random.choice(replies).strip()
return r
And the functions to reply to the users are as follows:
#Inline Reply
def inlinequery(update, context):
query = update.inline_query.query
results = [InlineQueryResultArticle(id=uuid4(), title="Interact",
input_message_content=InputTextMessageContent(
generate_reply()))]
update.inline_query.answer(results)
#Normal reply
def reply(update, context):
update.message.reply_text(generate_reply())
And after creating the bot I add it to the bot using:
dp.add_handler(CommandHandler("reply", reply))
dp.add_handler(InlineQueryHandler(inlinequery))
when I use /reply in chat it works as intended, but wherever I use an inline command in a chat with another user or a group, the random choice apparently stops working.How can I get around this problem?
I found out the answer to my question. Apparently Telegram caches the answers to similar inline queries for some time. For this to work correctly you should set cache_time to something you'd like, in my case 0.
#Inline Reply
def inlinequery(update, context):
query = update.inline_query.query
results = [InlineQueryResultArticle(id=uuid4(), title="Interact",
input_message_content=InputTextMessageContent(
generate_reply()))]
update.inline_query.answer(results, cache_time=0)
I'm using telebot (https://github.com/eternnoir/pyTelegramBotAPI) to create a bot to send photos to its users. The point is I didn't see a way to restrict the access to this bot as I intend to share private images through this bot.
I read in this forum that through python-telegram-bot there is a way to limit the access from the sender's message (How To Limit Access To A Telegram Bot), but I didn't know if via pyTelegramBotAPI it is possible.
Do you know how can I solve it?
A bit late tot the party - perhaps for future post readers. You can wrap the function to disallow access.
An example below:
from functools import wraps
def is_known_username(username):
'''
Returns a boolean if the username is known in the user-list.
'''
known_usernames = ['username1', 'username2']
return username in known_usernames
def private_access():
"""
Restrict access to the command to users allowed by the is_known_username function.
"""
def deco_restrict(f):
#wraps(f)
def f_restrict(message, *args, **kwargs):
username = message.from_user.username
if is_known_username(username):
return f(message, *args, **kwargs)
else:
bot.reply_to(message, text='Who are you? Keep on walking...')
return f_restrict # true decorator
return deco_restrict
Then where you are handling commands you can restrict access to the command like this:
#bot.message_handler(commands=['start'])
#private_access()
def send_welcome(message):
bot.reply_to(message, "Hi and welcome")
Keep in mind, order matters. First the message-handler and then your custom decorator - or it will not work.
The easiest way is probably a hard coded check on the user id.
# The allowed user id
my_user_id = '12345678'
# Handle command
#bot.message_handler(commands=['picture'])
def send_picture(message):
# Get user id from message
to_check_id = message.message_id
if my_user_id = to_check_id:
response_message = 'Pretty picture'
else:
response_message = 'Sorry, this is a private bot!'
# Send response message
bot.reply_to(message, response_message)