powerball 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +60 -0
- data/bin/powerball +81 -0
- data/lib/powerball.rb +5 -0
- data/lib/powerball/lottery.rb +77 -0
- data/lib/powerball/scheduler.rb +15 -0
- data/lib/powerball/slack.rb +136 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
data/bin/powerball
ADDED
@@ -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
|
+
|
data/lib/powerball.rb
ADDED
@@ -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,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: []
|