slack-smart-bot 1.13.1 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -4
  3. data/lib/slack/smart-bot/ai/open_ai/connect.rb +61 -0
  4. data/lib/slack/smart-bot/ai/open_ai/models.rb +21 -0
  5. data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +24 -0
  6. data/lib/slack/smart-bot/ai/open_ai/send_image_edit.rb +23 -0
  7. data/lib/slack/smart-bot/ai/open_ai/send_image_generation.rb +18 -0
  8. data/lib/slack/smart-bot/ai/open_ai/send_image_variation.rb +22 -0
  9. data/lib/slack/smart-bot/ai/open_ai/whisper_transcribe.rb +21 -0
  10. data/lib/slack/smart-bot/ai.rb +8 -0
  11. data/lib/slack/smart-bot/comm/get_channel_members.rb +15 -13
  12. data/lib/slack/smart-bot/comm/get_channels.rb +31 -29
  13. data/lib/slack/smart-bot/comm/respond_thread.rb +2 -2
  14. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +40 -0
  15. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_edit_image.rb +66 -0
  16. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_generate_image.rb +65 -0
  17. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_models.rb +37 -0
  18. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_variations_image.rb +84 -0
  19. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_whisper.rb +51 -0
  20. data/lib/slack/smart-bot/commands/general/bot_help.rb +1 -0
  21. data/lib/slack/smart-bot/commands/general/personal_settings.rb +38 -0
  22. data/lib/slack/smart-bot/commands/general/poster.rb +107 -104
  23. data/lib/slack/smart-bot/commands/general/public_holidays.rb +116 -114
  24. data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +6 -2
  25. data/lib/slack/smart-bot/commands/general/teams/add_team.rb +87 -0
  26. data/lib/slack/smart-bot/commands/general/teams/delete_team.rb +69 -0
  27. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team.rb +136 -0
  28. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team_comment.rb +37 -0
  29. data/lib/slack/smart-bot/commands/general/teams/memos/delete_memo_team.rb +83 -0
  30. data/lib/slack/smart-bot/commands/general/teams/memos/see_memo_team.rb +97 -0
  31. data/lib/slack/smart-bot/commands/general/teams/memos/see_memos_team.rb +304 -0
  32. data/lib/slack/smart-bot/commands/general/teams/memos/set_memo_status.rb +66 -0
  33. data/lib/slack/smart-bot/commands/general/teams/ping_team.rb +104 -0
  34. data/lib/slack/smart-bot/commands/general/teams/see_teams.rb +236 -0
  35. data/lib/slack/smart-bot/commands/general/teams/see_vacations_team.rb +183 -0
  36. data/lib/slack/smart-bot/commands/general/teams/update_team.rb +137 -0
  37. data/lib/slack/smart-bot/commands/general_bot_commands.rb +905 -741
  38. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +379 -353
  39. data/lib/slack/smart-bot/commands/on_bot/repl.rb +87 -189
  40. data/lib/slack/smart-bot/commands/on_bot/repl_client.rb +233 -0
  41. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +17 -4
  42. data/lib/slack/smart-bot/commands.rb +26 -10
  43. data/lib/slack/smart-bot/process.rb +14 -3
  44. data/lib/slack/smart-bot/process_first.rb +36 -2
  45. data/lib/slack/smart-bot/treat_message.rb +28 -0
  46. data/lib/slack/smart-bot/utils/check_vacations.rb +1 -0
  47. data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
  48. data/lib/slack/smart-bot/utils/display_calendar.rb +17 -10
  49. data/lib/slack/smart-bot/utils/encryption/decrypt.rb +23 -0
  50. data/lib/slack/smart-bot/utils/encryption/encrypt.rb +27 -0
  51. data/lib/slack/smart-bot/utils/{encryption_get_key_iv.rb → encryption/encryption_get_key_iv.rb} +12 -8
  52. data/lib/slack/smart-bot/utils/get_help.rb +3 -1
  53. data/lib/slack/smart-bot/utils/get_personal_settings.rb +14 -0
  54. data/lib/slack/smart-bot/utils/get_teams.rb +2 -2
  55. data/lib/slack/smart-bot/utils/get_vacations.rb +2 -2
  56. data/lib/slack/smart-bot/utils/save_stats.rb +3 -1
  57. data/lib/slack/smart-bot/utils/update_personal_settings.rb +18 -0
  58. data/lib/slack/smart-bot/utils/update_teams.rb +1 -1
  59. data/lib/slack/smart-bot/utils/update_vacations.rb +1 -1
  60. data/lib/slack/smart-bot/utils.rb +5 -3
  61. data/lib/slack-smart-bot.rb +12 -0
  62. data/whats_new.txt +13 -14
  63. metadata +63 -15
  64. data/lib/slack/smart-bot/commands/general/add_memo_team.rb +0 -117
  65. data/lib/slack/smart-bot/commands/general/add_team.rb +0 -81
  66. data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +0 -69
  67. data/lib/slack/smart-bot/commands/general/delete_team.rb +0 -55
  68. data/lib/slack/smart-bot/commands/general/ping_team.rb +0 -100
  69. data/lib/slack/smart-bot/commands/general/see_memos_team.rb +0 -202
  70. data/lib/slack/smart-bot/commands/general/see_teams.rb +0 -230
  71. data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +0 -136
  72. data/lib/slack/smart-bot/commands/general/set_memo_status.rb +0 -58
  73. data/lib/slack/smart-bot/commands/general/update_team.rb +0 -131
  74. data/lib/slack/smart-bot/utils/decrypt.rb +0 -15
  75. data/lib/slack/smart-bot/utils/encrypt.rb +0 -15
@@ -0,0 +1,83 @@
1
+ class SlackSmartBot
2
+ module Commands
3
+ module General
4
+ module Teams
5
+ module Memos
6
+ def delete_memo_team(user, team_name, memo_id)
7
+ save_stats(__method__) if answer.empty?
8
+
9
+ get_teams()
10
+ if @teams.key?(team_name.to_sym)
11
+ assigned_members = @teams[team_name.to_sym].members.values.flatten
12
+ assigned_members.uniq!
13
+ all_team_members = assigned_members.dup
14
+ team_members = []
15
+ if @teams[team_name.to_sym].channels.key?("members")
16
+ @teams[team_name.to_sym].channels["members"].each do |ch|
17
+ get_channels_name_and_id() unless @channels_id.key?(ch)
18
+ tm = get_channel_members(@channels_id[ch])
19
+ tm.each do |m|
20
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
21
+ team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
22
+ end
23
+ end
24
+ end
25
+ team_members.flatten!
26
+ team_members.uniq!
27
+ all_team_members += team_members
28
+ all_team_members.uniq!
29
+ end
30
+
31
+ if !@teams.key?(team_name.to_sym)
32
+ respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
33
+ elsif !(all_team_members + config.masters).flatten.include?(user.name)
34
+ respond "You have to be a member of the team or a Master admin to be able to delete a memo of the team."
35
+ elsif !@teams[team_name.to_sym].key?(:memos) or @teams[team_name.to_sym][:memos].empty? or !@teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
36
+ respond "It seems like there is no memo with id #{memo_id}"
37
+ elsif @teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
38
+ memo_selected = @teams[team_name.to_sym][:memos].select { |m| m.memo_id == memo_id.to_i }[-1]
39
+ if memo_selected.privacy == "personal" and memo_selected.user != user.name
40
+ respond "Only the creator can delete a personal memo."
41
+ else
42
+ if answer.empty?
43
+ message = @teams[team_name.to_sym][:memos].select { |memo| memo.memo_id == memo_id.to_i }.message.join
44
+ ask "do you really want to delete the memo #{memo_id} (#{message}) from #{team_name} team? (yes/no)"
45
+ else
46
+ case answer
47
+ when /yes/i, /yep/i, /sure/i
48
+ answer_delete
49
+ memos = []
50
+ message = ""
51
+ get_teams()
52
+ @teams[team_name.to_sym][:memos].each do |memo|
53
+ if memo.memo_id != memo_id.to_i
54
+ memos << memo
55
+ else
56
+ message = memo.message
57
+ memos_file = File.join(config.path, "teams", "t_#{team_name}_memos.yaml.deleted")
58
+ File.open(memos_file, "a+") { |file|
59
+ file.flock(File::LOCK_EX)
60
+ file.write("#{Utils::Encryption.encrypt([memo].to_yaml.gsub(/^---\s*$/, ""),config)}\n\n")
61
+ file.flock(File::LOCK_UN)
62
+ }
63
+ end
64
+ end
65
+ @teams[team_name.to_sym][:memos] = memos
66
+ update_teams()
67
+ respond "The memo has been deleted from team #{team_name}: #{message}"
68
+ when /no/i, /nope/i, /cancel/i
69
+ answer_delete
70
+ respond "Ok, the memo was not deleted"
71
+ else
72
+ respond "I don't understand"
73
+ ask "do you really want to delete the memo from #{team_name} team? (yes/no)"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,97 @@
1
+ class SlackSmartBot
2
+ module Commands
3
+ module General
4
+ module Teams
5
+ module Memos
6
+ def see_memo_team(user, team_name, memo_id)
7
+ save_stats(__method__)
8
+ get_teams()
9
+ team_name = team_name.to_sym
10
+ if @teams.key?(team_name)
11
+ if @teams[team_name].key?(:memos)
12
+ memo = @teams[team_name].memos.select { |m| m.memo_id == memo_id.to_i }[-1]
13
+ memo_deleted = false
14
+ deleted_memos_file = File.join(config.path, "teams", "t_#{team_name}_memos.yaml.deleted")
15
+ if memo.nil? and File.exist?(deleted_memos_file)
16
+ mydata = File.read(deleted_memos_file)
17
+ all_deleted_memos = []
18
+ mydata.split(/^\s*$/).each do |memo|
19
+ all_deleted_memos << Utils::Encryption.decrypt(memo, config)
20
+ end
21
+ memos = YAML.load(all_deleted_memos.join("\n"))
22
+ memo = memos.select { |m| m.memo_id == memo_id.to_i }[-1]
23
+ memo_deleted = true if memo
24
+ end
25
+ if memo
26
+ if memo_deleted
27
+ messages = ["This memo was deleted from the team #{team_name}.\nOnly the creator (#{memo.user}) of the memo can get access to it."]
28
+ if memo.user == user.name
29
+ messages << "Memo #{memo.memo_id} (#{memo.type}): #{memo.message}"
30
+ end
31
+ else
32
+ messages = see_memos_team(user, type: "all", add_stats: false, name: team_name, memo_id: memo_id)
33
+ end
34
+ if messages.empty?
35
+ messages = ["This memo is private or personal and you don't have access to it on this channel."]
36
+ messages << "Remember in case of a private memo you can only see it in the team members channel."
37
+ messages << "In case of a personal memo you can only see it if you are the creator and you are on a DM."
38
+ respond messages.join("\n")
39
+ else
40
+ if memo.type == "jira" and !memo_deleted
41
+ require "time"
42
+ http = NiceHttp.new(config.jira.host)
43
+ http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
44
+ resp = http.get("/rest/api/2/issue/#{memo.message}/comment")
45
+ if resp.code == 200
46
+ jira_comments = resp.data.json.comments
47
+ if !jira_comments.nil? and !jira_comments.empty?
48
+ messages << "\n_*JIRA Comments:*_"
49
+ jira_comments.each do |comment|
50
+ messages << " *#{comment.author.displayName}* > #{comment.body} _(#{Time.parse(comment.created).strftime("%Y/%m/%d %H:%M")})_"
51
+ end
52
+ end
53
+ end
54
+ http.close
55
+ elsif memo.type == "github" and !memo_deleted
56
+ http = NiceHttp.new(config.github.host)
57
+ http.headers.authorization = "token #{config.github.token}"
58
+ resp = http.get("/repos/#{memo.message}/comments")
59
+ if resp.code == 200
60
+ github_comments = resp.data.json
61
+ if !github_comments.nil? and !github_comments.empty?
62
+ messages << "\n_*GitHub Comments:*_"
63
+ github_comments.each do |comment|
64
+ messages << " *#{comment.user.login}* > #{comment.body} _(#{Time.parse(comment.created_at).strftime("%Y/%m/%d %H:%M")})_"
65
+ end
66
+ end
67
+ end
68
+ http.close
69
+ end
70
+ if memo.key?(:comments) and !memo.comments.empty? and (!memo_deleted or (memo_deleted and memo.user == user.name))
71
+ messages << "\n_*Comments:*_"
72
+ memo.comments.each do |comment|
73
+ messages << " *#{comment[:user_name]}* > #{comment[:message]} _(#{comment[:time][0..15]})_"
74
+ end
75
+ end
76
+
77
+ if memo.key?(:comments) and memo.comments.size > 5
78
+ respond_thread messages.join("\n")
79
+ else
80
+ respond messages.join("\n")
81
+ end
82
+ end
83
+ else
84
+ respond "Memo *#{memo_id}* does not exist in team *#{team_name}*."
85
+ end
86
+ else
87
+ respond "There are no memos in team *#{team_name}*."
88
+ end
89
+ else
90
+ respond "Team *#{team_name}* does not exist. Call `see teams` to see the list of teams."
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,304 @@
1
+ class SlackSmartBot
2
+ module Commands
3
+ module General
4
+ module Teams
5
+ module Memos
6
+ def see_memos_team(user, type: "all", name: nil, topic: "", add_stats: true, team: nil, memo_id: nil)
7
+ save_stats(__method__) if add_stats
8
+
9
+ get_teams()
10
+ type = "all" if type.match?(/all\s+memo/i)
11
+ message = []
12
+ if @teams.size > 0
13
+ if team.nil?
14
+ @teams.each do |team_name, teamv|
15
+ if (team_name.to_s == name.to_s) or (name.to_s.gsub("-", "").gsub("_", "") == team_name.to_s)
16
+ if teamv.key?(:memos) and teamv[:memos].size > 0
17
+ team = teamv.deep_copy
18
+ else
19
+ respond "There are no memos for the team #{name}." unless !add_stats
20
+ end
21
+ break
22
+ end
23
+ end
24
+ end
25
+ if team
26
+ react :running unless !add_stats
27
+ all_memos = {}
28
+ assigned_members, unassigned_members, not_on_team_channel, channels_members, all_team_members = get_team_members(team)
29
+ users_link = (Thread.current[:dest][0] == "D")
30
+ memos_filtered = []
31
+ all_topics = []
32
+ num_issues = 0
33
+ if memo_id.nil?
34
+ memo_selected = {}
35
+ else
36
+ memo_selected = team[:memos].select { |m| m.memo_id == memo_id.to_i }[-1]
37
+ end
38
+ team[:memos].each do |memo|
39
+ if memo_id.nil? or memo[:memo_id] == memo_id.to_i
40
+ memos_filtered << memo
41
+ all_topics << memo.topic
42
+ if memo.key?(:search) and memo.key?(:issues) and memo.issues.size > 0
43
+ num_issues += memo.issues.size
44
+ else
45
+ num_issues += 1
46
+ end
47
+ end
48
+ end
49
+ all_topics.uniq!
50
+ all_topics.delete(:no_topic)
51
+ if num_issues >= 10 and !add_stats
52
+ message << " > *_memos_*"
53
+ message << " There are too many memos to show. "
54
+ message << " Please use the `see MEMO_TYPE team #{team.name} TOPIC` command."
55
+ message << " Some available topics: #{all_topics.join(", ")}" if all_topics.size > 0
56
+ message << " Examples: `see bugs #{team.name} team`, `see all memos #{team.name} team #{all_topics.sample}`, `see tasks #{team.name} team #{all_topics.sample}`"
57
+ elsif memos_filtered.size > 0
58
+ memos_filtered.each do |memo|
59
+ if memo.privacy.empty? or
60
+ (memo.privacy == "private" and (all_team_members.include?(user.name) and (users_link or channels_members.include?(Thread.current[:dest])))) or
61
+ (memo.privacy == "personal" and memo.user == user.name and users_link)
62
+ if memo.type == "jira" and config.jira.host != ""
63
+ http = NiceHttp.new(config.jira.host)
64
+ http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
65
+ if memo.message.match?(/^\w+\-\d+$/) or (memo.key?(:search) and !memo.search)
66
+ resp = http.get("/rest/api/latest/issue/#{memo.message}")
67
+ issues = [resp.data.json] if resp.code == 200
68
+ memo.search = false
69
+ else
70
+ resp = http.get("/rest/api/latest/search/?jql=#{memo.message}")
71
+ issues = resp.data.json().issues if resp.code == 200
72
+ memo.search = true
73
+ end
74
+ if resp.code == 200
75
+ unless issues.empty?
76
+ if memo.search
77
+ if memo.key?(:issues)
78
+ orig_issues = memo.issues.deep_copy.sort
79
+ else
80
+ orig_issues = []
81
+ end
82
+ end
83
+ memo_issues = []
84
+ issues.each do |issue|
85
+ jira_memo = { jira: true, github: false, status: "", memo_id: memo.memo_id, topic: memo.topic, privacy: memo.privacy, user: memo.user, date: memo.date, message: "", type: memo.type }
86
+ jira_memo.message = issue.fields.summary
87
+ memo_issues << jira_memo.message
88
+ jira_memo.user = issue.fields.reporter.name
89
+ jira_memo.date = issue.fields.created
90
+ jira_memo.search = memo.search
91
+ if issue.fields.key?(:comment) and issue.fields.comment.key?(:comments)
92
+ jira_memo.comments = issue.fields.comment.comments
93
+ elsif issue.fields.key?(:comments)
94
+ jira_memo.comments = issue.fields.comments
95
+ else
96
+ jira_memo.comments = []
97
+ end
98
+ if memo.topic == :no_topic and !issue.fields.labels.empty?
99
+ jira_memo.topic = issue.fields.labels.sort.join("_").split(" ").join("_")
100
+ end
101
+ if topic == "" or (topic != "" and jira_memo.topic.to_s.downcase == topic.to_s.downcase)
102
+ case issue.fields.issuetype.name
103
+ when "Story"; jira_memo.type = ":abc:"; jira_memo.mtype = "memo"
104
+ when "Bug"; jira_memo.type = ":bug:"; jira_memo.mtype = "bug"
105
+ when "Task"; jira_memo.type = ":clock1:"; jira_memo.mtype = "task"
106
+ when "New Feature", "Improvement"; jira_memo.type = ":sunny:"; jira_memo.mtype = "feature"
107
+ else jira_memo.type = ":memo:"; jira_memo.mtype = "memo"
108
+ end
109
+
110
+ if (type == "all" or type.to_s == jira_memo.mtype.to_s or type == "")
111
+ case issue.fields.status.statusCategory.name
112
+ when "Done"; jira_memo.status = ":heavy_check_mark:"
113
+ when "To Do"; jira_memo.status = ":new:"
114
+ when "In Progress"; jira_memo.status = ":runner:"
115
+ else jira_memo.status = ":heavy_minus_sign:"
116
+ end
117
+ #todo: check if possible to add link to status instead of jira issue
118
+ #jira_memo.status = " <#{config.jira.host}/browse/#{issue[:key]}|#{jira_memo.status}> "
119
+ jira_memo.status += " <#{config.jira.host}/browse/#{issue[:key]}|#{issue[:key]}>"
120
+
121
+ all_memos[jira_memo.topic] ||= []
122
+ all_memos[jira_memo.topic] << jira_memo
123
+ end
124
+ end
125
+ end
126
+ if memo.search and orig_issues != memo_issues.sort
127
+ memo_selected = @teams[team.name.to_sym][:memos].select { |m| m.memo_id == memo.memo_id.to_i }[-1]
128
+ memo_selected[:issues] = memo_issues.deep_copy
129
+ memo_selected[:search] = true
130
+ update_teams()
131
+ end
132
+ end
133
+ end
134
+ http.close
135
+ elsif memo.type == "github" and config.github.host != ""
136
+ if memo.message.include?("?")
137
+ memo.search = true
138
+ else
139
+ memo.search = false
140
+ end
141
+
142
+ http = NiceHttp.new(config.github.host)
143
+ http.headers.authorization = "token #{config.github.token}"
144
+ issue_url = memo.message
145
+ issue_url + "?" unless issue_url.include?("?")
146
+ issue_url += "&per_page=50"
147
+
148
+ resp = http.get("/repos/#{issue_url}")
149
+ issues = resp.data.json()
150
+ issues = [issues] unless issues.is_a?(Array)
151
+ if resp.code == 200
152
+ unless issues.empty?
153
+ if memo.search
154
+ if memo.key?(:issues)
155
+ orig_issues = memo.issues.deep_copy.sort
156
+ else
157
+ orig_issues = []
158
+ end
159
+ end
160
+ memo_issues = []
161
+ issues.each do |issue|
162
+ github_memo = { jira: false, github: true, status: "", memo_id: memo.memo_id, topic: memo.topic, privacy: memo.privacy, user: memo.user, date: memo.date, message: "", type: memo.type }
163
+ github_memo.message = issue.title
164
+ memo_issues << github_memo.message
165
+ github_memo.user = issue.user.login
166
+ github_memo.date = issue.created_at
167
+ github_memo.search = memo.search
168
+ if issue.key?(:comments) and issue.comments.to_i > 0
169
+ github_memo.comments = [issue.comments.to_i]
170
+ else
171
+ github_memo.comments = []
172
+ end
173
+
174
+ if issue.labels.empty?
175
+ labels = ""
176
+ else
177
+ labels = issue.labels.name.sort.join("_").split(" ").join("_")
178
+ end
179
+ if memo.topic == :no_topic and !issue.labels.empty?
180
+ github_memo.topic = labels
181
+ end
182
+ if topic == "" or (topic != "" and github_memo.topic.to_s.downcase == topic.to_s.downcase)
183
+ case labels
184
+ when /bug/i; github_memo.type = ":bug:"; github_memo.mtype = "bug"
185
+ when /docum/i; github_memo.type = ":abc:"; github_memo.mtype = "note"
186
+ when /task/i; github_memo.type = ":clock1:"; github_memo.mtype = "task"
187
+ when /enhancem/i, /improvement/i; github_memo.type = ":sunny:"; github_memo.mtype = "feature"
188
+ else github_memo.type = ":memo:"; github_memo.mtype = "memo"
189
+ end
190
+
191
+ if (type == "all" or type.to_s == github_memo.mtype.to_s or type == "")
192
+ if issue.key?(:events_url)
193
+ resp_events = http.get(issue.events_url)
194
+ events = resp_events.data.json(:event)
195
+ issue.state = "in progress" if events.include?("referenced")
196
+ end
197
+ case issue.state
198
+ when "closed"; github_memo.status = ":heavy_check_mark:"
199
+ when "open"; github_memo.status = ":new:"
200
+ when "in progress"; github_memo.status = ":runner:"
201
+ else github_memo.status = ":heavy_minus_sign:"
202
+ end
203
+ #todo: check if possible to add link to status instead of github issue
204
+ github_memo.status += " <#{issue.html_url}|##{issue.number}>"
205
+
206
+ all_memos[github_memo.topic] ||= []
207
+ all_memos[github_memo.topic] << github_memo
208
+ end
209
+ end
210
+ end
211
+ if memo.search and orig_issues != memo_issues.sort
212
+ memo_selected = @teams[team.name.to_sym][:memos].select { |m| m.memo_id == memo.memo_id.to_i }[-1]
213
+ memo_selected[:issues] = memo_issues.deep_copy
214
+ memo_selected[:search] = true
215
+ update_teams()
216
+ end
217
+ end
218
+ end
219
+ http.close
220
+ else
221
+ if topic == "" or (topic != "" and memo.topic.to_s.downcase == topic.to_s.downcase)
222
+ if (type == "all" or type.to_s == memo[:type].to_s or type == "")
223
+ memo.jira = false
224
+ memo.github = false
225
+ all_memos[memo.topic] ||= []
226
+ case memo.type
227
+ when "memo"; memo.type = ":memo:"
228
+ when "note"; memo.type = ":abc:"
229
+ when "bug"; memo.type = ":bug:"
230
+ when "task"; memo.type = ":clock1:"
231
+ when "feature"; memo.type = ":sunny:"
232
+ when "issue"; memo.type = ":hammer:"
233
+ else memo.type = ":heavy_minus_sign:"
234
+ end
235
+ all_memos[memo.topic] << memo
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ if all_memos.empty?
242
+ message << "There are no memos #{name} team #{type} #{topic}." unless !add_stats
243
+ else
244
+ if !add_stats
245
+ if memo_id.nil?
246
+ message << " > *_memos_*"
247
+ else
248
+ if !memo_id.nil? and memo_selected and memo_selected.key?(:message) and
249
+ memo_selected.key?(:search) and memo_selected.search
250
+ message << "*_Team #{name.capitalize} memo #{memo_id}_*#{": #{memo_selected.message}"}"
251
+ else
252
+ message << "*_Team #{name.capitalize} memo #{memo_id}_*"
253
+ end
254
+ end
255
+ else
256
+ message << " > *_#{team.name} team #{type}_*"
257
+ end
258
+
259
+ if all_memos.key?(:no_topic)
260
+ all_memos[:no_topic].sort_by { |memo| memo[:date] }.each do |memo|
261
+ case memo.privacy
262
+ when "private"; priv = " `private`"
263
+ when "personal"; priv = " `personal`"
264
+ else priv = ""
265
+ end
266
+ message << " #{memo.type} #{memo.date.gsub("-", "/")[0..9]}: #{memo.status} #{memo.message} (#{memo.user} #{memo.memo_id})#{priv}#{" :spiral_note_pad:" if memo.key?(:comments) and !memo.comments.empty?}#{" :mag:" if memo.key?(:search) and memo.search}"
267
+ end
268
+ end
269
+ all_memos[:no_topic] = []
270
+ all_memos.each do |topic, mems|
271
+ unless mems.empty?
272
+ message << " _`#{topic}`_:"
273
+ mems.sort_by { |m| m[:date] }.each do |memo|
274
+ case memo.privacy
275
+ when "private"; priv = " `private`"
276
+ when "personal"; priv = " `personal`"
277
+ else priv = ""
278
+ end
279
+ message << " #{memo.type} #{memo.date.gsub("-", "/")[0..9]}: #{memo.status} #{memo.message} (#{memo.user} #{memo.memo_id})#{priv}#{" :spiral_note_pad:" if memo.key?(:comments) and !memo.comments.empty?}#{" :mag:" if memo.key?(:search) and memo.search}"
280
+ end
281
+ end
282
+ end
283
+ end
284
+ else
285
+ message << "There are no memos #{name} team #{type} #{topic}." unless !add_stats
286
+ end
287
+ unreact :running unless !add_stats
288
+ else
289
+ respond "There is no team named #{name}." unless !add_stats
290
+ end
291
+ if add_stats
292
+ respond message.join("\n")
293
+ else
294
+ return message
295
+ end
296
+ else
297
+ respond "There are no teams added yet\. Use `add team` command to add a team" unless !add_stats
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
@@ -0,0 +1,66 @@
1
+ class SlackSmartBot
2
+ module Commands
3
+ module General
4
+ module Teams
5
+ module Memos
6
+ def set_memo_status(user, team_name, memo_id, status)
7
+ save_stats(__method__) if answer.empty?
8
+
9
+ get_teams()
10
+ if @teams.key?(team_name.to_sym)
11
+ assigned_members = @teams[team_name.to_sym].members.values.flatten
12
+ assigned_members.uniq!
13
+ all_team_members = assigned_members.dup
14
+ team_members = []
15
+ if @teams[team_name.to_sym].channels.key?("members")
16
+ @teams[team_name.to_sym].channels["members"].each do |ch|
17
+ get_channels_name_and_id() unless @channels_id.key?(ch)
18
+ tm = get_channel_members(@channels_id[ch])
19
+ tm.each do |m|
20
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
21
+ team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
22
+ end
23
+ end
24
+ end
25
+ team_members.flatten!
26
+ team_members.uniq!
27
+ all_team_members += team_members
28
+ all_team_members.uniq!
29
+ end
30
+
31
+ if !@teams.key?(team_name.to_sym)
32
+ respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
33
+ elsif !(all_team_members + config.masters).flatten.include?(user.name)
34
+ respond "You have to be a member of the team or a Master admin to be able to set the status of a memo."
35
+ elsif !@teams[team_name.to_sym].key?(:memos) or @teams[team_name.to_sym][:memos].empty? or !@teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
36
+ respond "It seems like there is no memo with id #{memo_id}"
37
+ elsif @teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
38
+ memo_selected = @teams[team_name.to_sym][:memos].select { |m| m.memo_id == memo_id.to_i }[-1]
39
+ if memo_selected.type == "jira" or memo_selected.type == "github"
40
+ #todo: add tests for jira and github
41
+ respond "The memo specified is a #{memo_selected.type} and the status in those cases are linked to the specific issues in #{memo_selected.type}."
42
+ elsif memo_selected.privacy == "personal" and memo_selected.user != user.name
43
+ respond "Only the creator can set the status of a personal memo."
44
+ else
45
+ answer_delete
46
+ memos = []
47
+ message = ""
48
+ get_teams()
49
+ @teams[team_name.to_sym][:memos].each do |memo|
50
+ if memo.memo_id == memo_id.to_i
51
+ memo.status = status
52
+ message = memo.message
53
+ end
54
+ memos << memo
55
+ end
56
+ @teams[team_name.to_sym][:memos] = memos
57
+ update_teams()
58
+ respond "The memo status has been updated on team #{team_name}: #{message}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,104 @@
1
+ class SlackSmartBot
2
+ module Commands
3
+ module General
4
+ module Teams
5
+ def ping_team(user, type, team_name, member_type, message)
6
+ if type == :ping
7
+ save_stats(:ping_team)
8
+ icon = ":large_green_circle:"
9
+ else
10
+ save_stats(:contact_team)
11
+ icon = ":email:"
12
+ end
13
+ if Thread.current[:dest][0] == "D"
14
+ respond "This command cannot be called from a DM"
15
+ else
16
+ get_teams()
17
+ if !@teams.key?(team_name.to_sym)
18
+ respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
19
+ else
20
+ team = @teams[team_name.to_sym].deep_copy
21
+
22
+ assigned_members = team.members.values.flatten
23
+ assigned_members.uniq!
24
+ assigned_members.dup.each do |m|
25
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) or u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
26
+ assigned_members.delete(m) if user_info.nil? or user_info.deleted
27
+ end
28
+ unassigned_members = []
29
+ not_on_team_channel = []
30
+
31
+ if ((!team.members.key?(member_type) or member_type == "all") and team.channels.key?("members"))
32
+ team_members = []
33
+ team.channels["members"].each do |ch|
34
+ get_channels_name_and_id() unless @channels_id.key?(ch)
35
+ tm = get_channel_members(@channels_id[ch])
36
+ tm.each do |m|
37
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
38
+ team_members << user_info.name unless user_info.nil? or user_info.is_app_user or user_info.is_bot or user_info.deleted
39
+ end
40
+ end
41
+ team_members.flatten!
42
+ team_members.uniq!
43
+ unassigned_members = team_members - assigned_members
44
+ unassigned_members.delete(config.nick)
45
+
46
+ unless unassigned_members.empty?
47
+ um = unassigned_members.dup
48
+ um.each do |m|
49
+ user_info = @users.select { |u| u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
50
+ unless user_info.nil? or user_info.profile.title.to_s == "" or user_info.deleted
51
+ team.members[user_info.profile.title.to_snake_case] ||= []
52
+ team.members[user_info.profile.title.to_snake_case] << m
53
+ unassigned_members.delete(m)
54
+ end
55
+ end
56
+ unless unassigned_members.empty?
57
+ team.members["unassigned"] ||= []
58
+ team.members["unassigned"] += unassigned_members
59
+ team.members["unassigned"].sort!
60
+ end
61
+ end
62
+ end
63
+
64
+ if team.members.key?(member_type) or member_type == "all"
65
+ if member_type == "all"
66
+ members_list = team.members.values.flatten.uniq.shuffle
67
+ else
68
+ members_list = team.members[member_type].shuffle
69
+ end
70
+ if type == :ping
71
+ active_members = []
72
+ members_list.each do |member|
73
+ member_info = @users.select { |u| u.name == member }[-1]
74
+ unless member_info.nil? or member_info.deleted
75
+ active = (get_presence(member_info.id).presence.to_s == "active")
76
+ active_members << member if active
77
+ end
78
+ end
79
+ members = active_members
80
+ else
81
+ members = members_list
82
+ end
83
+ members.dup.each do |m|
84
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) or u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
85
+ members.delete(m) if user_info.nil? or user_info.deleted
86
+ end
87
+
88
+ if members.size > 0
89
+ respond "#{icon} *#{type} #{team_name} team #{member_type}*\nfrom <@#{user.name}>\nto <@#{members[0..9].join(">, <@")}>#{", #{members[10..-1].join(", ")}" if members.size > 10} \n> #{message.split("\n").join("\n> ")}"
90
+ elsif type == :ping
91
+ respond "It seems like there are no available #{member_type} members on #{team_name} team. Please call `see team #{team_name}`"
92
+ else
93
+ respond "It seems like there are no #{member_type} members on #{team_name} team. Please call `see team #{team_name}`"
94
+ end
95
+ else
96
+ respond "The member type #{member_type} doesn't exist, please call `see team #{team_name}`"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end