-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathteam.rb
More file actions
397 lines (319 loc) · 11.2 KB
/
Copy pathteam.rb
File metadata and controls
397 lines (319 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# frozen_string_literal: true
class Team
field :dead_at, type: DateTime
field :trial_informed_at, type: DateTime
field :past_due_informed_at, type: DateTime
field :stripe_customer_id, type: String
field :subscribed, type: Boolean, default: false
field :subscribed_at, type: DateTime
scope :api, -> { where(api: true) }
field :api, type: Boolean, default: false
field :api_token, type: String
field :elo, type: Integer, default: 0
field :unbalanced, type: Boolean, default: false
field :leaderboard_max, type: Integer
field :gifs, type: Boolean, default: true
field :aliases, type: Array, default: %w[gamebot pongbot pp]
scope :subscribed, -> { where(subscribed: true) }
has_many :channels, dependent: :destroy
has_many :admins, dependent: :destroy
has_many :users
has_many :seasons
has_many :matches
has_many :challenges
before_save :update_subscribed_at
after_update :subscribed!
after_save :activated!
before_destroy :destroy_subscribed_team
def subscription_expired?
return false if subscribed?
time_limit = Time.now.utc - 2.weeks
return false if created_at > time_limit
true
end
def trial_ends_at
raise 'Team is subscribed.' if subscribed?
created_at + 2.weeks
end
def remaining_trial_days
raise 'Team is subscribed.' if subscribed?
[0, (trial_ends_at.to_date - Time.now.utc.to_date).to_i].max
end
def trial_message
[
if remaining_trial_days.zero?
'Your trial subscription has expired.'
else
"Your trial subscription expires in #{remaining_trial_days} day#{'s' unless remaining_trial_days == 1}."
end,
subscribe_text
].join(' ')
end
def inform_trial!
return if subscribed? || subscription_expired?
return if trial_informed_at && (Time.now.utc < trial_informed_at + 7.days)
inform! trial_message
update_attributes!(trial_informed_at: Time.now.utc)
end
def subscribe_text
"Subscribe your team for $49.99 a year at #{SlackRubyBotServer::Service.url}/subscribe?team_id=#{team_id}."
end
def update_cc_text
"Update your credit card info at #{SlackRubyBotServer::Service.url}/update_cc?team_id=#{team_id}."
end
def to_s
{
name: name,
domain: domain,
id: team_id
}.map do |k, v|
"#{k}=#{v}" if v
end.compact.join(', ')
end
def asleep?(dt = 2.weeks)
time_limit = Time.now.utc - dt
return false if created_at > time_limit
recent_match = matches.desc(:updated_at).limit(1).first
return false if recent_match && recent_match.updated_at >= time_limit
recent_challenge = challenges.desc(:updated_at).limit(1).first
return false if recent_challenge && recent_challenge.updated_at >= time_limit
true
end
def dead?(dt = 1.month)
asleep?(dt)
end
def dead!(message)
inform! message
ensure
update_attributes!(dead_at: Time.now.utc)
end
def aliases_s
aliases&.any? ? aliases.map { |a| "`#{a}`" }.and : 'not set'
end
def api_s
api? ? 'on' : 'off'
end
def gifs_s
gifs? ? 'on' : 'off'
end
def unbalanced_s
unbalanced? ? 'on' : 'off'
end
def leaderboard_max_s
leaderboard_max || 'not set'
end
def api_url
return unless api?
"#{SlackRubyBotServer::Service.api_url}/teams/#{id}"
end
def slack_client
@slack_client ||= SlackGamebot::Web::Client.new(token: token, gifs: false)
end
def bot_mention
"<@#{bot_user_id}>" if bot_user_id
end
def slack_channels
raise 'missing bot_user_id' unless bot_user_id
channels = []
slack_client.users_conversations(
user: bot_user_id,
exclude_archived: true,
types: 'public_channel,private_channel'
) do |response|
channels.concat(response.channels)
end
channels
end
def inform!(message)
channels.app_home.each do |channel|
logger.info "Sending '#{message}' to #{self} on app_home channel ##{channel.channel_id}."
slack_client.chat_postMessage(text: message, channel: channel.channel_id, as_user: true)
end
slack_channels.each do |channel|
logger.info "Sending '#{message}' to #{self} on ##{channel['name']}."
slack_client.chat_postMessage(text: message, channel: channel['id'], as_user: true)
end
end
def stripe_customer
return unless stripe_customer_id
@stripe_customer ||= Stripe::Customer.retrieve(stripe_customer_id)
end
def stripe_customer_text
"Customer since #{Time.at(stripe_customer.created).strftime('%B %d, %Y')}."
end
def subscriber_text
subscribed_at ? "Subscriber since #{subscribed_at.strftime('%B %d, %Y')}." : 'Team is subscribed.'
end
def stripe_subcriptions
return unless stripe_customer
stripe_customer.subscriptions
end
def stripe_customer_subscriptions_info(with_unsubscribe = false)
stripe_customer.subscriptions.map do |subscription|
amount = ActiveSupport::NumberHelper.number_to_currency(subscription.plan.amount.to_f / 100)
current_period_end = Time.at(subscription.current_period_end).strftime('%B %d, %Y')
if subscription.status == 'active'
[
"Subscribed to #{subscription.plan.name} (#{amount}), will#{' not' if subscription.cancel_at_period_end} auto-renew on #{current_period_end}.",
!subscription.cancel_at_period_end && with_unsubscribe ? "Send `unsubscribe #{subscription.id}` to unsubscribe." : nil
].compact.join("\n")
else
"#{subscription.status.titleize} subscription created #{Time.at(subscription.created).strftime('%B %d, %Y')} to #{subscription.plan.name} (#{amount})."
end
end
end
def stripe_customer_invoices_info
stripe_customer.invoices.map do |invoice|
amount = ActiveSupport::NumberHelper.number_to_currency(invoice.amount_due.to_f / 100)
"Invoice for #{amount} on #{Time.at(invoice.date).strftime('%B %d, %Y')}, #{invoice.paid ? 'paid' : 'unpaid'}."
end
end
def stripe_customer_sources_info
stripe_customer.sources.map do |source|
"On file #{source.brand} #{source.object}, #{source.name} ending with #{source.last4}, expires #{source.exp_month}/#{source.exp_year}."
end
end
def active_stripe_subscription?
!active_stripe_subscription.nil?
end
def active_stripe_subscription
return unless stripe_customer
stripe_customer.subscriptions.detect do |subscription|
subscription.status == 'active' && !subscription.cancel_at_period_end
end
end
def tags
[
subscribed? ? 'subscribed' : 'trial',
stripe_customer_id? ? 'paid' : nil
].compact
end
def ping_if_active!
return unless active?
ping!
rescue Slack::Web::Api::Errors::SlackError => e
logger.warn "Active team #{self} ping, #{e.message}."
case e.message
when 'account_inactive', 'invalid_auth'
deactivate!
end
end
def find_create_or_update_admin_by_slack_id!(slack_id)
instance = admins.where(user_id: slack_id).first
users_info = begin
slack_client.users_info(user: slack_id)
rescue Slack::Web::Api::Errors::SlackError => e
raise e unless e.message == 'user_not_found'
end
instance_info = Hashie::Mash.new(users_info).user if users_info
if users_info && instance
if instance.user_name != instance_info.name || instance.is_admin != instance_info.is_admin || instance.is_owner != instance_info.is_owner
instance.update_attributes!(
user_name: instance_info.name,
is_owner: instance_info.is_owner,
is_admin: instance_info.is_admin
)
end
elsif !instance && instance_info
instance = admins.create!(
team: self,
user_id: slack_id,
user_name: instance_info.name,
is_owner: instance_info.is_owner,
is_admin: instance_info.is_admin
)
end
raise SlackGamebot::Error, "I don't know who <@#{slack_id}> is!" unless instance
instance
end
def find_create_or_update_channel_by_channel_id!(channel_id, user_id)
raise 'missing channel_id' unless channel_id
return nil if channel_id[0] == 'D'
channel = channels.where(channel_id: channel_id).first
return channel if channel
create_channel!(channel_id, user_id)
end
def find_create_or_update_user_in_channel_by_slack_id!(channel_id, user_id)
channel = find_create_or_update_channel_by_channel_id!(channel_id, user_id)
channel ? channel.find_or_create_by_slack_id!(user_id) : user_id
end
def join_channel!(channel_id, inviter_id)
channel = channels.where(channel_id: channel_id).first
if channel
update_channel!(channel, inviter_id)
else
channel = create_channel!(channel_id, inviter_id)
end
channel
end
def leave_channel!(channel_id)
channel = channels.where(channel_id: channel_id).first
channel&.update_attributes!(enabled: false)
channel || false
end
INSTALLED_TEXT = [
"Hi there! I'm your team's Leaderboard Gamebot.",
"I don't play actual games, but I'll be keeping your leaderboards.",
'Thanks for trying me out. To start, invite me to a channel.',
'You can always DM me `help` for instructions.'
].join("\n")
private
SUBSCRIBED_TEXT = [
"Hi there! I'm your team's Leaderboard Gamebot.",
"I don't play actual games, but I keep your leaderboards.",
'Your team has purchased a yearly subscription.',
'Follow us on X at https://twitter.com/playplayio for news and updates.',
'Thanks for being a customer!'
].join("\n")
def welcome_dm!
channel = slack_client.conversations_open(users: activated_user_id)
logger.info "Sending welcome DM to #{activated_user_id} for #{self}."
slack_client.chat_postMessage(text: INSTALLED_TEXT, channel: channel.channel.id, as_user: true)
end
def subscribed!
return unless subscribed? && (subscribed_changed? || saved_change_to_subscribed?)
inform! SUBSCRIBED_TEXT
end
def activated!
return unless active? && activated_user_id && bot_user_id
return unless active_changed? || activated_user_id_changed? || saved_change_to_active? || saved_change_to_activated_user_id?
welcome_dm!
end
def update_subscribed_at
return unless subscribed_changed?
if subscribed
self.subscribed_at ||= Time.now.utc
else
self.subscribed_at = nil
end
end
def update_channel!(channel, inviter_id)
channel_info = slack_client.conversations_info(channel: channel.channel_id)
logger.info "Updating channel id=#{channel.channel_id}, name=#{channel_info&.channel&.name}, #{self}."
channel.update_attributes!(
enabled: true,
inviter_id: inviter_id,
is_group: !channel_info&.channel&.is_group.nil?
)
end
def create_channel!(channel_id, inviter_id)
# do not support direct message channels for all commands
channel_info = slack_client.conversations_info(channel: channel_id)
return nil if channel_info&.channel&.is_im
logger.info "Creating channel id=#{channel_id}, name=#{channel_info&.channel&.name}, #{self}."
channels.create!(
channel_id: channel_id,
enabled: true,
inviter_id: inviter_id,
is_group: !channel_info&.channel&.is_group.nil?,
elo: elo,
aliases: channel_info&.channel&.is_group ? [] : aliases,
gifs: gifs,
unbalanced: unbalanced,
leaderboard_max: leaderboard_max
)
end
def destroy_subscribed_team
raise 'cannot destroy a subscribed team' if subscribed?
end
end