powerball 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|