commit 42470aceb634c905e8f29ea52da7c491a54d40a5 Author: Mispy <^_^@mispy.me> Date: Fri Dec 20 04:55:36 2013 -0800 Ebooks example repo diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..6059e80 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'http://rubygems.org' +ruby '1.9.3' + +gem 'twitter_ebooks' diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..9357bc0 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: ruby run.rb start diff --git a/README.md b/README.md new file mode 100644 index 0000000..c60ca8d --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# ebooks_example + +As requested, this is the [twitter_ebooks](https://github.com/mispy/twitter_ebooks) app which I use to run most of my own bots. It tweets one guaranteed tweet every 24h, always responds to interactions, and has some small unprompted interaction probability based on keyword matching. + +## Usage + +```bash +git clone https://github.com/mispy/ebooks_example.git +cd ebooks_example +bundle install +ebooks archive username corpus/username.json +ebooks consume corpus/username.json +``` + +Populate bots.rb with your auth details, the bot username and model name, then: + +`./run.rb` + +Also runs as a Heroku app! See the [twitter_ebooks](https://github.com/mispy/twitter_ebooks) README for more information. diff --git a/bots.rb b/bots.rb new file mode 100644 index 0000000..ac26cb4 --- /dev/null +++ b/bots.rb @@ -0,0 +1,129 @@ +#!/usr/bin/env ruby + +require 'twitter_ebooks' +include Ebooks + +CONSUMER_KEY = "" +CONSUMER_SECRET = "" +DELAY = 2..30 # Simulated human reply delay range in seconds +BLACKLIST = ['insomnius', 'upulie'] # Grumpy users to avoid interaction with + +# Track who we've randomly interacted with globally +$have_talked = {} + +class GenBot + def initialize(bot, modelname) + @bot = bot + @model = nil + + bot.consumer_key = CONSUMER_KEY + bot.consumer_secret = CONSUMER_SECRET + + 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?('ebooks') && rand > 0.05 + + tokens = NLP.tokenize(tweet[:text]) + + very_interesting = tokens.find_all { |t| @top50.include?(t.downcase) }.length > 2 + special = tokens.find { |t| ['ebooks', 'bot', 'bots', 'clone', 'singularity', 'world domination'].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| ['ebooks', 'bot', 'bots', 'clone', 'singularity', 'world domination'].include?(t) } + + if special + favorite(tweet) + + 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 + 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 = {} + 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 + end + end + + def favorite(tweet) + @bot.log "Favoriting @#{tweet[:user][:screen_name]}: #{tweet[:text]}" + @bot.delay DELAY do + @bot.twitter.favorite(tweet[:id]) + end + end + + def retweet(tweet) + @bot.log "Retweeting @#{tweet[:user][:screen_name]}: #{tweet[:text]}" + @bot.delay DELAY do + @bot.twitter.retweet(tweet[:id]) + end + end +end + +def make_bot(bot, modelname) + GenBot.new(bot, modelname) +end + +Ebooks::Bot.new("username_ebooks") do |bot| # Ebooks account username + bot.oauth_token = "" # oauth token for ebooks account + bot.oauth_token_secret = "" # oauth secret for ebooks account + + make_bot(bot, "username") # This should be the name of the text model +end diff --git a/run.rb b/run.rb new file mode 100755 index 0000000..9753116 --- /dev/null +++ b/run.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +require_relative 'bots' + +if ARGV[0] && ARGV[0] != 'start' + Ebooks::Bot.get(ARGV[0]).start +else + EM.run do + Ebooks::Bot.all.each do |bot| + bot.start + end + end +end