Skip to content

Commit

Permalink
Added support for custom team fields, members of the same custom team…
Browse files Browse the repository at this point in the history
… don't meet. Closes #5.
  • Loading branch information
dblock committed Aug 21, 2017
1 parent 3a497b4 commit bf119b7
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 90 deletions.
36 changes: 23 additions & 13 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2017-08-20 14:29:34 -0400 using RuboCop version 0.49.1.
# on 2017-08-21 11:33:21 -0400 using RuboCop version 0.49.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
Expand All @@ -19,36 +19,46 @@ Lint/EndAlignment:
Exclude:
- 'slack-sup/models/team.rb'

# Offense count: 14
# Offense count: 1
Lint/UselessAssignment:
Exclude:
- 'slack-sup/models/user.rb'

# Offense count: 17
Metrics/AbcSize:
Max: 40
Max: 44

# Offense count: 52
# Offense count: 55
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Max: 162
Max: 206

# Offense count: 1
# Offense count: 2
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 126
Max: 150

# Offense count: 5
# Offense count: 7
Metrics/CyclomaticComplexity:
Max: 11

# Offense count: 244
# Offense count: 272
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 256
Max: 250

# Offense count: 11
# Offense count: 14
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 31
Max: 36

# Offense count: 1
# Configuration parameters: CountKeywordArgs.
Metrics/ParameterLists:
Max: 6

# Offense count: 5
# Offense count: 7
Metrics/PerceivedComplexity:
Max: 11

Expand Down
1 change: 1 addition & 0 deletions slack-sup/api/presenters/user_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module UserPresenter
property :user_id, type: String, desc: 'Slack user ID.'
property :user_name, type: String, desc: 'Slack user name.'
property :real_name, type: String, desc: 'Slack real name.'
property :custom_team_name, type: String, desc: 'Custom team name from the user profile.'
property :is_admin, type: Boolean, desc: 'User is an admin.'
property :enabled, type: Boolean, desc: 'User is enabled.'
property :opted_in, type: Boolean, desc: "User is opted into S'Up."
Expand Down
17 changes: 9 additions & 8 deletions slack-sup/commands/help.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ class Help < SlackRubyBot::Commands::Base
User
----
opt in|out - opt in/out of S'Up
opt in|out - opt in/out of S'Up
General
-------
help - this helpful message
about - more helpful info about this bot
help - this helpful message
about - more helpful info about this bot
Team Admins
-----------
set day [day of week] - set the day to S'Up, default is Monday
set tz [timezone] - set team timezone, default is Eastern Time (US & Canada)
set api on|off - enable/disable API access to your team data
opt in|out [@mention] - opt a user in/out of S'Up by @mention
subscription - show team ubscription info
set day [day of week] - set the day to S'Up, default is Monday
set timezone [tz] - set team timezone, default is Eastern Time (US & Canada)
set api [on|off] - enable/disable API access to your team data
set team field [name] - set the name of the custom profile team field (users in the same team don't meet)
opt [in|out] [@mention] - opt a user in/out of S'Up by @mention
subscription - show team ubscription info
More information at https://sup.playplay.io
```
Expand Down
117 changes: 74 additions & 43 deletions slack-sup/commands/set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,100 +4,131 @@ class Set < SlackRubyBot::Commands::Base
include SlackSup::Commands::Mixins::Subscribe

class << self
def set_api(client, data, user, v)
def set_api(client, team, data, user, v)
if user.is_admin? || v.nil?
client.owner.update_attributes!(api: v.to_b) unless v.nil?
team.api = v.to_b unless v.nil?
message = [
"API for team #{client.owner.name} is #{client.owner.api? ? 'on!' : 'off.'}",
client.owner.api_url
"Team data access via the API is #{team.api_changed? ? 'now ' : ''}#{team.api? ? 'on' : 'off'}.",
team.api_url
].compact.join("\n")
client.say(channel: data.channel, text: message)
team.save! if team.api_changed?
else
client.say(channel: data.channel, text: 'Only a Slack team admin can do this, sorry.')
client.say(channel: data.channel, text: 'Only a Slack team admin can turn data access via the API on and off, sorry.')
end
logger.info "SET: #{client.owner} - #{user.user_name} API is #{client.owner.api? ? 'on' : 'off'}"
logger.info "SET: #{team}, user=#{user.user_name}, api=#{team.api? ? 'on' : 'off'}"
end

def unset_api(client, data, user)
if user.is_admin?
client.owner.update_attributes!(api: false)
client.say(channel: data.channel, text: "API for team #{client.owner.name} is off.")
logger.info "UNSET: #{client.owner} - #{user.user_name} API is off"
def set_day(client, team, data, user, v)
if user.is_admin? || v.nil?
team.sup_wday = Date.parse(v).wday unless v.nil?
client.say(channel: data.channel, text: "Team S'Up is#{team.sup_wday_changed? ? ' now' : ''} on #{team.sup_day}.")
team.save! if team.sup_wday_changed?
else
client.say(channel: data.channel, text: 'Only a Slack team admin can do this, sorry.')
client.say(channel: data.channel, text: "Team S'Up is on #{team.sup_day}. Only a Slack team admin can change that, sorry.")
end
logger.info "SET: #{team}, user=#{user.user_name}, sup_day=#{team.sup_day}."
rescue ArgumentError
raise SlackSup::Error, "Day _#{v}_ is invalid, try _Monday_, _Tuesday_, etc. Team S'Up is on #{team.sup_day}."
end

def set_day(client, data, user, v)
def set_timezone(client, team, data, user, v)
if user.is_admin? || v.nil?
client.owner.sup_wday = Date.parse(v).wday unless v.nil?
client.say(channel: data.channel, text: "Team S'Up is#{client.owner.sup_wday_changed? ? ' now' : ''} on #{client.owner.sup_day}.")
client.owner.save! if client.owner.sup_wday_changed?
unless v.nil?
timezone = ActiveSupport::TimeZone.new(v)
raise SlackSup::Error, "TimeZone _#{v}_ is invalid, see https://github.com/rails/rails/blob/5.1.3/activesupport/lib/active_support/values/time_zone.rb#L30 for a list. Team S'Up timezone is currently #{team.sup_tzone}." unless timezone
team.sup_tz = timezone.name
end
client.say(channel: data.channel, text: "Team S'Up timezone is#{team.sup_tz_changed? ? ' now' : ''} #{team.sup_tzone}.")
team.save! if team.sup_tz_changed?
else
client.say(channel: data.channel, text: "Team S'Up is on #{client.owner.sup_day}. Only a Slack team admin can change that, sorry.")
client.say(channel: data.channel, text: "Team S'Up timezone is #{team.sup_tzone}. Only a Slack team admin can change that, sorry.")
end
logger.info "SET: #{client.owner} - #{user.user_name} team S'Up is on #{client.owner.sup_day}."
rescue ArgumentError
raise SlackSup::Error, "Day _#{v}_ is invalid, try _Monday_, _Tuesday_, etc. Team S'Up is on #{client.owner.sup_day}."
logger.info "SET: #{team} user=#{user.user_name}, timezone=#{team.sup_tzone}."
end

def set_timezone(client, data, user, v)
def set_custom_profile_team_field(client, team, data, user, v = nil)
if user.is_admin? || v.nil?
unless v.nil?
timezone = ActiveSupport::TimeZone.new(v)
raise SlackSup::Error, "TimeZone _#{v}_ is invalid, see https://github.com/rails/rails/blob/5.1.3/activesupport/lib/active_support/values/time_zone.rb#L30 for a list. Team S'Up timezone is currently #{client.owner.sup_tzone}." unless timezone
client.owner.sup_tz = timezone.name
team.team_field_label = v unless v.nil?
if team.team_field_label_changed?
team.save!
client.say(channel: data.channel, text: "Custom profile team field is now _#{team.team_field_label || 'not set'}_.")
else
client.say(channel: data.channel, text: "Custom profile team field is _#{team.team_field_label || 'not set'}_.")
end
client.say(channel: data.channel, text: "Team S'Up timezone is#{client.owner.sup_tz_changed? ? ' now' : ''} #{client.owner.sup_tzone}.")
client.owner.save! if client.owner.sup_tz_changed?
else
client.say(channel: data.channel, text: "Team S'Up timezone is #{client.owner.sup_tzone}. Only a Slack team admin can change that, sorry.")
client.say(channel: data.channel, text: "Custom profile team field is _#{team.team_field_label || 'not set'}_. Only a Slack team admin can change that, sorry.")
end
logger.info "SET: #{client.owner} - #{user.user_name} team S'Up timezone is #{client.owner.sup_tzone}."
logger.info "SET: #{team}, user=#{user.user_name}, team_field_label=#{team.team_field_label || '(not set)'}."
end

def set(client, data, user, k, v)
def unset_custom_profile_team_field(client, team, data, user)
if user.is_admin?
team.team_field_label = nil
if team.team_field_label_changed?
team.save!
client.say(channel: data.channel, text: 'Custom profile team field is now _not set_.')
else
client.say(channel: data.channel, text: 'Custom profile team field is not set.')
end
else
client.say(channel: data.channel, text: "Custom profile team field is _#{team.team_field_label || 'not set'}_. Only a Slack team admin can change that, sorry.")
end
logger.info "UNSET: #{team}, user=#{user.user_name}, team_field_label=#{team.team_field_label || '(not set)'}."
end

def set(client, team, data, user, k, v)
case k
when 'api' then
set_api client, data, user, v
set_api client, team, data, user, v
when 'day' then
set_day client, data, user, v
set_day client, team, data, user, v
when 'tz', 'timezone' then
set_timezone client, data, user, v
set_timezone client, team, data, user, v
when 'teamfield' then
set_custom_profile_team_field client, team, data, user, v
else
raise SlackSup::Error, "Invalid setting _#{k}_, you can _set api on|off_ or _set day_."
raise SlackSup::Error, "Invalid setting _#{k}_, see _help_ for available options."
end
end

def unset(client, data, user, k)
def unset(client, team, data, user, k)
case k
when 'api' then
unset_api client, data, user
set_api client, team, data, user, false
when 'teamfield' then
unset_custom_profile_team_field client, team, data, user
else
raise SlackSup::Error, "Invalid setting _#{k}_, you can _unset api_."
raise SlackSup::Error, "Invalid setting _#{k}_, see _help_ for available options."
end
end

def parse_expression(m)
m['expression']
.gsub(/^team field/, 'teamfield')
.split(/[\s]+/, 2)
end
end

command 'set' do |client, data, match|
user = ::User.find_create_or_update_by_slack_id!(client, data.user)
if !match['expression']
client.say(channel: data.channel, text: 'Missing setting, eg. _set api off_.')
client.say(channel: data.channel, text: 'Missing setting, see _help_ for available options.')
logger.info "SET: #{client.owner} - #{user.user_name}, failed, missing setting"
else
k, v = match['expression'].split(/[\s]+/, 2)
set client, data, user, k, v
k, v = parse_expression(match)
set client, client.owner, data, user, k, v
end
end

command 'unset' do |client, data, match|
user = ::User.find_create_or_update_by_slack_id!(client, data.user)
if !match['expression']
client.say(channel: data.channel, text: 'Missing setting, eg. _unset api_.')
client.say(channel: data.channel, text: 'Missing setting, see _help_ for available options.')
logger.info "UNSET: #{client.owner} - #{user.user_name}, failed, missing setting"
else
k, = match['expression'].split(/[\s]+/, 2)
unset client, data, user, k
k, = parse_expression(match)
unset client, client.owner, data, user, k
end
end
end
Expand Down
10 changes: 10 additions & 0 deletions slack-sup/models/round.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def dm!
def solve(remaining_users)
combination = group(remaining_users)
Ambit.clear! if ran_at + Round::TIMEOUT.seconds < Time.now.utc
Ambit.fail! if same_team?(combination)
Ambit.fail! if met_recently?(combination)
Ambit.fail! if meeting_already?(combination)
Sup.create!(round: self, users: combination)
Expand All @@ -89,6 +90,15 @@ def meeting_already?(users)
end
end

def same_team?(users)
pairs = users.to_a.permutation(2)
pairs.any? do |pair|
pair.first.custom_team_name == pair.last.custom_team_name &&
pair.first.custom_team_name &&
pair.last.custom_team_name
end
end

def met_recently?(users, tt = 3.weeks)
pairs = users.to_a.permutation(2)
pairs.any? do |pair|
Expand Down
28 changes: 28 additions & 0 deletions slack-sup/models/team.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Team
field :sup_tz, type: String, default: 'Eastern Time (US & Canada)'
validates_presence_of :sup_tz

# custom team field
field :team_field_label, type: String
field :team_field_label_id, type: String

field :stripe_customer_id, type: String
field :subscribed, type: Boolean, default: false
field :subscribed_at, type: DateTime
Expand All @@ -20,6 +24,8 @@ class Team
has_many :rounds, dependent: :destroy

after_update :inform_subscribed_changed!
before_validation :validate_team_field_label
before_validation :validate_team_field_label_id

def api_url
return unless api?
Expand Down Expand Up @@ -112,6 +118,11 @@ def sync!
existing_user ||= User.new(user_id: member.id, team: self)
existing_user.user_name = member.name
existing_user.real_name = member.real_name
begin
existing_user.update_custom_profile
rescue StandardError => e
logger.warn "Error updating custom profile for #{existing_user}: #{e.message}."
end
existing_user
end
humans.each do |human|
Expand All @@ -134,6 +145,23 @@ def sync!

private

def validate_team_field_label
return unless team_field_label && team_field_label_changed?
client = Slack::Web::Client.new(token: access_token)
profile_fields = client.team_profile_get.profile.fields
label = profile_fields.detect { |field| field.label.casecmp(team_field_label.downcase).zero? }
if label
self.team_field_label_id = label.id
self.team_field_label = label.label
else
errors.add(:team_field_label, "Custom profile team field _#{team_field_label}_ is invalid. Possible values are _#{profile_fields.map(&:label).join('_, _')}_.")
end
end

def validate_team_field_label_id
self.team_field_label_id = nil unless team_field_label
end

def trial_expired_text
return unless subscription_expired?
"Your S'Up bot trial subscription has expired."
Expand Down
12 changes: 11 additions & 1 deletion slack-sup/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class User

field :user_id, type: String
field :user_name, type: String
field :custom_team_name, type: String
field :is_admin, type: Boolean, default: false
field :real_name, type: String

Expand Down Expand Up @@ -61,6 +62,15 @@ def self.find_create_or_update_by_slack_id!(client, slack_id)
end

def to_s
"user_name=#{user_name}, user_id=#{user_id}, real_name=#{real_name}"
"user_name=#{user_name}, user_id=#{user_id}, real_name=#{real_name}, custom_team_name=#{custom_team_name}"
end

def update_custom_profile
custom_team_name = nil
return unless team.team_field_label_id
client = Slack::Web::Client.new(token: team.access_token)
fields = client.users_profile_get(user: user_id).profile.fields
custom_field_value = fields[team.team_field_label_id] if fields && fields.is_a?(::Slack::Messages::Message)
self.custom_team_name = custom_field_value.value if custom_field_value
end
end
Loading

0 comments on commit bf119b7

Please sign in to comment.