Ruby "include?" method not working - ruby

I have a few commands from a Twitch chat, it works and it's awesome! People can type commands like !about and it's going to send a response. There's just one problem, if you go down to the thread and look under # // COMMANDS // COMMANDS // COMMANDS. There's a problem, there´s a few admin commands (look under # ADMIN COMMANDS // ADMIN COMMANDS). I have this: if msg.include?("!project") then msg.slice!("!project "), but it's not working like I want it to. I want to be able to type !project Then some substring here. But when a substring is attached it doesn't work, even though I can print msg and it clearly includes !project. Both the !disconnect and !project command work when it's only those commands by themselves. Meaning that something like !disconnect Hello World wouldn't work even though it clearly includes !disconnect.
# Message formatting in console
class String
def red; "\e[31m#{self}\e[0m" end
def yellow; "\e[33m#{self}\e[0m" end
def green; "\e[32m#{self}\e[0m" end
def cyan; "\e[36m#{self}\e[0m" end
def bold; "\e[1m#{self}\e[22m" end
end
# Requied packages / modules
require 'socket'
require 'logger'
require 'open-uri'
# Create logger
File.delete("log.txt") # Clear previous log
log = Logger.new("log.txt", formatter: proc {|severity, datetime, progname, msg|
"#{datetime}: #{msg}\n"})
# Required Info
load "credentials.txt"
log.info("Loading \"credentials.txt\"")
# -------- IGNORE -------- #
OAUTH.downcase!
BOTNAME.downcase!
CHANNEL.downcase!.gsub!("#", "")
# //- AGE -// #
time = Time.new
age = time.year - 2000
if time.month == 10
if time.day < 28
age -= 1
end
elsif time.month < 10
age -= 1
end
# -------- IGNORE -------- #
# Save "Preparing to connect" to "log.txt"
log.info("Preparing to connect")
# Variables
socket = TCPSocket.new('irc.chat.twitch.tv', 6667)
send = "PRIVMSG ##{CHANNEL} :" # shortcut for sending messages
running = true
content = nil
message_count = 0
message_limit = Time.now.to_i
# Commands
commands = ["!about","!uptime","!commands","!cortexio","!followed"]
api_commands = ["!followed","!uptime"]
admin_commands = ["!disconnect","!project"]
# Authorization Login
socket.puts("PASS #{OAUTH}") # Send the password(oauth) to Twitch
socket.puts("NICK #{BOTNAME}") # Send the botname to Twitch
socket.puts("JOIN ##{CHANNEL}") # Send the channel to Twitch
# Save "Connected!" to "log.txt
log.info("Joining #{CHANNEL.capitalize} as #{BOTNAME.capitalize} using OAUTH Token: #{OAUTH[6,OAUTH.length-12]}" + "*"*12)
# Thread.abort_on_exception = true
# Loop (Background Thread) for recieving Twitch chat data
Thread.start do
socket.puts(send + "Connected!") # Send "Connected!" to the Twitch channel
puts "#{BOTNAME} Joined ##{CHANNEL}" # Connection Status
puts "You should be fully connected now" # Connection Status
puts ""
puts "Type \"clear\" to clear terminal"
puts ""
while (running) do
ready = IO.select([socket])
ready[0].each do |s|
line = s.gets
# Respond to Twitch IRC "PING" Message
if line.index("PING") == 0
line.strip!
socket.puts("PONG :tmi.twitch.tv\r\n")
log.info("[IRC Message]: " + line)
log.info("[IRC Response]: PONG :tmi.twitch.tv")
puts("-".bold.red*line.length)
puts "[Twitch] ".bold.cyan + "IRC: ".bold.yellow + line.bold.green
puts "[Response] ".bold.cyan + "IRC: ".bold.yellow + "PONG :tmi.twitch.tv".bold.green
puts("-".bold.red*line.length)
end
match = line.match(/^:(.+)!(.+)PRIVMSG ##{CHANNEL} :(.+)$/)
message = match && match[3]
if message =~ /^/
message.strip!
user = match[1] # Get username
# Twitch message limit - (Max 100 messages in 30 secs - Applies to mods and above)
# Avoid global ban
if Time.now.to_i - message_limit > 30 # If more than 30 seconds has passed
message_count = 0 # Reset "message_count"
end
if message_count == 0 # If "message_count" is 0
message_limit = Time.now.to_i # Start counting to 30 again
end
message_count = message_count + 1
end
# // COMMANDS // COMMANDS // COMMANDS
if message != nil
msg = message.downcase
# ADMIN COMMANDS // ADMIN COMMANDS
if admin_commands.include?(msg) and user == CHANNEL
if msg.include?("!disconnect")
socket.puts(send + "Disconnecting") # Disconnect from the channel
socket.puts("PART ##{CHANNEL}") # Disconnect from the channel
Disconnect()
log.info("[Command] #{user}: #{message}")
elsif msg.include?("!project")
msg.slice!("!project ")
File.write("Responses/project.txt", msg)
end
user = user.capitalize # Capitalize first letter (Cuz I'm that kind of person)
elsif commands.include?(msg) and message_count < 80
puts "[Command] ".bold.cyan + "#{user}: ".bold + "#{message}".bold.cyan
# API COMMANDS // API COMMANDS
if api_commands.include?(msg)
if msg.include?("!uptime")
file = open("https://decapi.me/twitch/uptime?channel=#{CHANNEL}")
content = "#{CHANNEL} has been streaming for: " + file.read
elsif msg.include?("!followed")
file = open("https://decapi.me/twitch/followage/#{CHANNEL}/#{user}")
content = file.read
if content == "Follow not found"
content = "#{user} is not following #{CHANNEL}"
else
content = "#{user} has been following #{CHANNEL} for " + content
end
end
puts "[Response] ".bold.red + "Cortexio: ".bold + "API: ".bold.yellow + "\"#{content}\"".bold.red
else
file = open "Responses/" + msg.gsub!("!","") + ".txt" # open matching file
content = file.read
file.close
puts "[Response] ".bold.red + "Cortexio: ".bold + "File: ".bold.yellow + "\"#{msg}.txt\"".bold.red
end
file.close
log.info("[Command] #{user}: #{message}")
else
puts "[Message] ".bold.green + "#{user}: ".bold + "#{message}".bold.green
log.info("[Message] #{user}: #{message}")
if message[0] == "!" # Unrecognized command
content = "Unrecognized command: \"#{message}\" - Type !commands to see a list of available commands."
end
end
# Response handling
if content != nil
content.gsub!("USER", "##{user}")
content.gsub!("AGE", "#{age}")
content.gsub!("CHANNEL", "#{CHANNEL}")
if content.include?("COMMANDS")
content.gsub!("COMMANDS", "#{commands}")
content.gsub!("\"", "")
content.gsub!("[","")
content.gsub!("]","")
end
socket.puts(send + content) # Send response if any
content = nil # Too avoid multiple messages with the same response
end
end
end
end
end
def Disconnect() # End script
running = false
exit
end
# Loop to keep bot going
while (running) do
input = gets.chomp
if input == "clear"
system "clear" or system "cls"
end
end

The problem isn't with
if msg.include?("!project")
it's with the line before it:
admin_commands.include?(msg)
admin_commands is an array which contains the 2 strings ["!disconnect","!project"]. It does not, however, contain the string "!project Then some substring here", so you are never checking if msg.include?("!project"). You should be seeing your log messages about an unrecognized command, which means you aren't making it into your first if statement. What you'll want to be doing is something like:
if admin_commands.any? { |command| msg.include?(command) }
if msg.include?("!project")
# ...

Related

Sending data to TCPServer more than one time

I'm new to ruby and I'm trying to make a client to connect to a TCPServer, and it seems that in order to do so I have to call the method close_write every time I finish sending data one way, to let the client/server know that the other end is finished sending data. Whenever I do that then Im not able to write info to the server or client again because the socket is not opened for writing anymore.
This is my code:
client.rb
require "socket"
socket = TCPSocket.open("localhost", 6666)
loop do
input = gets.chomp
socket.puts input # Send data to server
socket.close_write
while(line = socket.gets)
puts line
end # Print sever response
break if input=="EXIT"
end
socket.close
server.rb
require "socket"
server = TCPServer.new 6666
data = Hash.new { |hash, key| hash[key] = {} }
WAITING_SET_VALUE = "1"
WAITING_NEW_COMMAND = "0"
loop do
Thread.start(server.accept) do |session|
thread_status ||= WAITING_NEW_COMMAND
....
puts "Entering If..."
if(thread_status == WAITING_NEW_COMMAND) #Check thread status
puts "thread_status == WAITING_NEW_COMMAND"
puts "CHECKING COMMAND.."
case line.strip
when /^set \w* \w* \d{1,7} \d{1,7}$/
puts "COMMAND SET"
thread_status = WAITING_SET_VALUE
lineArr = line.strip.split(" ")
varName = lineArr[1]
flag = lineArr[2]
ttl = lineArr[3]
size = lineArr[4]
puts "END SET EXECUTION"
session.write "Executed"
session.close_write
...
Is there a way to open the socket for writing again, or a better way to do this back and forth connection between server and client without losing context? Thanks!
When designing a client-server protocol, you have to decide:
How a client knows when a response has more lines/data.
How a client knows when a response is complete.
How a client knows when a response is invalid/valid.
How a client knows when there was some type of server error.
A simple approach is for the server to return a response with the number of lines (as in the code below). However, instead, you could use END or something so that the client knows when there is no more data to read. I would strongly suggest looking into tutorials about Protocols.
Save the below into a file called client_server.rb. First, run the server with ruby ./client_server.rb s and then in a separate terminal run the client with ruby ./client_server.rb c. Next, type in list over and over to see the different responses. I added list so that you don't have to type in set w w 1 1 over and over for testing purposes. Check it out and let me know if you have any questions.
# frozen_string_literal: true
require 'socket'
# Use:
# First, run the server: ruby ./client_server.rb s
# Then, run the client: ruby ./client_server.rb c
# Use "netcat localhost 6666" on the command line to test
# without implementing a client.
# Returns false to close this client socket.
# Returns true to continue reading from this client socket.
def handle_client(client_id, client_socket, command)
# TODO: Define some type of client-server Protocol here.
case command
when /^set \w* \w* \d{1,7} \d{1,7}$/
puts "Running command for client #{client_id}: #{command}"
# This is just for testing purposes.
case rand(3)
when 0
client_socket.puts 'lines 0'
when 1
client_socket.puts 'lines 3'
client_socket.puts 'This is line 1.'
client_socket.puts 'This is line 2.'
client_socket.puts 'This is line 3.'
when 2
client_socket.puts 'The set command returned an error.'
end
when 'list'
puts "Responding to client #{client_id} with list of messages."
# This is just for testing purposes.
case rand(3)
when 0
client_socket.puts 'messages 0'
when 1
client_socket.puts 'messages 2'
client_socket.puts 'This is message 1.'
client_socket.puts 'This is message 2.'
when 2
client_socket.puts 'Unable to retrieve messages due to error.'
end
when 'bye'
puts "Killing client #{client_id}."
return false # Disconnect and kill the thread.
else
client_socket.puts "[ERROR] Invalid command: #{command}"
end
client_socket.flush # Flush all output just in case.
true # Continue connection.
end
case ARGV[0].to_s.downcase
when 's' # server
TCPServer.open(6666) do |server_socket|
global_client_id = 1
loop do
Thread.start(global_client_id, server_socket.accept) do |client_id, client_socket|
puts "Accepting new client #{client_id}."
loop do
command = client_socket.gets
if command.nil?
puts "Client #{client_id} disconnected manually."
break
end
command = command.strip
keep_alive = handle_client(client_id, client_socket, command)
break unless keep_alive
end
client_socket.close
end
global_client_id += 1
end
end
when 'c' # client
TCPSocket.open('localhost', 6666) do |socket|
puts '[Commands]'
puts 'set <word> <word> <num> <num> Run set command.'
puts 'list Get list of messages.'
puts 'exit, bye Exit the client.'
puts
loop do
print '> '
input = $stdin.gets.strip
# TODO: Define some type of client-server Protocol here.
case input
when /EXIT|BYE/i
socket.puts 'bye'
socket.flush
break
when /\Aset .+\z/
socket.puts input
socket.flush
response = socket.gets
if response.nil?
puts 'Server is not running anymore! Disconnecting.'
break
end
response = response.strip
match_data = response.match(/\Alines (?<lines>\d+)\z/)
if match_data
line_count = match_data[:lines].to_i
puts "Received #{line_count} lines from server."
1.upto(line_count) do |i|
line = socket.gets
puts ">> Resp[#{i}] #{line}"
end
else
# Can check for "response == 'ERROR'" or something.
puts "Server error or invalid response from server: #{response}"
end
when 'list'
socket.puts input
socket.flush
response = socket.gets
if response.nil?
puts 'Server is not running anymore! Disconnecting.'
break
end
response = response.strip
match_data = response.match(/\Amessages (?<messages>\d+)\z/)
if match_data
message_count = match_data[:messages].to_i
puts "Received #{message_count} messages from server."
1.upto(message_count) do |i|
line = socket.gets
puts ">> Resp[#{i}] #{line}"
end
else
# Can check for "response == 'ERROR'" or something.
puts "Server error or invalid response from server: #{response}"
end
else
puts "Invalid command: #{input}"
end
end
end
else
puts "Pass in 'c' for client or 's' for server."
end

Put contents of array all at once

I don't understand why this won't do what the title states.
#!/usr/bin/env ruby
require 'socket'
require 'timeout'
class Scanner
def initialize(host, port)
#host = host
#port = port
end
def popen
begin
array = []
sock = Socket.new(:INET, :STREAM)
sockaddr = Socket.sockaddr_in(#port, #host)
Timeout::timeout(5) do
array.push("Port #{#port}: Open") if sock.connect(sockaddr)
end
puts array
rescue Timeout::Error
puts "Port #{#port}: Filtered"
rescue Errno::ECONNREFUSED
end
end
end # end Scanner
def main
begin
p = 1
case ARGV[0]
when '-p'
eport = ARGV[1]
host = ARGV[2]
else
eport = 65535
host = ARGV[0]
end
t1 = Time.now
puts "\n"
puts "-" * 70
puts "Scanning #{host}..."
puts "-" * 70
while p <= eport.to_i do
scan = Scanner.new(host, p)
scan.popen
p += 1
end
t2 = Time.now
time = t2 - t1
puts "\nScan completed: #{host} scanned in #{time} seconds."
rescue Errno::EHOSTUNREACH
puts "This host appears to be unreachable"
rescue Interrupt
puts "onnection terminated."
end
end
main
What I'm trying to achieve is an output similar to nmap, in the way that it scans everything, and then shows all open or closed ports at the end. Instead what happens is that it prints them out as it discovers them. I figured pushing the output into an array then printing the array would achieve such an output, yet it still prints out the ports one at a time. Why is this happening?
Also, I apologize for the formatting, the code tags are a little weird.
Your loop calls popen once per iteration. Your popen method sets array = [] each time it is called, then populates it with one item, then you print it with puts. On the next loop iteration, you reset array to [] and do it all again.
You only asked "why," but – you could solve this by setting array just once in the body of main and then passing it to popen (or any number of ways).

Ruby output is not displayed on the sinatra browser

I want to bulid a multi threaded application. If i do not use threads, everything works fine. When i try to use threads, then nothing is displayed on the browser. when i use the syntax 'puts "%s" %io.read' then it displays on the command prompt and not on the browser. Any help would be appreciated.
require 'sinatra'
require 'thread'
set :environment, :production
get '/price/:upc/:rtype' do
Webupc = "#{params[:upc]}"
Webformat = "#{params[:rtype]}"
MThread = Thread.new do
puts "inside thread"
puts "a = %s" %Webupc
puts "b = %s" %Webformat
#call the price
Maxupclen = 16
padstr = ""
padupc = ""
padlen = (Maxupclen - Webupc.length)
puts "format type: #{params[:rtype]}"
puts "UPC: #{params[:upc]}"
puts "padlen: %s" %padlen
if (Webformat == 'F')
puts "inside format"
if (padlen == 0 ) then
IO.popen("tstprcpd.exe #{Webupc}")
{ |io|
"%s" %io.read
}
elsif (padlen > 0 ) then
for i in 1 .. padlen
padstr = padstr + "0"
end
padupc = padstr + Webupc
puts "padupc %s" %padupc
IO.popen("tstprcpd.exe #{padupc}") { |io|
"%s" %io.read
}
elsif (padlen < 0 ) then
IO.popen("date /T") { |io|
"UPC length must be 16 digits or less." %io.read
}
end
end
end
end
Your code has several problems:
It is not formatted properly
You are using Uppercase names for variables; that makes them constants!
puts will not output to the browser, but to the console. The browser will recieve the return value of the block, i.e. the return value of the last statement in the block. Therefore, you need to build your output differently (see below).
You are never joining the thread
Here's a minimal sinatra app that uses a thread. However, the thread makes no sense in this case because you must wait for its termination anyway before you can output the result to the browser. In order to build the output I have used StringIO, which you can use with puts to build a multiline string conveniently. However, you could also simply initialize res with an empty string with res = "" and then append your lines to this string with res << "new line\n".
require 'sinatra'
require 'thread'
require 'stringio'
get '/' do
res = StringIO.new
th = Thread.new do
res.puts 'Hello, world!'
end
th.join
res.string
end

How can I do readline arguments completion?

I have a Ruby app which uses readline with command completion.
After the first string (the command) was typed, I would like to be able to complete its arguments. The arguments list should be based on the chosen command.
Does someone have a quick example?
These are the commands:
COMMANDS = [
'collect', 'watch'
].sort
COLLECT = [
'stuff', 'otherstuff'
].sort
comp = proc do |s|
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
Readline.completion_proc = comp
Each time I press TAB, the proc block is executed and a command from the COMMANDS array is matched.
After one of the commands was fully matched I would like to start searching for the argument only in the COLLECT array.
Since your question popped up first every time I looked for something like this I want to share my code for any one else.
#!/usr/bin/env ruby
require 'readline'
module Shell
PROMPT = "shell> "
module InputCompletor
CORE_WORDS = %w[ clear help show exit export]
SHOW_ARGS = %w[ list user ]
EXPORT_ARGS = %w[ file ]
COMPLETION_PROC = proc { |input|
case input
when /^(show|export) (.*)/
command = $1
receiver = $2
DISPATCH_TABLE[$1].call($2)
when /^(h|s|c|e.*)/
receiver = $1
CORE_WORDS.grep(/^#{Regexp.quote(receiver)}/)
when /^\s*$/
puts
CORE_WORDS.map{|d| print "#{d}\t"}
puts
print PROMPT
end
}
def self.show(receiver)
if SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"show #{SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
def self.export(receiver)
if EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"export #{EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
DISPATCH_TABLE = {'show' => lambda {|x| show(x)} ,
'export' => lambda {|x| export(x)}}
end
class CLI
Readline.completion_append_character = ' '
Readline.completer_word_break_characters = "\x00"
Readline.completion_proc = Shell::InputCompletor::COMPLETION_PROC
def initialize
while line = Readline.readline("#{PROMPT}",true)
Readline::HISTORY.pop if /^\s*$/ =~ line
begin
if Readline::HISTORY[-2] == line
Readline::HISTORY.pop
end
rescue IndexError
end
cmd = line.chomp
case cmd
when /^clear/
system('clear')
when /^help/
puts 'no help here'
when /show list/
puts 'nothing to show'
when /^show\s$/
puts 'missing args'
when /export file/
puts 'nothing to export'
when /^export\s$/
puts 'missing args'
when /^exit/
exit
end
end
end
end
end
Shell::CLI.new
After thinking a while, the solution was very simple:
comp = proc do |s|
if Readline.line_buffer =~ /^.* /
COLLECT.grep( /^#{Regexp.escape(s)}/ )
else
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
end
Now I just need to turn it into something more flexible/usable.

streaming html from webrick?

Has anyone tried streaming html/text/content from webrick? I've tried assigning an IO to the response body, but webrick is waiting for the stream to be closed first.
found this link by accident (http://redmine.ruby-lang.org/attachments/download/161) which contains webrick patch
# Copyright (C) 2008 Brian Candler, released under Ruby Licence.
#
# A collection of small monkey-patches to webrick.
require 'webrick'
module WEBrick
class HTTPRequest
# Generate HTTP/1.1 100 continue response. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18459
def continue
if self['expect'] == '100-continue' && #config[:HTTPVersion] >= "1.1"
#socket.write "HTTP/#{#config[:HTTPVersion]} 100 continue\r\n\r\n"
#header.delete('expect')
end
end
end
class HTTPResponse
alias :orig_setup_header :setup_header
# Correct termination of streamed HTTP/1.1 responses. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18454 and
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18565
def setup_header
orig_setup_header
unless chunked? || #header['content-length']
#header['connection'] = "close"
#keep_alive = false
end
end
# Allow streaming of zipfile entry. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18460
def send_body(socket)
if #body.respond_to?(:read) then send_body_io(socket)
elsif #body.respond_to?(:call) then send_body_proc(socket)
else send_body_string(socket)
end
end
# If the response body is a proc, then we invoke it and pass in
# an object which supports "write" and "<<" methods. This allows
# arbitary output streaming.
def send_body_proc(socket)
if #request_method == "HEAD"
# do nothing
elsif chunked?
#body.call(ChunkedWrapper.new(socket, self))
_write_data(socket, "0#{CRLF}#{CRLF}")
else
size = #header['content-length'].to_i
#body.call(socket) # TODO: StreamWrapper which supports offset, size
#sent_size = size
end
end
class ChunkedWrapper
def initialize(socket, resp)
#socket = socket
#resp = resp
end
def write(buf)
return if buf.empty?
data = ""
data << format("%x", buf.size) << CRLF
data << buf << CRLF
socket = #socket
#resp.instance_eval {
_write_data(socket, data)
#sent_size += buf.size
}
end
alias :<< :write
end
end
end
if RUBY_VERSION < "1.9"
old_verbose, $VERBOSE = $VERBOSE, nil
# Increase from default of 4K for efficiency, similar to
# http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/lib/net/protocol.rb?r1=11708&r2=12092
# In trunk the default is 64K and can be adjusted using :InputBufferSize,
# :OutputBufferSize
WEBrick::HTTPRequest::BUFSIZE = 16384
WEBrick::HTTPResponse::BUFSIZE = 16384
$VERBOSE = old_verbose
end
to use simply pass a proc to as the response body, like so
res.body = proc { |w|
10.times do
w << Time.now.to_s
sleep(1)
end
}
woot!
I would suggest against using WEBrick for anything really, it's junk. I would say try Mongrel.
I know that wasn't your question, it's just some friendly advice.

Resources