powerball 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e6306315e116fab566309e8bc942673d43adafab
4
+ data.tar.gz: 8de5d6dc9814d3ede70f1e5786a79f6698a73650
5
+ SHA512:
6
+ metadata.gz: 13910ff1c18a8b95aaa17037bc40318758659e8a2a9e541dfcdc4b9ba6fb7c2a7eb856ea800a4c9078cced069f63c4f638809a2b24406e60c4c743e8f4732527
7
+ data.tar.gz: d23f59b32a778b4ed6b1467098ba28a208699a74ec1fcb323d2e48b0ba64ba55086bb29db8698866960f905263a8cd47e2b9cb8d41b7fa262f85a84d1449399f
@@ -0,0 +1,60 @@
1
+ # powerball
2
+ ## The stupid simple Slack lotto bot.
3
+
4
+ This is a stupid simple Slack bot that just draws a winner each hour from
5
+ a CSV specified list. It's single purpose in design, so it's quite unlikely
6
+ to be of any use to you.
7
+
8
+ ### Configuration
9
+
10
+ `powerball` reads its configuration from `config.yaml` in the current directory. The
11
+ format looks something like this:
12
+
13
+ ``` Yaml
14
+ ---
15
+ :admins:
16
+ - binford2k
17
+ - meg
18
+ - anna
19
+ :channel: apitesting
20
+ :token: "<my slack api token>"
21
+ :starting: "2017-12-07 13:00 UTC"
22
+ ```
23
+
24
+ The attendee input comes (by default) from `attendees.csv` in the current directory. Its
25
+ format is that of an attendee report dumped from EventBrite. The fields we care about can
26
+ be seen in the [source code](https://github.com/binford2k/powerball/blob/master/lib/powerball/lottery.rb).
27
+
28
+ ### Usage
29
+
30
+ All the options from `config.yaml` can also be specified on the command line. Use the `--help`
31
+ flag for usage.
32
+
33
+ ```
34
+ $ powerball --help
35
+
36
+ Usage : powerball [-t <token>] [-c <channel>] [-a <admins>] [--starting '2017-12-07 13:00 UTC']
37
+
38
+ -- Starts the Powerball lotto bot.
39
+ -t, --token TOKEN Slack token.
40
+ -c, --channel CHANNEL Slack channel.
41
+ -a, --admins ADMINS Comma separated list of admin Slack usernames
42
+ --attendees PATH Path to CSV file from EventBrite containing attendees.
43
+ --winners PATH Path to CSV file to save the winners in.
44
+ --starting TIME Time to start the drawing.
45
+
46
+ -h, --help Displays this help
47
+ ```
48
+
49
+ ## Limitations
50
+
51
+ This is super early in development and has not yet been battle tested. It might eat your kitten.
52
+
53
+ ## Disclaimer
54
+
55
+ I take no liability for the use of this tool.
56
+
57
+ Contact
58
+ -------
59
+
60
+ binford2k@gmail.com
@@ -0,0 +1,81 @@
1
+ #! /usr/bin/env ruby
2
+ require 'powerball'
3
+ require 'optparse'
4
+ require 'yaml'
5
+
6
+ options = {
7
+ :attendees => 'attendees.csv',
8
+ :winners => 'winners.csv',
9
+ :channel => 'apitesting',
10
+ }
11
+ options.merge!(YAML.load_file('config.yaml')) rescue nil
12
+ OptionParser.new { |opts|
13
+ opts.banner = "Usage : powerball [-t <token>] [-c <channel>] [-a <admins>] [--starting '2017-12-07 13:00 UTC']
14
+
15
+ -- Starts the Powerball lotto bot.
16
+ "
17
+
18
+ opts.on("-t TOKEN", "--token TOKEN", "Slack token.") do |arg|
19
+ options[:token] = arg
20
+ end
21
+
22
+ opts.on("-c CHANNEL", "--channel CHANNEL", "Slack channel.") do |arg|
23
+ options[:channel] = arg
24
+ end
25
+
26
+ opts.on("-a ADMINS", "--admins ADMINS", "Comma separated list of admin Slack usernames") do |arg|
27
+ options[:admins] = arg.split
28
+ end
29
+
30
+ opts.on("--attendees PATH", "Path to CSV file from EventBrite containing attendees.") do |arg|
31
+ options[:attendees] = arg
32
+ end
33
+
34
+ opts.on("--winners PATH", "Path to CSV file to save the winners in.") do |arg|
35
+ options[:winners] = arg
36
+ end
37
+
38
+ opts.on("--starting TIME", "Time to start the drawing.") do |arg|
39
+ options[:starting] = arg
40
+ end
41
+
42
+ opts.separator('')
43
+
44
+ opts.on("-h", "--help", "Displays this help") do
45
+ puts
46
+ puts opts
47
+ puts
48
+ exit
49
+ end
50
+ }.parse!
51
+
52
+ lottery = Powerball::Lottery.new(options[:attendees], options[:winners])
53
+ slack = Powerball::Slack.new(options[:token], options[:channel], lottery, options[:admins])
54
+
55
+ Thread.new do
56
+ slack.start!
57
+ end
58
+
59
+ if options[:starting]
60
+ time = [Time.parse(options[:starting]) - Time.now, 0].max
61
+
62
+ if time > 86400
63
+ puts "Drawings start in about #{(time/86400).round} days..."
64
+ elsif time > 3600
65
+ puts "Drawings start in about #{(time/3600).round} hours..."
66
+ elsif time > 60
67
+ puts "Drawings start in about #{(time/60).round} minutes..."
68
+ elsif time > 0
69
+ puts "Drawings start in about #{time} seconds..."
70
+ else
71
+ puts "Starting time expired; drawings commence now!"
72
+ end
73
+
74
+ sleep(time)
75
+ end
76
+
77
+ Clockwork.lottery = lottery
78
+ Clockwork::run
79
+
80
+ Thread.join
81
+
@@ -0,0 +1,5 @@
1
+ class Powerball
2
+ require 'powerball/slack'
3
+ require 'powerball/lottery'
4
+ require 'powerball/scheduler'
5
+ end
@@ -0,0 +1,77 @@
1
+ require 'csv'
2
+
3
+ class Powerball::Lottery
4
+ # first = item[2]
5
+ # last = item[3]
6
+ # email = item[4]
7
+ # slack = item[13]
8
+ # company = item[16]
9
+ # addr1 = item[17]
10
+ # addr2 = item[18]
11
+ # city = item[19]
12
+ # state = item[20]
13
+ # zip = item[21]
14
+ # country = item[22]
15
+ # phone = item[23]
16
+
17
+ def initialize(attendees = 'attendees.csv', winners = 'winners.csv')
18
+ @winners = CSV.read(winners) rescue []
19
+ @attendees = CSV.read(attendees) rescue []
20
+ @header = @attendees.shift # remove header
21
+
22
+ @attendees.reject! do |attendee|
23
+ email = attendee[4].strip.downcase
24
+ company = attendee[16].strip.downcase
25
+
26
+ (company == 'puppet' \
27
+ || email.end_with?('@puppet.com') \
28
+ || email.end_with?('@puppetlabs.com'))
29
+ end
30
+
31
+ @attendees.map! do |attendee|
32
+ attendee[13].strip!
33
+ attendee[13].slice!(0) if attendee[13].start_with?('@')
34
+
35
+ attendee
36
+ end
37
+
38
+ puts "INFO: #{@attendees.size} eligible attendees"
39
+ end
40
+
41
+ def chatroom=(chatroom)
42
+ @chatroom = chatroom
43
+ end
44
+
45
+ def drawing
46
+ @chatroom.start_drawing
47
+
48
+ active = @chatroom.active_members
49
+ pool = @attendees.select do |attendee|
50
+ active.include? attendee[13]
51
+ end
52
+
53
+ puts "INFO: eligible and active attendees: #{pool.map {|a| "#{a[2]} #{a[3]} (#{a[13]})" }.inspect}"
54
+
55
+ if pool.size > 0
56
+ winner = pool.sample
57
+ @winners << winner
58
+ @attendees.delete winner
59
+
60
+ @chatroom.post_winner(winner[13])
61
+ @chatroom.alert_admins("New winner: #{winner[2]} #{winner[3]} (#{winner[13]})")
62
+ write_winners
63
+ else
64
+ @chatroom.alert_admins("WARNING: there are no currently eligible active members.")
65
+ end
66
+ end
67
+
68
+ def write_winners
69
+ CSV.open('winners.csv', "wb") do |csv|
70
+ csv << @header # copy the input header
71
+ @winners.each do |row|
72
+ csv << row
73
+ end
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,15 @@
1
+ require 'clockwork'
2
+ require 'time'
3
+
4
+ module Clockwork
5
+ class << self;
6
+ attr_accessor :lottery
7
+ end
8
+
9
+ handler do |job|
10
+ @lottery.drawing
11
+ end
12
+
13
+ #every(30.seconds, 'frequent.job')
14
+ every(1.hour, 'drawing', :at => '**:00')
15
+ end
@@ -0,0 +1,136 @@
1
+ require 'slack-ruby-client'
2
+
3
+ class Powerball::Slack
4
+
5
+ def initialize(token, channel, lottery, admins = [] )
6
+ Slack.configure do |config|
7
+ config.token = token
8
+ end
9
+
10
+ @client = Slack::RealTime::Client.new
11
+ @lottery = lottery
12
+ @channel = channel
13
+ @admins = admins.map{|a| "@#{a}" }
14
+ @messages = [
15
+ "Yay, it's drawing time!",
16
+ "Oh boy, I get to draw a winner!",
17
+ "You mean I get to pick another winner?! :allthethings:",
18
+ "It's time for another one. :confetti_ball:",
19
+ "Another drawing :game_die:, who's it gonna be?",
20
+ "Party on, Wayne :metal:. Draw us a winner.",
21
+ ]
22
+
23
+ @images = [
24
+ { title: "Powerball!",
25
+ image_url: "https://i.giphy.com/media/Ps8XflhsT5EVa/giphy.gif" },
26
+ { title: "Who's it gonna be?",
27
+ image_url: "http://www.thinkgeek.com/images/products/zoom/2063_critical_hit_led_dice_set.gif" },
28
+ { title: "Let's roll the dice!",
29
+ image_url: "https://studio.code.org/v3/assets/GBhvGLEcbJGFHdJfHkChqw/8TEb9oxGc.gif" },
30
+ { title: "Draw a card, any card...",
31
+ image_url: "https://c1.staticflickr.com/2/1262/1267623453_99002a752a_m.jpg" },
32
+ { title: "Powerball!",
33
+ image_url: "http://a.abcnews.com/images/US/RT_Powerball_Machine_ER_160112_4x3_992.jpg" },
34
+ ]
35
+
36
+ @lottery.chatroom = self
37
+
38
+ @client.on :hello do
39
+ puts "Successfully connected '#{@client.self.name}' to the '#{@client.team.name}' team at https://#{@client.team.domain}.slack.com."
40
+ # bots cannot join channels. They must be invited
41
+ #@client.web_client.channels_join(:name => @channel)
42
+ end
43
+
44
+ @client.on :close do |_data|
45
+ puts 'Connection closing, exiting.'
46
+ end
47
+
48
+ @client.on :closed do |_data|
49
+ puts 'Connection has been disconnected.'
50
+ end
51
+
52
+ @client.on :message do |data|
53
+ puts data
54
+
55
+ case data.text
56
+ when "<@#{@client.self.id}> hi", 'powerball hi', "hi <@#{@client.self.id}>", 'hi powerball' then
57
+ @client.message channel: data.channel, text: "Hi <@#{data.user}>!"
58
+
59
+ when "<@#{@client.self.id}> drawing", 'powerball drawing' then
60
+ username = @client.web_client.users_info(user: data.user)['user']['name']
61
+ next unless admins.include? username
62
+
63
+ @lottery.drawing
64
+ end
65
+ end
66
+ end
67
+
68
+ def start!
69
+ @client.start!
70
+ end
71
+
72
+ def active_members
73
+ begin
74
+ # The api will let you use ID or name, but only if the channel is public!
75
+ list = @client.web_client.conversations_list(:types => 'public_channel, private_channel')
76
+ chan = list['channels'].select { |c| c['name'] == @channel }.first['id']
77
+
78
+ data = @client.web_client.conversations_members( :channel => chan, :limit => 500 )
79
+ data['members'].map do |user|
80
+ # we only want active users!
81
+ next unless @client.web_client.users_getPresence(:user => user)['presence'] == 'active'
82
+
83
+ @client.web_client.users_info(:user => user)['user']['name'] rescue nil
84
+ end.compact
85
+ rescue => e
86
+ puts 'ERROR: cannot list channel members'
87
+ puts e.message
88
+ puts e.backtrace.join "\n"
89
+ []
90
+ end
91
+ end
92
+
93
+ def start_drawing
94
+ begin
95
+ @client.web_client.chat_postMessage({
96
+ channel: @channel,
97
+ text: @messages.sample,
98
+ attachments: [@images.sample],
99
+ as_user: true,
100
+ })
101
+ rescue => e
102
+ puts 'ERROR: cannot start drawing'
103
+ puts e.message
104
+ puts e.backtrace.join "\n"
105
+ end
106
+ end
107
+
108
+ def post_winner(winner)
109
+ begin
110
+ slack = @client.web_client.users_info(user: "@#{winner}")['user']['id']
111
+ @client.web_client.chat_postMessage({
112
+ channel: @channel,
113
+ text: "Congratulations <@#{slack}>, you're our latest winner! :tada: :clap:",
114
+ as_user: true,
115
+ })
116
+ return true
117
+ rescue => e
118
+ puts 'ERROR: drawing incomplete'
119
+ puts e.message
120
+ puts e.backtrace.join "\n"
121
+ return false
122
+ end
123
+ end
124
+
125
+ def alert_admins(message)
126
+ @admins.each do |admin|
127
+ begin
128
+ channel = @client.web_client.im_open(:user => admin)['channel']['id']
129
+ @client.web_client.chat_postMessage({ channel: channel, text: message, as_user: true })
130
+ rescue => e
131
+ puts "ERROR: could not send message to #{admin}"
132
+ end
133
+ end
134
+ end
135
+
136
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: powerball
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Ben Ford
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: clockwork
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slack-ruby-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: |2
42
+ This is a stupid simple Slack bot that just draws a winner each hour from
43
+ a CSV specified list. It's single purpose in design, so it's quite unlikely
44
+ to be of any use to you.
45
+ email: binford2k@gmail.com
46
+ executables:
47
+ - powerball
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - README.md
52
+ - bin/powerball
53
+ - lib/powerball.rb
54
+ - lib/powerball/lottery.rb
55
+ - lib/powerball/scheduler.rb
56
+ - lib/powerball/slack.rb
57
+ homepage: https://github.com/binford2k/powerball/
58
+ licenses:
59
+ - Apache-2.0
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 2.6.10
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: Slack lotto bot.
81
+ test_files: []