mirror of
https://github.com/thewesker/ebooks_example.git
synced 2025-12-19 20:01:12 -05:00
3.0.0 update
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -1,4 +1,4 @@
|
||||
source 'http://rubygems.org'
|
||||
ruby '1.9.3'
|
||||
ruby '2.1.3'
|
||||
|
||||
gem 'twitter_ebooks'
|
||||
|
||||
72
Gemfile.lock
Normal file
72
Gemfile.lock
Normal file
@@ -0,0 +1,72 @@
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
addressable (2.3.6)
|
||||
awesome_print (1.2.0)
|
||||
bloomfilter-rb (2.1.1)
|
||||
redis
|
||||
buftok (0.2.0)
|
||||
coderay (1.1.0)
|
||||
engtagger (0.2.0)
|
||||
equalizer (0.0.9)
|
||||
eventmachine (1.0.3)
|
||||
faraday (0.9.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
fast-stemmer (1.0.2)
|
||||
gingerice (1.2.2)
|
||||
addressable
|
||||
awesome_print
|
||||
highscore (1.2.0)
|
||||
bloomfilter-rb (>= 2.1.1)
|
||||
whatlanguage (>= 1.0.0)
|
||||
htmlentities (4.3.2)
|
||||
http (0.6.3)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
http_parser.rb (0.6.0)
|
||||
json (1.8.1)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
method_source (0.8.2)
|
||||
multipart-post (2.0.0)
|
||||
naught (1.0.0)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
redis (3.1.0)
|
||||
rufus-scheduler (3.0.9)
|
||||
tzinfo
|
||||
simple_oauth (0.3.0)
|
||||
slop (3.6.0)
|
||||
thread_safe (0.3.4)
|
||||
twitter (5.13.0)
|
||||
addressable (~> 2.3)
|
||||
buftok (~> 0.2.0)
|
||||
equalizer (~> 0.0.9)
|
||||
faraday (~> 0.9.0)
|
||||
http (~> 0.6.0)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
json (~> 1.8)
|
||||
memoizable (~> 0.4.0)
|
||||
naught (~> 1.0)
|
||||
simple_oauth (~> 0.3.0)
|
||||
twitter_ebooks (3.0.0)
|
||||
engtagger
|
||||
eventmachine (~> 1.0.3)
|
||||
fast-stemmer
|
||||
gingerice
|
||||
highscore
|
||||
htmlentities
|
||||
pry
|
||||
rufus-scheduler
|
||||
simple_oauth
|
||||
twitter (~> 5.0)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
whatlanguage (1.0.5)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
twitter_ebooks
|
||||
@@ -14,6 +14,6 @@ ebooks consume corpus/username.json
|
||||
|
||||
Populate bots.rb with your auth details, the bot username and model name, then:
|
||||
|
||||
`./run.rb`
|
||||
`ebooks start`
|
||||
|
||||
Also runs as a Heroku app! See the [twitter_ebooks](https://github.com/mispy/twitter_ebooks) README for more information.
|
||||
|
||||
224
bots.rb
224
bots.rb
@@ -1,138 +1,134 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'twitter_ebooks'
|
||||
include Ebooks
|
||||
|
||||
CONSUMER_KEY = ""
|
||||
CONSUMER_SECRET = ""
|
||||
OATH_TOKEN = "" # oauth token for ebooks account
|
||||
OAUTH_TOKEN_SECRET = "" # oauth secret for ebooks account
|
||||
# Information about a particular Twitter user we know
|
||||
class UserInfo
|
||||
attr_reader :username
|
||||
|
||||
ROBOT_ID = "ebooks" # Avoid infinite reply chains
|
||||
TWITTER_USERNAME = "ebooks_username" # Ebooks account username
|
||||
TEXT_MODEL_NAME = "username" # This should be the name of the text model
|
||||
# @return [Integer] how many times we can pester this user unprompted
|
||||
attr_accessor :pesters_left
|
||||
|
||||
DELAY = 2..30 # Simulated human reply delay range in seconds
|
||||
BLACKLIST = ['insomnius', 'upulie'] # Grumpy users to avoid interaction with
|
||||
SPECIAL_WORDS = ['ebooks', 'bot', 'bots', 'clone', 'singularity', 'world domination']
|
||||
# @param username [String]
|
||||
def initialize(username)
|
||||
@username = username
|
||||
@pesters_left = 1
|
||||
end
|
||||
end
|
||||
|
||||
# Track who we've randomly interacted with globally
|
||||
$have_talked = {}
|
||||
class CloneBot < Ebooks::Bot
|
||||
attr_accessor :original, :model_path
|
||||
|
||||
class GenBot
|
||||
def initialize(bot, modelname)
|
||||
@bot = bot
|
||||
@model = nil
|
||||
def configure
|
||||
# Configuration for all CloneBots
|
||||
self.consumer_key = ""
|
||||
self.consumer_secret = ""
|
||||
self.blacklist = ['kylelehk', 'friedrichsays', 'Sudieofna', 'tnietzschequote', 'NerdsOnPeriod', 'FSR', 'BafflingQuotes', 'Obey_Nxme']
|
||||
|
||||
bot.consumer_key = CONSUMER_KEY
|
||||
bot.consumer_secret = CONSUMER_SECRET
|
||||
@userinfo = {}
|
||||
end
|
||||
|
||||
bot.on_startup do
|
||||
@model = Model.load("model/#{modelname}.model")
|
||||
@top100 = @model.keywords.top(100).map(&:to_s).map(&:downcase)
|
||||
@top50 = @model.keywords.top(20).map(&:to_s).map(&:downcase)
|
||||
end
|
||||
|
||||
bot.on_message do |dm|
|
||||
bot.delay DELAY do
|
||||
bot.reply dm, @model.make_response(dm[:text])
|
||||
end
|
||||
end
|
||||
|
||||
bot.on_follow do |user|
|
||||
bot.delay DELAY do
|
||||
bot.follow user[:screen_name]
|
||||
end
|
||||
end
|
||||
|
||||
bot.on_mention do |tweet, meta|
|
||||
# Avoid infinite reply chains (very small chance of crosstalk)
|
||||
next if tweet[:user][:screen_name].include?(ROBOT_ID) && rand > 0.05
|
||||
|
||||
tokens = NLP.tokenize(tweet[:text])
|
||||
|
||||
very_interesting = tokens.find_all { |t| @top50.include?(t.downcase) }.length > 2
|
||||
special = tokens.find { |t| SPECIAL_WORDS.include?(t) }
|
||||
|
||||
if very_interesting || special
|
||||
favorite(tweet)
|
||||
end
|
||||
|
||||
reply(tweet, meta)
|
||||
end
|
||||
|
||||
bot.on_timeline do |tweet, meta|
|
||||
next if tweet[:retweeted_status] || tweet[:text].start_with?('RT')
|
||||
next if BLACKLIST.include?(tweet[:user][:screen_name])
|
||||
|
||||
tokens = NLP.tokenize(tweet[:text])
|
||||
|
||||
# We calculate unprompted interaction probability by how well a
|
||||
# tweet matches our keywords
|
||||
interesting = tokens.find { |t| @top100.include?(t.downcase) }
|
||||
very_interesting = tokens.find_all { |t| @top50.include?(t.downcase) }.length > 2
|
||||
special = tokens.find { |t| SPECIAL_WORDS.include?(t) }
|
||||
|
||||
if special
|
||||
favorite(tweet)
|
||||
favd = true # Mark this tweet as favorited
|
||||
|
||||
bot.delay DELAY do
|
||||
bot.follow tweet[:user][:screen_name]
|
||||
end
|
||||
end
|
||||
|
||||
# Any given user will receive at most one random interaction per day
|
||||
# (barring special cases)
|
||||
next if $have_talked[tweet[:user][:screen_name]]
|
||||
$have_talked[tweet[:user][:screen_name]] = true
|
||||
|
||||
if very_interesting || special
|
||||
favorite(tweet) if (rand < 0.5 && !favd) # Don't fav the tweet if we did earlier
|
||||
retweet(tweet) if rand < 0.1
|
||||
reply(tweet, meta) if rand < 0.1
|
||||
elsif interesting
|
||||
favorite(tweet) if rand < 0.1
|
||||
reply(tweet, meta) if rand < 0.05
|
||||
end
|
||||
end
|
||||
|
||||
# Schedule a main tweet for every day at midnight
|
||||
bot.scheduler.cron '0 0 * * *' do
|
||||
bot.tweet @model.make_statement
|
||||
$have_talked = {}
|
||||
def model
|
||||
@model_path ||= "model/#{original}.model"
|
||||
if @model.nil?
|
||||
log "Loading model #{model_path}"
|
||||
@model = Ebooks::Model.load(model_path)
|
||||
else
|
||||
@model
|
||||
end
|
||||
end
|
||||
|
||||
def reply(tweet, meta)
|
||||
resp = @model.make_response(meta[:mentionless], meta[:limit])
|
||||
@bot.delay DELAY do
|
||||
@bot.reply tweet, meta[:reply_prefix] + resp
|
||||
def top100; @top100 ||= model.keywords.take(100); end
|
||||
def top20; @top20 ||= model.keywords.take(20); end
|
||||
|
||||
def delay(&b)
|
||||
sleep (1..4).to_a.sample
|
||||
b.call
|
||||
end
|
||||
|
||||
def on_startup
|
||||
model
|
||||
|
||||
scheduler.cron '0 0 * * *' do
|
||||
# Each day at midnight, post a single tweet
|
||||
tweet model.make_statement
|
||||
end
|
||||
end
|
||||
|
||||
def on_direct_message(dm)
|
||||
delay do
|
||||
reply dm, model.make_response(dm.text)
|
||||
end
|
||||
end
|
||||
|
||||
def on_mention(tweet)
|
||||
# Become more inclined to pester a user when they talk to us
|
||||
userinfo(tweet.user.screen_name).pesters_left += 1
|
||||
delay do
|
||||
reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit))
|
||||
end
|
||||
end
|
||||
|
||||
def on_timeline(tweet)
|
||||
return if tweet.retweeted_status?
|
||||
tokens = Ebooks::NLP.tokenize(tweet.text)
|
||||
|
||||
interesting = tokens.find { |t| top100.include?(t.downcase) }
|
||||
very_interesting = tokens.find_all { |t| top20.include?(t.downcase) }.length > 2
|
||||
|
||||
return unless can_pester?(tweet.user.screen_name)
|
||||
|
||||
delay do
|
||||
if very_interesting
|
||||
favorite(tweet) if rand < 0.5
|
||||
retweet(tweet) if rand < 0.1
|
||||
reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) if rand < 0.05
|
||||
elsif interesting
|
||||
favorite(tweet) if rand < 0.05
|
||||
reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) if rand < 0.01
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Find information we've collected about a user
|
||||
# @param username [String]
|
||||
# @return [Ebooks::UserInfo]
|
||||
def userinfo(username)
|
||||
@userinfo[username] ||= UserInfo.new(username)
|
||||
end
|
||||
|
||||
# Check if we're allowed to send unprompted tweets to a user
|
||||
# @param username [String]
|
||||
# @return [Boolean]
|
||||
def can_pester?(username)
|
||||
userinfo(username).pesters_left > 0
|
||||
end
|
||||
|
||||
# Only follow our original user or people who are following our original user
|
||||
# @param user [Twitter::User]
|
||||
def can_follow?(username)
|
||||
@original.nil? || username == @original || twitter.friendship?(username, @original)
|
||||
end
|
||||
|
||||
def favorite(tweet)
|
||||
@bot.log "Favoriting @#{tweet[:user][:screen_name]}: #{tweet[:text]}"
|
||||
@bot.delay DELAY do
|
||||
@bot.twitter.favorite(tweet[:id])
|
||||
if !can_follow?(tweet.user.screen_name)
|
||||
log "Unfollowing @#{tweet.user.screen_name}"
|
||||
twitter.unfollow(tweet.user.screen_name)
|
||||
else
|
||||
super(tweet)
|
||||
end
|
||||
end
|
||||
|
||||
def retweet(tweet)
|
||||
@bot.log "Retweeting @#{tweet[:user][:screen_name]}: #{tweet[:text]}"
|
||||
@bot.delay DELAY do
|
||||
@bot.twitter.retweet(tweet[:id])
|
||||
def on_follow(user)
|
||||
if can_follow?(user.screen_name)
|
||||
follow(user.screen_name)
|
||||
else
|
||||
log "Not following @#{user.screen_name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_bot(bot, modelname)
|
||||
GenBot.new(bot, modelname)
|
||||
end
|
||||
CloneBot.new("abby_ebooks") do |bot|
|
||||
bot.access_token = ""
|
||||
bot.access_token_secret = ""
|
||||
|
||||
Ebooks::Bot.new(TWITTER_USERNAME) do |bot|
|
||||
bot.oauth_token = OATH_TOKEN
|
||||
bot.oauth_token_secret = OAUTH_TOKEN_SECRET
|
||||
|
||||
make_bot(bot, TEXT_MODEL_NAME)
|
||||
bot.original = "0xabad1dea"
|
||||
end
|
||||
|
||||
9413
model/0xabad1dea.model
Normal file
9413
model/0xabad1dea.model
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user