I have a problem sending proactive messages using the Bot Framework with Python.
First what I need is to get the message body from Outlook, and then the bot must send that as a message to all the chats where it was added.
To do that, first I created a new file and called it Email.py.
To read every incoming message body I simply used while true: and time.sleep()
Here is my code example:
import imaplib, email, getpass
from email import policy
import json
import time
imap_host = 'outlook.office365.com'
imap_user = 'xx#xx.com'
# init imap connection
mail = imaplib.IMAP4_SSL(imap_host, 993)
rc, resp = mail.login(imap_user, 'xxxxxx')
while True:
# select only unread messages from inbox
mail.select('Inbox')
status, data = mail.search(None, '(UNSEEN)')
if not data[0].split():
time.sleep(120)
# Bot message variable
Message_for_bot = ''
# for each e-mail messages
for num in data[0].split():
# get a single message and parse it by policy.SMTP (RFC compliant)
status, data = mail.fetch(num, '(RFC822)')
email_msg = data[0][1]
email_msg = email.message_from_bytes(email_msg, policy=policy.SMTP)
# print only message parts that contain text data
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
for line in part.get_content().splitlines():
Message_for_bot += '\n' + line
print(Message_for_bot)
After I successfully created a program to read and print all incoming messages, I tried to build my bot. I found a proactive message bot on the Internet and used it as an example.
First I thought to just run this file with os in the background, but then my bot wasn't running. So then I tried adding an async function in the bot file but it didn't work. My bot just ignores that function. (Then I found the async functions in activity_handler.py, but I didn't find any that could help me.)
Then I tried adding an on_message_activity function and thought maybe it will start working if I call the bot like "#bot hi" for example in Teams. For that idea I must always run the while cycle and never stop the bot, but then I just get a message, and if there's a new incoming message then the bot doesn't write it anymore, and it's not a solution because if the bot is used for multiple chats then it simply doesn't work this way.
Then I try include my code on on_members_added_activity it seems working on azure test in web chat perfectly, but in teams after 1-2 messages stopping to work.
my code
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
imap_host = 'outlook.office365.com'
imap_user = 'xxxxxx#xxxxxx.com'
# init imap connection
mail = imaplib.IMAP4_SSL(imap_host, 993)
rc, resp = mail.login(imap_user, 'xxxxxx')
while True:
# select only unread messages from inbox
mail.select('Inbox')
status, data = mail.search(None, '(UNSEEN)')
if not data[0].split():
time.sleep(5)
# Bot message variable
Message_for_bot = ''
# for each e-mail messages
for num in data[0].split():
# get a single message and parse it by policy.SMTP (RFC compliant)
status, data = mail.fetch(num, '(RFC822)')
email_msg = data[0][1]
email_msg = email.message_from_bytes(email_msg, policy=policy.SMTP)
# print only message parts that contain text data
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
for line in part.get_content().splitlines():
Message_for_bot += '\n' + line
await turn_context.send_activity(f"{Message_for_bot}")
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity(
"bot starting work..."
)
So maybe it's possible to send a message to wherever the bot is added (it needs to get this information somehow, maybe it's kept in the bot memory) whenever Message_for_bot is not empty.
All help will be appreciated.
As we have discussed some logic has to change
Move your code out of the on_members_added_activity function
Use Proactive concept to send the message
-Vinoth
Related
i'm learning Python!
I'm building a Bot that searches for products in an e-commerce, the bot will always send a message with a price predefined by me!
The bot does a new search every 5 minutes, but I don't want send the same product again in the next message, i did some research but found nothing that fits what i need.
Sorry for my bad english! I hope you understand what I meant.
Code:
def send_to_telegram(message):
apiToken = 'xxxxx'
chatID = '-xxxx'
bot = telebot.TeleBot(apiToken)
if placa and fabricante_1 in marca:
bot.send_message(
chat_id=chatID, text=f"<b>Modelo:</b> {marca} \n<b>Fabricante:</b> {fabricante_1}\n<b>Preço a vista:</b> R$ {valor_preco_avista} \n<b>Preço a prazo:</b> R$ {valor_preco_prazo} \n<b>Loja:</b> {loja} \n\n<b>Link Produto:</b> {url_completa}", parse_mode='HTML')
I tried some modules like: schedule, time
But in the end the bot sends the duplicate message anyway.
I managed to solve my problem, I'll leave the solution for anyone who has a similar doubt in the future!
To avoid sending repeated messages with a Telegram bot with Python, you can store a record of the messages that have already been sent and check against this record before sending a new message.
import os
import pickle
sent_messages_file = "sent_messages.pickle"
if os.path.exists(sent_messages_file):
with open(sent_messages_file, "rb") as f:
sent_messages = pickle.load(f)
else:
sent_messages = []
def send_message(mensagem):
apiToken = 'XXXXXXXXXX'
chatID = 'XXXXXXXX'
bot = telebot.TeleBot(apiToken)
bot.send_message(
chat_id=chatID, text=message, parse_mode='HTML')
sent_messages.append(message)
with open(sent_messages_file, "wb") as f:
pickle.dump(sent_messages, f)
In this code, the sent_messages list is saved to a file using the pickle module. Before sending a new message, the code checks if the sent_messages_file exists. If it exists, the code loads the sent_messages list from the file. If it does not exist, the code creates an empty sent_messages list. After sending a message, the code saves the updated sent_messages list to the file.
This way, when you run the code again, it will load the sent_messages list from the file, and only send new messages that have not been sent before.
I am trying to read otp from mail and after that I want to delete that email from gmail option. I have no problem in reading email but I am not able to delete mail. I tried some code from stackoverflow. below is my code.
def getOtpMail(vEmail, vPaasword):
connection = imaplib.IMAP4_SSL(IMAP_URL) # stablish connection with IMAP server
try:
connection.login(vEmail, vPaasword) # Login with userid password
except Exception as e:
print(e)
return
loopLock = True
while loopLock:
# fetch
connection.select('"INBOX"', readonly=True)
retCode, messages = connection.search(None, '(UNSEEN)')
print(messages[0])
latest = int(messages[0].split()[-1])
res, msg = connection.fetch(str(latest), "(RFC822)")
for response in msg:
if isinstance(response, tuple):
print('\n------------email--------------\n')
msg = email.message_from_bytes(response[1])
if SENDER_NAME in msg['From'] and KEYWORD in msg['Subject']:
loopLock = False
# fetch required information
for part in msg.walk():
body = part.get_payload()
word_list = body.split()
index = word_list.index('verification')
otp = word_list[index + 3].strip('.')
#delete mail - below two line not working
connection.store(str(latest), '+FLAGS', '"[Gmail]/Trash"')
print(connection.expunge())
return otp
else:
continue
I read documentation and print connection.expunge() so I got response as ('NO', [b'EXPUNGE attempt on READ-ONLY folder (Failure)']) . I think issue I have to establish connection in WRITE mode. I am not sure about it.
In this issue, I opened mail box in readonly mode. Hence my program not able to write and store in IMAP server.
I changed
connection.select('"INBOX"', readonly=True)
to
connection.select('"INBOX"', readonly=False)
also I changed command type and flag type in store method -
connection.store(str(latest), '+FLAGS', '"[Gmail]/Trash"')
to
connection.store(str(latest), '+FLAGS', '\\Deleted')
.
I'm using SendGrid to send emails from my python-based Heroku app.I'm okay with it taking 10 or so minutes to get to my inbox, but I'm receiving three copies of the message and I can't figure out why. Here is the relevant code:
import sendgrid
from sendgrid import SendGridError, SendGridClientError, SendGridServerError
sg = sendgrid.SendGridClient('xxx#heroku.com', 'xxx')
message = sendgrid.Mail()
message.add_to('John Doe <xxx#xxx.com>')
message.set_subject('Example')
message.set_html('Body')
message.set_text('Body')
message.set_from('Dark Knight <xxx#xxx.com>')
message.add_attachment('image.jpg', './image.jpg')
status, msg = sg.send(message)
#app.route('/test2')
def test2():
sg.send(message)
return "sent"
When I go to the relevant route I get 'sent' returned and the email is sent, but again, it send three copies. I'm not sure why. Any help would be great.
Emails one and two:
status, msg = sg.send(message) would send two emails and then set status and msg to the response object.
Email three: after you load the route sg.send(message) sends the next email.
I suggest you to use sendgrid sendmail api to send email. its efficient, fast way to send emails.
You are calling sg.send(message) three times in your code.
It is called twice here: status, msg = sg.send(message) - this will send one mail for status and log it's response to that variable. It will then send again for msg and log it's response to that variable as well.
Then when the user hits the /test2 the function is called again, making it three messages in total.
Here's how you might change it to log responses but just send the one message out:
import sendgrid
from sendgrid import SendGridError, SendGridClientError, SendGridServerError
sg = sendgrid.SendGridClient('xxx#heroku.com', 'xxx')
def sendMessage(options):
message = sendgrid.Mail()
message.add_to('John Doe <xxx#xxx.com>')
message.set_subject('Example')
message.set_html('Body')
message.set_text('Body')
message.set_from('Dark Knight <xxx#xxx.com>')
message.add_attachment('image.jpg', './image.jpg')
// send the message and log the results to status
msg = sg.send(message)
return msg
#app.route('/test2')
def test2():
// send the message, pass any options like email address (not required)
status = sendMessage(options)
return status
I've added a new function above to send out the message and given it an optional options var, so you could use that to pass things to the message, like a different email address, or subject.
I have sleekXMPP for python and I have used the API to create functions to send messages, although when I researched into receiving them I can't find anything, can someone please help me to work this out, or disprove the possibility. Thanks.
Below is the code I used to send Messages, If its any help.
to = config.get('usermap', to[4:])
gmail_from_user = config.get('auth', 'email')
gmail_from_secret = config.get('auth', 'secret')
sys.stdout = stdouttmp
sys.stderr = stderrtmp
print "Sending chat message to " + to
xmpp = SendMsgBot(gmail_from_user, gmail_from_secret, to, message)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0199') # XMPP Ping
sys.stdout = stdouttmp
if xmpp.connect(('talk.google.com', 5222)):
xmpp.process(block=True)
else:
sys.stdout = stdouttmp
print("Unable to connect.")
sys.stdout = stdouttmp
sys.stderr = stderrtmp
btw I'm using a .cfg text file for the users email and password, along with some contacts, which is then parsed in
I see that you're using the send_client.py example. The intent of that example is how to reliably log in, send a single message, and then log out. Your use case is to both send and receive messages, so you would be better served looking at the echo_client.py example.
Notably, in order to receive a message you would do:
# in your __init__ method:
def __init__(...):
# ...
self.add_event_handler('message', self.recv_message)
def recv_message(self, msg):
# You'll probably want to ignore error and headline messages.
# If you want to handle group chat messages, add 'groupchat' to the list.
if msg['type'] in ('chat', 'normal'):
print "%s says: %s" % (msg['from'], msg['body'])
Again, you will need to switch from using the SendMsgBot example because it automatically disconnects after sending its message.
Don't forget that there is the sleek#conference.jabber.org chat room if you need any help.
-- Lance
So I have this class that starts like this:
class emailreader():
def __init__(self, server, port, username, password):
self.imap_server = imaplib.IMAP4_SSL(server, int(port))
self.imap_server.login(username, password)
self.imap_server.select('INBOX')
def _get_raw_emails(self):
messages = []
typ, data = self.imap_server.search(None, 'UnSeen')
for num in data[0].split():
typ, data = self.imap_server.fetch(num, '(RFC822)')
messages.append(data[0][1])
return messages
It's working great for fetching messages like this:
mail = emailreader(server, port, username, password)
emails = mail._get_raw_emails()
But if I send a new message to the e-mail address I can't just run the last line again, the new mail won't show up until I delete the mail object and start all over again. Why is that? Do I have to reset the last search or something?
Edit: I think I found the solution. I had to do a imap_server.check() also...
I found the solution. I had to execute a method that's called check() from my imap object whenever new mails may have arrived. This is my "new" _get_raw_emails() method:
def _get_raw_emails(self):
messages = []
self.imap_server.check() # Fetch new mails since object is created.
typ, data = self.imap_server.search(None, 'UnSeen')
for num in data[0].split():
typ, data = self.imap_server.fetch(num, '(RFC822)')
messages.append(data[0][1])
return messages
It may be because I'm not a native english speaker, but when I looked att the documentation for imaplib it did'nt seem obvious what the check method did. It only says "Checkpoint mailbox on server." So I did'nt thought that it would solve my problem.