Skip to content

Instantly share code, notes, and snippets.

@sstarr
Forked from alexyoung/testbot.rb
Created March 23, 2010 12:05
Show Gist options
  • Save sstarr/341094 to your computer and use it in GitHub Desktop.
Save sstarr/341094 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# Basic titlebot for JsChat (http://jschat.org)
# Run 'gem install eventmachine' (with sudo if required) if your computer doesn't have it
# gem might also be gem1.8 if you got it from Debian/ubuntu
require 'rubygems'
require 'eventmachine'
require 'json'
require 'open-uri'
require 'twitter'
require 'hpricot'
require 'timeout'
ROOM = '#jschat'
PORT = 6789
SERVER = 'localhost'
NAME = 'titlebot'
BOT_MASTER = 'simon'
URL_REGEX = /\b(([\w-]+:\/\/?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|\/)))/
TIMEOUT = 5
TOTAL_BYTES = 1024 * 10
# Twitter login details for twatting URLs
TWIT_LOGIN = 'your_login'
TWIT_PASS = 'your_password'
module JsClient
module Trigger
# You can change these
TRIGGERS = [
# Simple
{:match => /^hai$/i, :response => 'Hello' },
# Using the sender's name
{:match => /!greet/, :response => Proc.new { |response| ["Hello #{response['user']}", "Hai #{response['user']}, sup"][rand(2)] }},
# Matching and random responses
{:match => /y\/n/, :response => Proc.new { |response| ['Yes.', 'No.', 'nah'][rand(3)] }},
{:match => /c\/d/, :response => Proc.new { |response| ['Denied.', 'Confirmed.'][rand(2)] }},
{:match => URL_REGEX, :response => Proc.new { |response| Utility.get_title(response['message'].match(URL_REGEX)[1]) } }
]
def self.random(response)
run_trigger(response, TRIGGERS[rand(TRIGGERS.size)])
end
def self.triggered?(response)
get_trigger(response)
end
def self.get_trigger(response)
TRIGGERS.find { |trigger| trigger[:match] =~ response['message'] }
end
def self.run(response)
run_trigger(response, get_trigger(response))
end
def self.run_trigger(response, trigger)
return nil if trigger.nil?
return trigger[:response] if trigger[:response].class == String
trigger[:response].call(response)
end
end
class Utility
def self.get_page_head(url)
html = nil
status = Timeout::timeout(TIMEOUT) do
open(url) do |page|
html = page.read(TOTAL_BYTES)
end
end
html
end
def self.get_title(url)
head = get_page_head(url)
return unless head
doc = Hpricot.parse(head)
title = (doc/'title').inner_html.gsub(/\s+/, ' ').strip
tweet("#{url} #{title}")
return title.empty? ? nil : title
rescue
nil
end
def self.tweet(msg)
client = Twitter::Client.new(:login => TWIT_LOGIN, :password => TWIT_PASS)
client.status(:post, msg)
end
end
class Protocol
def initialize(connection)
@connection = connection
end
def legal_commands
%w(message joined quit error join names part identified part_notice quit_notice join_notice)
end
def legal?(command)
legal_commands.include? command
end
def message(json)
if json['room']
"#{Time.now.strftime('%H:%M')} [#{json['room']}] <#{json['user']}> #{json['message']}"
@connection.handle_privmsg json
else
"#{Time.now.strftime('%H:%M')} PRIVATE <#{json['user']}> #{json['message']}"
end
end
def join(json)
@connection.send_names json['room']
"* User #{json['user']} joined #{json['room']}"
end
def join_notice(json)
@connection.names << json['user']
"* User #{json['user']} joined #{json['room']}"
end
def part(json)
"* You left #{json['room']}"
end
def part_notice(json)
@connection.names.delete json['user']
"* #{json['user']} left #{json['room']}"
end
def quit(json)
@connection.names.delete json['user']
"* User #{json['user']} left #{json['room']}"
end
def names(json)
@connection.names = json
"* In this channel: #{json.join(', ')}"
end
def identified(json)
@connection.send_join(ROOM)
puts "* You are now known as #{json['name']}"
end
def error(json)
"* [ERROR] #{json['message']}"
end
alias_method :quit_notice, :quit
end
def post_init
@protocol = Protocol.new self
puts 'Connected, identifying...'
send_identify NAME
send_join ROOM
end
def names=(names)
@nicks = names
end
include EM::Protocols::LineText2
def receive_line(data)
json = JSON.parse(data)
# Execute the json
if json.has_key?('display') and @protocol.legal? json['display']
@protocol.send(json['display'], json[json['display']])
else
puts "* [SERVER] #{data}"
end
rescue Exception => exception
puts "* [CLIENT ERROR] #{exception}"
end
def send_join(channel)
@current_channel = channel
send_data({ 'join' => channel }.to_json + "\n")
end
def send_part(channel = nil)
channel = @current_channel if channel.nil?
send_data({ 'part' => channel }.to_json + "\n")
end
def send_names(channel = nil)
channel = @current_channel if channel.nil? or channel.strip.empty?
send_data({ 'names' => channel }.to_json + "\n")
end
def send_message(line)
send_data({ 'to' => @current_channel, 'send' => line }.to_json + "\n")
end
def send_private_message(user, message)
send_data({ 'to' => user, 'send' => message }.to_json + "\n")
end
def send_identify(username)
send_data({ 'identify' => username }.to_json + "\n")
end
def handle_privmsg(response)
p response
return_str = nil
if response['user'] == NAME
return
end
if response['message'] =~ /^reload!$/ && response['user'] == BOT_MASTER
return_str = "Reloading..."
$RELOADING = true
load __FILE__
$RELOADING = false
elsif Trigger.triggered?(response)
return_str = Trigger.run(response)
end
if return_str
send_message return_str
end
end
end
unless $RELOADING
EM.run do
connection = EM.connect SERVER, PORT, JsClient
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment