How do I make the bot reply even if user’s message doesn’t 100% match with words I wrote in ‘when’? I want to make the bot trigger if someone sent “hello there”, for example, instead of just hello.
I’ve heard about when include?('example') but I can’t make it work.
require 'telegram/bot'
token = 'x'
Telegram::Bot::Client.run(token) do |bot|
bot.listen do |message|
puts "##{message.from.username}: #{message.text}"
case message.text
when 'hello'
bot.api.send_message(chat_id: message.chat.id, text: "answer")
end
end
end
You can use a regexp to match your text:
case message.text
when /hello/ # matches if there is the 'hello' somewhere in the string
# ...
when /\Ahello/ # matches if the string starts with 'hello'
# ...
end
Or you can use include? in an if condition:
if message.text.include?('hello')
# ...
end
Related
I have a custom matcher in RSpec, that ignores whitespaces / newlines, and just matches content:
RSpec::Matchers.define :be_matching_content do |expected|
match do |actual|
actual.gsub(/\s/,'').should == expected.gsub(/\s/,'')
end
diffable
end
I can use it like this:
body = " some data \n more data"
body.should be_matching_content("some data\nmore wrong data")
However, when a test fails (like the one above), the diff output looks not good:
-some data
-more wrong data
+ some data
+ more data
Is it possible to configure the diffable output? The first line some data is right, but the second more wrong data is wrong. It would be very useful, to only get the second line as the root cause of the failure.
I believe you should disable default diffable behaviour in RSpec and substitute your own implementation:
RSpec::Matchers.define :be_matching_content do |expected|
match do |actual|
#stripped_actual = actual.gsub(/\s/,'')
#stripped_expected = expected.gsub(/\s/,'')
expect(#stripped_actual).to eq #stripped_expected
end
failure_message do |actual|
message = "expected that #{#stripped_actual} would match #{#stripped_expected}"
message += "\nDiff:" + differ.diff_as_string(#stripped_actual, #stripped_expected)
message
end
def differ
RSpec::Support::Differ.new(
:object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
:color => RSpec::Matchers.configuration.color?
)
end
end
RSpec.describe 'something'do
it 'should diff correctly' do
body = " some data \n more data"
expect(body).to be_matching_content("some data\nmore wrong data")
end
end
produces the following:
Failures:
1) something should diff correctly
Failure/Error: expect(body).to be_matching_content("some data\nmore wrong data")
expected that somedatamoredata would match somedatamorewrongdata
Diff:
## -1,2 +1,2 ##
-somedatamorewrongdata
+somedatamoredata
You can use custom differ if you want, even reimplement this whole matcher to a system call to diff command, something like this:
♥ diff -uw --label expected --label actual <(echo " some data \n more data") <(echo "some data\nmore wrong data")
--- expected
+++ actual
## -1,2 +1,2 ##
some data
- more data
+more wrong data
Cheers!
You can override the expected and actual methods that will then be used when generating the diff. In this example, we store the expected and actual values as instance variables and define methods that return the instance variables:
RSpec::Matchers.define :be_matching_content do |expected_raw|
match do |actual_raw|
#actual = actual_raw.gsub(/\s/,'')
#expected = expected_raw.gsub(/\s/,'')
expect(expected).to eq(#actual)
end
diffable
attr_reader :actual, :expected
end
Another example is to match for specific attributes in two different types of objects. (The expected object in this case is a Client model.)
RSpec::Matchers.define :have_attributes_of_v1_client do |expected_client|
match do |actual_object|
#expected = client_attributes(expected_client)
#actual = actual_object.attributes
expect(actual_object).to have_attributes(#expected)
end
diffable
attr_reader :actual, :expected
def failure_message
"expected attributes of a V1 Client view row, but they do not match"
end
def client_attributes(client)
{
"id" => client.id,
"client_type" => client.client_type.name,
"username" => client.username,
"active" => client.active?,
}
end
end
Example failure looks like this:
Failure/Error: is_expected.to have_attributes_of_v1_client(client_active_partner)
expected attributes of a V1 Client view row, but they do not match
Diff:
## -1,6 +1,6 ##
"active" => true,
-"client_type" => #<ClientType id: 2, name: "ContentPartner">,
+"client_type" => "ContentPartner",
"id" => 11,
There is a gem called diffy which can be used.
But it goes through a string line by line and compares them so instead of removing all whitespace you could replace any amount of whitespace with a newline and diff those entries.
This is an example of something you could do to improve your diffs a little bit. I am not 100% certain about where to insert this into your code.
def compare(str1, str2)
str1 = break_string(str1)
str2 = break_string(str2)
return true if str1 == str2
puts Diffy::Diff.new(str1, str2).to_s
return false
end
def break_string(str)
str.gsub(/\s+/,"\n")
end
The diffy gem can be set to produce color output suitable for the terminal.
Using this code would work like this
str1 = 'extra some content'
str2 = 'extra more content'
puts compare(str1, str2)
this would print
extra
-some # red in terminal
+more # green in terminal
content
\ No newline at end of file
I am using Blather to make a chatbot.
I am taking this example from the documentation:
message :chat?, :body => 'hello' do |m|
say m.from, 'world'
end
but I would like it to account for ANY 'case' of hello, i.e. hEllo, HELLO, Hello, and respond with world.
How would I go about doing that?
You should be able to use a regular expression. In this case, just make a simple case-insensitive match:
message :chat?, :body => /hello/i do |m|
# ... etc
If you'll read the "guards" documentation you'll see:
# Hash with regular expression (:body => /exit/)
# Calls the key on the stanza and checks for a match
# Equivalent to stanza.body.match /exit/
message :body => /exit/
I was wondering if anyone can help me understanding the Ruby code below? I'm pretty new to Ruby programming and having trouble understanding the meaning of each functions.
When I run this with my twitter username and password as parameter, I get a stream of twitter feed samples. What do I need to do with this code to only display the hashtags?
I'm trying to gather the hashtags every 30 seconds, then sort from least to most occurrences of the hashtags.
Not looking for solutions, but for ideas. Thanks!
require 'eventmachine'
require 'em-http'
require 'json'
usage = "#{$0} <user> <password>"
abort usage unless user = ARGV.shift
abort usage unless password = ARGV.shift
url = 'https://stream.twitter.com/1/statuses/sample.json'
def handle_tweet(tweet)
return unless tweet['text']
puts "#{tweet['user']['screen_name']}: #{tweet['text']}"
end
EventMachine.run do
http = EventMachine::HttpRequest.new(url).get :head => { 'Authorization' => [ user, password ] }
buffer = ""
http.stream do |chunk|
buffer += chunk
while line = buffer.slice!(/.+\r?\n/)
handle_tweet JSON.parse(line)
end
end
end
puts "#{tweet['user']['screen_name']}: #{tweet['text']}"
That line shows you a user name followed by the content of the tweet.
Let's take a step back for a sec.
Hash tags appear inside the tweet's content--this means they're inside tweet['text']. A hash tag always takes the form of a # followed by a bunch of non-space characters. That's really easy to grab with a regex. Ruby's core API facilitates that via String#scan. Example:
"twitter is short #foo yawn #bar".scan(/\#\w+/) # => ["#foo", "#bar"]
What you want is something like this:
def handle_tweet(tweet)
return unless tweet['text']
# puts "#{tweet['user']['screen_name']}: #{tweet['text']}" # OLD
puts tweet['text'].scan(/\#\w+/).to_s
end
tweet['text'].scan(/#\w+/) is an array of strings. You can do whatever you want with that array. Supposing you're new to Ruby and want to print the hash tags to the console, here's a brief note about printing arrays with puts:
puts array # => "#foo\n#bar"
puts array.to_s # => '["#foo", "#bar"]'
#Load Libraries
require 'eventmachine'
require 'em-http'
require 'json'
# Looks like this section assumes you're calling this from commandline.
usage = "#{$0} <user> <password>" # $0 returns the name of the program
abort usage unless user = ARGV.shift # Return first argument passed when program called
abort usage unless password = ARGV.shift
# The URL
url = 'https://stream.twitter.com/1/statuses/sample.json'
# method which, when called later, prints out the tweets
def handle_tweet(tweet)
return unless tweet['text'] # Ensures tweet object has 'text' property
puts "#{tweet['user']['screen_name']}: #{tweet['text']}" # write the result
end
# Create an HTTP request obj to URL above with user authorization
EventMachine.run do
http = EventMachine::HttpRequest.new(url).get :head => { 'Authorization' => [ user, password ] }
# Initiate an empty string for the buffer
buffer = ""
# Read the stream by line
http.stream do |chunk|
buffer += chunk
while line = buffer.slice!(/.+\r?\n/) # cut each line at newline
handle_tweet JSON.parse(line) # send each tweet object to handle_tweet method
end
end
end
Here's a commented version of what the source is doing. If you just want the hashtag, you'll want to rewrite handle_tweet to something like this:
handle_tweet(tweet)
tweet.scan(/#\w/) do |tag|
puts tag
end
end
How can I achieve something like this:
listen_for /say (REGEX_THAT_CAPTURES_EVERYTHING_AFTER_SAY)/i do |message|
puts "Message: #{message}"
end
With what do I have to replace (REGEX_THAT_CAPTURES_EVERYTHING_AFTER_SAY) so that everything after say is captured:
Input
say Hello there
Result
Message: Hello there
/say (.+)$/
maybe?
I am parsing a text file and want to be able to extend the sets of tokens that can be recognized easily. Currently I have the following:
if line =~ /!DOCTYPE/
puts "token doctype " + line[0,20]
#ast[:doctype] << line
elsif line =~ /<html/
puts "token main HTML start " + line[0,20]
html_scanner_off = false
elsif line =~ /<head/ and not html_scanner_off
puts "token HTML header starts " + line[0,20]
html_header_scanner_on = true
elsif line =~ /<title/
puts "token HTML title " + line[0,20]
#ast[:HTML_header_title] << line
end
Is there a way to write this with a yield block, e.g. something like:
scanLine("title", :HTML_header_title, line)
?
Don't parse HTML with regexes.
That aside, there are several ways to do what you're talking about. One:
class Parser
class Token
attr_reader :name, :pattern, :block
def initialize(name, pattern, block)
#name = name
#pattern = pattern
#block = block
end
def process(line)
#block.call(self, line)
end
end
def initialize
#tokens = []
end
def scanLine(line)
#tokens.find {|t| line =~ t.pattern}.process(line)
end
def addToken(name, pattern, &block)
#tokens << Token.new(name, pattern, block)
end
end
p = Parser.new
p.addToken("title", /<title/) {|token, line| puts "token #{token.name}: #{line}"}
p.scanLine('<title>This is the title</title>')
This has some limitations (like not checking for duplicate tokens), but works:
$ ruby parser.rb
token title: <title>This is the title</title>
$
If you're intending to parse HTML content, you might want to use one of the HTML parsers like nokogiri (http://nokogiri.org/) or Hpricot (http://hpricot.com/) which are really high-quality. A roll-your-own approach will probably take longer to perfect than figuring out how to use one of these parsers.
On the other hand, if you're dealing with something that's not quite HTML, and can't be parsed that way, then you'll need to roll your own somehow. There's a few Ruby parser frameworks out there that may help, but for simple tasks where performance isn't a critical factor, you can get by with a pile of regexps like you have here.