HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMajor

Chat bot for posting recent answers

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
chatrecentanswerspostingbotfor

Problem

I am the half-robot side of syb0rg that will be posting the recent answers of Code Review to the CR Answers chatroom. Here is the list of review suggestions I would like, in order of preference:

  • Efficiency (with API requests, speed of login and posting answers, etc.)



  • Security issues



  • Best practices



For feature requests regarding the chat bot, please see this meta post.

Any and all reviews are acceptable however. Don't be too harsh please, this is one of my first times using Ruby.

```
ACCESS_TOKEN = ''
# get your access token here:
# https://stackexchange.com/oauth/dialog?client_id=2666&redirect_uri=http://keyboardfire.com/chatdump.html&scope=no_expiry
$root = 'http://stackexchange.com'
$chatroot = 'http://chat.stackexchange.com'
$room_number = 12723
site = 'codereview'
email = ''
password = ''

require 'rubygems'
require 'mechanize'
require 'json'
require 'net/http'

loop
{
begin

$agent = Mechanize.new
$agent.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE

login_form = $agent.get('https://openid.stackexchange.com/account/login').forms.first
login_form.email = email
login_form.password = password
$agent.submit login_form, login_form.buttons.first
puts 'logged in with SE openid'

meta_login_form = $agent.get($root + '/users/login').forms.last
meta_login_form.openid_identifier = 'https://openid.stackexchange.com/'
$agent.submit meta_login_form, meta_login_form.buttons.last
puts 'logged in to root'

chat_login_form = $agent.get('http://stackexchange.com/users/chat-login').forms.last
$agent.submit chat_login_form, chat_login_form.buttons.last
puts 'logged in to chat'

$fkey = $agent.get($chatroot + '/chats/join/favorite').forms.last.fkey
puts 'found fkey'

def send_message text
loop
{
begin
resp = $agent.post("#{$chatroot}/chats/#{$room_number}/messages/new", [['text', text], ['fkey', $fkey]]).body
success = JSON.parse(resp)['id']

Solution

For starters, indent your code consistently — the standard in Ruby is two spaces. That includes indenting the contents of your begin-rescue-end blocks.

Normally, I don't like to make such a huge fuss about indentation, but in this case I think it's highly important, because:

  • Your program has a highly unusual outline (infinite loops and a function definition(!) inside an infinite loop)



  • The stakes are high: if you misbehave, you could make a lot of people upset. Therefore, good software engineering practices should be used.



An outline like this would be more idiomatic for Ruby:

class AnswerBot
  ROOT = 'http://stackexchange.com'
  CHAT_ROOT = 'http://chat.stackexchange.com'

  def initialize(options)
    @agent = Mechanize.new
    @options = options
  end

  def login
    # Do stuff with @agent
    login_form = $agent.get('https://openid.stackexchange.com/account/login').forms.first
    login_form.email = @options[:email]
    # ...
    @fkey = @agent.get(CHAT_ROOT + '/chats/join/favorite').forms.last.fkey
  end

  def fetch_answers
    # Make request to api.stackexchange.com
    # ...
    data['items'].each { |event| yield event }
    return (data['backoff'] || 0).to_i
  end

  def send_message(text, retries=5, backoff=40)
    # ...
  end
end

bot = AnswerBot.new(:access_token => ...,
                    :room_number = 12723,
                    :site => 'codereview',
                    :email => ...,
                    :password => ...)
loop {
  begin
    bot.login

    do
      backoff = bot.fetch_answers do |event|
        if ['answer_posted'].include?(event['event_type']) #  e
    puts "An error occurred."
    p e
  end
  puts "Bot restarted."
}

Code Snippets

class AnswerBot
  ROOT = 'http://stackexchange.com'
  CHAT_ROOT = 'http://chat.stackexchange.com'

  def initialize(options)
    @agent = Mechanize.new
    @options = options
  end

  def login
    # Do stuff with @agent
    login_form = $agent.get('https://openid.stackexchange.com/account/login').forms.first
    login_form.email = @options[:email]
    # ...
    @fkey = @agent.get(CHAT_ROOT + '/chats/join/favorite').forms.last.fkey
  end

  def fetch_answers
    # Make request to api.stackexchange.com
    # ...
    data['items'].each { |event| yield event }
    return (data['backoff'] || 0).to_i
  end

  def send_message(text, retries=5, backoff=40)
    # ...
  end
end

bot = AnswerBot.new(:access_token => ...,
                    :room_number = 12723,
                    :site => 'codereview',
                    :email => ...,
                    :password => ...)
loop {
  begin
    bot.login

    do
      backoff = bot.fetch_answers do |event|
        if ['answer_posted'].include?(event['event_type']) # <-- Is that right?
          bot.send_message(...)
        end
      end
    while sleep(40 + backoff)
  rescue => e
    puts "An error occurred."
    p e
  end
  puts "Bot restarted."
}

Context

StackExchange Code Review Q#44511, answer score: 22

Revisions (0)

No revisions yet.