How to make a Bot listen to a conversation in the background, but respond only if his name is quoted in a sentence - response

I am developing a ChatBot for Telegram named "Joker". It works perfectly in a private conversation. Has a whole training set up to answer several questions. But when placed in a group, it responds to all messages sent to the group, creating a chat disorder. It would be nice if my Bot could hear the group talk in the background and intervene only if his name "Joker" is quoted in one sentence. He would normally answer the questions as long as the word "Joker" was contained in the sentence. For this reason, I am trying to implement this feature, but it does not work as it should.
Current Reaction
Gillan: Good morning guys!
Mitzi Dupree: Good morning, Gillan!
... Joker is typing
Gillan: Good morning, Joker!
... Joker is typing
Gillan: Joker?
... Joker is typing
Communication.py
def respond(self, message):
"""
Receive message from user and returns corresponding answer.
"""
if re.search("joker", message, re.IGNORECASE):
joker_in_message = True
else:
joker_in_message = False
message_without_joker = re.sub(r'\bjoker\b', '', message, flags=re.IGNORECASE)
#remove duplicate spaces
message_without_joker = re.sub(r'\s{2,}', ' ', message_without_joker)
if joker_in_message and len(message) > 50 and self.watson_usage:
top_answer = get_analysis(message_without_joker)
return f"Hmm, you're talking about {top_answer}"
if joker_in_message and len(message.strip()) == len("joker"):
return "Something wrong is not right, text me what "\
"you told my creators!" \
"Type /info to learn more."
elif joker_in_message:
return self.comm.get_response(self.clean(message_without_joker))
Application.py
def text_message(self, bot, update):
self.send_type_action(bot, update)
if not self.check_for_emotion(update):
message = update.effective_message.text
answer = self.comm.respond(message)
if answer:
update.effective_message.reply_text(answer)
return 0

Related

Is there a way to get the last chat massage in shout or say?

I want to search the last message for a few strings and then echo the message back with those strings replaced with other strings.
I searched multiple documentations but didn't find a way to get the last message.
This is the first forum I ask as I already have an account so have no real starting point to give you.
Thanks in advance!
There is no way in the WoW API to get the last chat message of a specific channel. You will have to handle the CHAT_MSG_CHANNEL event (see Event Handling) to read all messages, and store the newest one. Specifically for the say or yell (shout) channels there are the CHAT_MSG_SAY and CHAT_MSG_YELL events respectively.
To do this your addon needs to own a Frame, these frames can register event handlers and you will have to store the last message you receive from that handler in a local variable in your script (let's call it last_message). Then when your other piece of code executes you can read the last_message variable:
local frame = CreateFrame("FRAME", "FooAddonFrame");
local last_message = nil;
frame:RegisterEvent("CHAT_MSG_CHANNEL");
local function eventHandler(self, event, ...)
-- Look up the arguments that are given to your specific event
-- and assign them to variables in order by retrieving them from
-- the `...` variable arguments
local msg, author, language, channel = ...
print("Hello World! Hello " .. event);
last_message = msg
end
frame:SetScript("OnEvent", eventHandler);

How to create context for the chatbot using twilio

I am trying to create a bot where when ever someone send a message about a type of food to the bot, then the bot will respond with the location that serves that food. However I am trying to establish context so that the conversation can flow more thoroughly.
I have tried nesting the if statement, and it gets it to display the message, but it would have to rely on the if-statement prior to be true before testing for the ones that comes after.
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
from intents import fallback_intent, getLocation
import random
app = Flask(__name__)
location_fallback = ['What kind of restaurant are you seeking?', 'What kind? Nearby, Cheap or The best?']
welcome = ['hello', 'what\'s up', 'hey','hi', 'what\'s happening?']
near = ['near', 'nearby']
cheap = ['cheap', 'good for my pockets']
good = ['good', 'top rated']
intro_resp = ['''Hey! Welcome to Crave! This interactive platform connects you to the top foodies in the world! We provide you with the best food places where ever you are. The instructions are simple:
1. Save our number in your Phone as Crave.
2. Text us and tell us what type of food you are craving!
This is from python''', '''
Welcome to Crave! Are you ready to get some food for today?
1. Save our number in your Phone as Crave.
2. Text us and tell us what type of food you are craving!
''']
#app.route('/sms', methods=['GET','POST'])
def sms():
num = request.form['From']
msg = request.form['Body'].lower()
resp = MessagingResponse()
#welcome intent
if any(word in msg for word in welcome):
if any(near_word in msg for near_word in near):
resp.message('These are the location of places near you!')
print(str(msg.split()))
return str(resp)
elif any(cheap_word in msg for cheap_word in cheap):
resp.message('These are the location of places that are low cost to you!')
return str(resp)
elif any(good_word in msg for good_word in good):
resp.message('These are the best places in town!')
return str(resp)
else:
location_fallback[random.randint(0,1)]
resp.message(intro_resp[random.randint(0, 1)])
print(str(msg.split()))
return str(resp)
else:
resp.message(fallback_intent())
print(str(msg))
return str(resp)
if __name__ == '__main__':
app.run(debug=True)
I want the user to say 'hi'' or something related to initiate the bot, then I want the bot to prompt the user to ask what kind of food they would like. Then the bot will ask what parameters for the restaurant they would like(i.e Close, cheap, or good). Then the user will answer accordingly, and then the bot needs to use these parameters to search for the restaurant near them with these attributes.
Twilio developer evangelist here.
You could store this in many places, in cookies as part of the conversation with Twilio, in a database where you use the user's number as a key to look up previous messages, or even just in memory.
If you're looking for a more robust way to achieve this, with better natural language processing, have you checked out Twilio Autopilot? It stores the context of a conversation for you and is built to collect information before giving a response based on the complete set like you are doing.

getAdGroupBidLandscape returning no campaigns found

I'm trying to use the Google AdWords bid simulator system to try and get some insights out of the AdWords bid simulator. More specifically I'm using the AdGroupBidLandscape() functionality, but it's returning 'No Campaigns Found', but we definitely have campaigns where the Bid Simulator tool works through the AdWords web page interface, so I'm a bit confused. Here is the code I'm running, and yes I know I'm only retrieving a single field - I'm just trying to keep things as simple as possible.
from googleads import adwords
import logging
import time
CHUNK_SIZE = 16 * 1024
PAGE_SIZE = 100
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
adwords_client = adwords.AdWordsClient.LoadFromStorage()
dataService = adwords_client.GetService('DataService', version='v201710')
offset = 0
selector = {'fields':['Bid'], #'impressions', 'promotedImpressions', 'requiredBudget', 'bidModifier', 'totalLocalImpressions', 'totalLocalClicks', 'totalLocalCost', 'totalLocalPromotedImpressions'],
'paging': {
'startIndex': str(offset),
'numberResults': str(PAGE_SIZE)
}
}
more_pages = True
while more_pages:
page = dataService.getAdGroupBidLandscape(selector)
# Display results.
if 'entries' in page:
for campaign in page['entries']:
print ('Campaign with id "%s", name "%s", and status "%s" was '
'found.' % (campaign['id'], campaign['name'],
campaign['status']))
else:
print 'No campaigns were found.'
offset += PAGE_SIZE
selector['paging']['startIndex'] = str(offset)
more_pages = offset < int(page['totalNumEntries'])
time.sleep(1)
We have several different accounts attachd to AdWords. My account is the only one that has developer API access, so I sort of wonder if the problem is that my account isn't the primary account associated with the campaigns- I just have one of the few administrator accounts. Can anyone provide some insights about this for me?
Thanks,
Brad
The solution I found to this problem was to add a predicate to the selector specifying a particular CampaignId. While that doesn't make any sense to me that it would fix it, because it should really just be filtering the data with that if I understand things correctly, it seems to have. I don't have a good explanation for that, but I thought someone else might find this useful. If I come to realize this wasn't the fix to the problem I had, I will come back and update this answer.

Stop parsing when hitting an empty line

I have a Rails app parsing incoming e-mails on Heroku using the Cloud-mailin add-on. The app recieves a list of prices in an e-mail and inserts them into the database.
This works fine, but if the e-mail contains for instance a signature in the bottom the code fails because it's also trying to parse that text.
Therefor I would like to rewrite the below parsing code to stop when it hits an empty line in the e-mail. All the price data is always at the top of the e-mail.
email_text = params[:plain]
email_text_array = []
email_text.split("\n").each do |email_line|
email_text_array << email_line.split(" ")
end
How do I change the above to stop when it hits an empty line in the email_taxt variable?
Thanks!
You can add a break :
email_text.split("\n").each do |email_line|
break if email_line.blank? # ends loop on first empty line
email_text_array << email_line.split(" ")
end
Does this question help: Is there a "do ... while" loop in Ruby?
Edit 1:
From the above article I think something like this would work:
email_text.split("\n").each do |email_line|
break if email_line.length < 1
email_text_array << email_line.split(" ")
end

Excluding author from peer reviewer list in gerrit

I'm setting up the access control for my company in gerrit and in our current internal process has cross-over between peer reviewers and coders (they tend to be the same group of people). We also want to only require 1 reviewer to peer review the code and submit it if it looks good.
With the default setup any user with the +2: Looks good to me, approved option can peer review their own code.
Is there any way to prevent the author from reviewing their own code, but still allow them to fully review other's code? I haven't been able to find any kind of exclude author in the access control group setup or permissions setups.
The Gerrit Cookbook Example 8 does not strictly prevent the Author to review his/her own change, but will require someone else to +2 it before being able to submit.
This is working for me, but it's a quick hack:
allows a configurable number of +1s to count as a +2 for manual submission
optionally automatically submit with enough +1 votes
optionally counts -1 votes as countering +1 votes for the purposes of the tally
optionally ignores the uploader's own +1 (you may prefer a check against the author, which I've not done)
I've tweaked my earlier answer so it doesn't assume you're using a mysql server.
You might want to move the logfile somewhere it'll be subject to any normal log rotation - perhaps in ../logs/comment-added.log.
I've tried to pull the configurable bits to the fore. Call this file comment-hook and
put it in $gerrit_root/hooks, chmod it 755 or similar. Set up a robot user in the admin
group so the hook can use the sql interface (and comment +2 on things with enough +1s).
#!/usr/bin/perl
#
# comment-hook for a +2 approval from a simple quorum of +1 votes.
#
# Licence: Public domain. All risk is yours; if it breaks, you get to keep both pieces.
$QUORUM = 2; # Total number of +1 votes causing a +2
$PLEBIANS = 'abs(value) < 2'; # or 'value = 1' to ignore -1 unvotes
$AUTO_SUBMIT_ON_QUORACY = '--submit'; # or '' for none
$AND_IGNORE_UPLOADER = 'and uploader_account_id != account_id'; # or '' to let uploaders votes count
$GERRIT_SSH_PORT = 29418;
$SSH_PRIVATE_KEY = '/home/gerrit2/.ssh/id_rsa';
$SSH_USER_IN_ADMIN_GROUP = 'devuser';
# Hopefully you shouldn't need to venture past here.
$SSH = "ssh -i $SSH_PRIVATE_KEY -p $GERRIT_SSH_PORT $SSH_USER_IN_ADMIN_GROUP\#localhost";
$LOG = "/home/gerrit2/hooks/log.comment-added";
open LOG, ">>$LOG" or die;
sub count_of_relevant_votes {
# Total selected code review votes for this commit
my $relevance = shift;
$query = "
select sum(value) from patch_sets, patch_set_approvals
where patch_sets.change_id = patch_set_approvals.change_id
and patch_sets.patch_set_id = patch_set_approvals.patch_set_id
and revision = '$V{commit}'
and category_id = 'CRVW'
and $relevance
$AND_IGNORE_UPLOADER
;";
$command = "$SSH \"gerrit gsql -c \\\"$query\\\"\"";
#print LOG "FOR... $command\n";
#lines = qx($command);
chomp #lines;
#print LOG "GOT... ", join("//", #lines), "\n";
# 0=headers 1=separators 2=data 3=count and timing.
return $lines[2];
}
sub response {
my $review = shift;
return "$SSH 'gerrit review --project=\"$V{project}\" $review $V{commit}'";
}
# ######################
# Parse options
$key='';
while ( $_ = shift #ARGV ) {
if (/^--(.*)/) {
$key = $1;
}
else {
$V{$key} .= " " if exists $V{$key};
$V{$key} .= $_;
}
}
#print LOG join("\n", map { "$_ = '$V{$_}'" } keys %V), "\n";
# ######################
# Ignore my own comments
$GATEKEEPER="::GATEKEEPER::";
if ($V{comment} =~ /$GATEKEEPER/) {
# print LOG localtime() . "$V{commit}: Ignore $GATEKEEPER comments\n";
exit 0;
}
# ######################
# Forbear to analyse anything already +2'd
$submittable = count_of_relevant_votes('value = 2');
if ($submittable > 0) {
# print LOG "$V{commit} Already +2'd by someone or something.\n";
exit 0;
}
# ######################
# Look for a consensus amongst qualified voters.
$plebicite = count_of_relevant_votes($PLEBIANS);
#if ($V{comment} =~ /TEST:(\d)/) {
# $plebicite=$1;
#}
# ######################
# If there's a quorum, approve and submit.
if ( $plebicite >= $QUORUM ) {
$and_submitting = ($AUTO_SUBMIT_ON_QUORACY ? " and submitting" : "");
$review = " --code-review=+2 --message=\"$GATEKEEPER approving$and_submitting due to $plebicite total eligible votes\" $AUTO_SUBMIT_ON_QUORACY";
}
else {
$review = " --code-review=0 --message=\"$GATEKEEPER ignoring $plebicite total eligible votes\"";
# print LOG "$V{commit}: $review\n";
exit 0;
}
$response = response($review);
print LOG "RUNNING: $response\n";
$output = qx( $response 2>&1 );
if ($output =~ /\S/) {
print LOG "$V{commit}: output from commenting: $output";
$response = response(" --message=\"During \Q$review\E: \Q$output\E\"");
print LOG "WARNING: $response\n";
$output = qx( $response 2>&1 );
print LOG "ERROR: $output\n";
}
exit 0;
Gerrit allows you to set up prolog "submit rules" that define when a change is submittable.
The documentation includes several examples, including one that prevents the author from approving his own change.
You can do it from the GUI in the access tab.
Go to the /refs/heads/ section -> add group 'change owner' in Label Code-Review section -> choose -1..+1
This will make the change owner to privilege for giving -1 to +1
I have just written this prolog filter for our Gerrit installation. I did it as a submit_filter in the parent project because I wanted it to apply to all projects in our system.
%filter to require all projects to have a code-reviewer other than the owner
submit_filter(In, Out) :-
%unpack the submit rule into a list of code reviews
In =.. [submit | Ls],
%add the non-owner code review requiremet
reject_self_review(Ls, R),
%pack the list back up and return it (kinda)
Out =.. [submit | R].
reject_self_review(S1, S2) :-
%set O to be the change owner
gerrit:change_owner(O),
%find a +2 code review, if it exists, and set R to be the reviewer
gerrit:commit_label(label('Code-Review', 2), R),
%if there is a +2 review from someone other than the owner, then the filter has no work to do, assign S2 to S1
R \= O, !,
%the cut (!) predicate prevents further rules from being consulted
S2 = S1.
reject_self_review(S1, S2) :-
%set O to be the change owner
gerrit:change_owner(O),
% find a +2 code review, if it exists, and set R to be the reviewer - comment sign was missing
gerrit:commit_label(label('Code-Review', 2), R),
R = O, !,
%if there isn't a +2 from someone else (above rule), and there is a +2 from the owner, reject with a self-reviewed label
S2 = [label('Self-Reviewed', reject(O))|S1].
%if the above two rules didn't make it to the ! predicate, there aren't any +2s so let the default rules through unfiltered
reject_self_review(S1, S1).
The benefits (IMO) of this rule over rule #8 from the cookbook are:
The Self-Reviewed label is only shown when the the change is being blocked, rather than adding a Non-Author-Code-Review label to every change
By using reject(O) the rule causes the Self-Reviewed label to literally be a red flag
As a submit_filter instead of a submit_rule, this rule is installed in a parent project and applies to all sub-projects
Please Note: This rule is authored to prevent the Owner from self-reviewing a change, while the example from the cookbook compares against the Author. Depending on your workflow, you may want to replace the 2 gerrit:change_owner(O) predicates with gerrit:commit_author(O) or gerrit:commit_committer(O)

Resources