├── Gemfile ├── Procfile ├── README.md ├── bots.rb ├── corpus └── .gitignore └── model ├── .gitignore └── 0xabad1dea.model /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'twitter_ebooks' 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: bundle exec ebooks start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ebooks_example 2 | 3 | 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. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | git clone https://github.com/mispy/ebooks_example.git 9 | cd ebooks_example 10 | bundle install 11 | ebooks archive username corpus/username.json 12 | ebooks consume corpus/username.json 13 | ``` 14 | 15 | Populate bots.rb with your auth details, the bot username and model name, then: 16 | 17 | `ebooks start` 18 | 19 | Also runs as a Heroku app! See the [twitter_ebooks](https://github.com/mispy/twitter_ebooks) README for more information. 20 | -------------------------------------------------------------------------------- /bots.rb: -------------------------------------------------------------------------------- 1 | require 'twitter_ebooks' 2 | 3 | # Information about a particular Twitter user we know 4 | class UserInfo 5 | attr_reader :username 6 | 7 | # @return [Integer] how many times we can pester this user unprompted 8 | attr_accessor :pesters_left 9 | 10 | # @param username [String] 11 | def initialize(username) 12 | @username = username 13 | @pesters_left = 1 14 | end 15 | end 16 | 17 | class CloneBot < Ebooks::Bot 18 | attr_accessor :original, :model, :model_path 19 | 20 | def configure 21 | # Configuration for all CloneBots 22 | self.consumer_key = "" 23 | self.consumer_secret = "" 24 | self.blacklist = ['kylelehk', 'friedrichsays', 'Sudieofna', 'tnietzschequote', 'NerdsOnPeriod', 'FSR', 'BafflingQuotes', 'Obey_Nxme'] 25 | self.delay_range = 1..6 26 | @userinfo = {} 27 | end 28 | 29 | def top100; @top100 ||= model.keywords.take(100); end 30 | def top20; @top20 ||= model.keywords.take(20); end 31 | 32 | def on_startup 33 | load_model! 34 | 35 | scheduler.cron '0 0 * * *' do 36 | # Each day at midnight, post a single tweet 37 | tweet(model.make_statement) 38 | end 39 | end 40 | 41 | def on_message(dm) 42 | delay do 43 | reply(dm, model.make_response(dm.text)) 44 | end 45 | end 46 | 47 | def on_mention(tweet) 48 | # Become more inclined to pester a user when they talk to us 49 | userinfo(tweet.user.screen_name).pesters_left += 1 50 | 51 | delay do 52 | reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) 53 | end 54 | end 55 | 56 | def on_timeline(tweet) 57 | return if tweet.retweeted_status? 58 | return unless can_pester?(tweet.user.screen_name) 59 | 60 | tokens = Ebooks::NLP.tokenize(tweet.text) 61 | 62 | interesting = tokens.find { |t| top100.include?(t.downcase) } 63 | very_interesting = tokens.find_all { |t| top20.include?(t.downcase) }.length > 2 64 | 65 | delay do 66 | if very_interesting 67 | favorite(tweet) if rand < 0.5 68 | retweet(tweet) if rand < 0.1 69 | if rand < 0.01 70 | userinfo(tweet.user.screen_name).pesters_left -= 1 71 | reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) 72 | end 73 | elsif interesting 74 | favorite(tweet) if rand < 0.05 75 | if rand < 0.001 76 | userinfo(tweet.user.screen_name).pesters_left -= 1 77 | reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) 78 | end 79 | end 80 | end 81 | end 82 | 83 | # Find information we've collected about a user 84 | # @param username [String] 85 | # @return [Ebooks::UserInfo] 86 | def userinfo(username) 87 | @userinfo[username] ||= UserInfo.new(username) 88 | end 89 | 90 | # Check if we're allowed to send unprompted tweets to a user 91 | # @param username [String] 92 | # @return [Boolean] 93 | def can_pester?(username) 94 | userinfo(username).pesters_left > 0 95 | end 96 | 97 | # Only follow our original user or people who are following our original user 98 | # @param user [Twitter::User] 99 | def can_follow?(username) 100 | @original.nil? || username.casecmp(@original) == 0 || twitter.friendship?(username, @original) 101 | end 102 | 103 | def favorite(tweet) 104 | if can_follow?(tweet.user.screen_name) 105 | super(tweet) 106 | else 107 | log "Unfollowing @#{tweet.user.screen_name}" 108 | twitter.unfollow(tweet.user.screen_name) 109 | end 110 | end 111 | 112 | def on_follow(user) 113 | if can_follow?(user.screen_name) 114 | follow(user.screen_name) 115 | else 116 | log "Not following @#{user.screen_name}" 117 | end 118 | end 119 | 120 | private 121 | def load_model! 122 | return if @model 123 | 124 | @model_path ||= "model/#{original}.model" 125 | 126 | log "Loading model #{model_path}" 127 | @model = Ebooks::Model.load(model_path) 128 | end 129 | end 130 | 131 | CloneBot.new("abby_ebooks") do |bot| 132 | bot.access_token = "" 133 | bot.access_token_secret = "" 134 | 135 | bot.original = "0xabad1dea" 136 | end 137 | -------------------------------------------------------------------------------- /corpus/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mispy-archive/ebooks_example/345211684c6b09151f8f8194511c3accbd65fd18/corpus/.gitignore -------------------------------------------------------------------------------- /model/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mispy-archive/ebooks_example/345211684c6b09151f8f8194511c3accbd65fd18/model/.gitignore -------------------------------------------------------------------------------- /model/0xabad1dea.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mispy-archive/ebooks_example/345211684c6b09151f8f8194511c3accbd65fd18/model/0xabad1dea.model --------------------------------------------------------------------------------