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.
- checksums.yaml +4 -4
- data/README.md +100 -4
- data/lib/slack/smart-bot/ai/open_ai/connect.rb +61 -0
- data/lib/slack/smart-bot/ai/open_ai/models.rb +21 -0
- data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +24 -0
- data/lib/slack/smart-bot/ai/open_ai/send_image_edit.rb +23 -0
- data/lib/slack/smart-bot/ai/open_ai/send_image_generation.rb +18 -0
- data/lib/slack/smart-bot/ai/open_ai/send_image_variation.rb +22 -0
- data/lib/slack/smart-bot/ai/open_ai/whisper_transcribe.rb +21 -0
- data/lib/slack/smart-bot/ai.rb +8 -0
- data/lib/slack/smart-bot/comm/get_channel_members.rb +15 -13
- data/lib/slack/smart-bot/comm/get_channels.rb +31 -29
- data/lib/slack/smart-bot/comm/respond_thread.rb +2 -2
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +40 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_edit_image.rb +66 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_generate_image.rb +65 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_models.rb +37 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_variations_image.rb +84 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_whisper.rb +51 -0
- data/lib/slack/smart-bot/commands/general/bot_help.rb +1 -0
- data/lib/slack/smart-bot/commands/general/personal_settings.rb +38 -0
- data/lib/slack/smart-bot/commands/general/poster.rb +107 -104
- data/lib/slack/smart-bot/commands/general/public_holidays.rb +116 -114
- data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +6 -2
- data/lib/slack/smart-bot/commands/general/teams/add_team.rb +87 -0
- data/lib/slack/smart-bot/commands/general/teams/delete_team.rb +69 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team.rb +136 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team_comment.rb +37 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/delete_memo_team.rb +83 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/see_memo_team.rb +97 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/see_memos_team.rb +304 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/set_memo_status.rb +66 -0
- data/lib/slack/smart-bot/commands/general/teams/ping_team.rb +104 -0
- data/lib/slack/smart-bot/commands/general/teams/see_teams.rb +236 -0
- data/lib/slack/smart-bot/commands/general/teams/see_vacations_team.rb +183 -0
- data/lib/slack/smart-bot/commands/general/teams/update_team.rb +137 -0
- data/lib/slack/smart-bot/commands/general_bot_commands.rb +905 -741
- data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +379 -353
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +87 -189
- data/lib/slack/smart-bot/commands/on_bot/repl_client.rb +233 -0
- data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +17 -4
- data/lib/slack/smart-bot/commands.rb +26 -10
- data/lib/slack/smart-bot/process.rb +14 -3
- data/lib/slack/smart-bot/process_first.rb +36 -2
- data/lib/slack/smart-bot/treat_message.rb +28 -0
- data/lib/slack/smart-bot/utils/check_vacations.rb +1 -0
- data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
- data/lib/slack/smart-bot/utils/display_calendar.rb +17 -10
- data/lib/slack/smart-bot/utils/encryption/decrypt.rb +23 -0
- data/lib/slack/smart-bot/utils/encryption/encrypt.rb +27 -0
- data/lib/slack/smart-bot/utils/{encryption_get_key_iv.rb → encryption/encryption_get_key_iv.rb} +12 -8
- data/lib/slack/smart-bot/utils/get_help.rb +3 -1
- data/lib/slack/smart-bot/utils/get_personal_settings.rb +14 -0
- data/lib/slack/smart-bot/utils/get_teams.rb +2 -2
- data/lib/slack/smart-bot/utils/get_vacations.rb +2 -2
- data/lib/slack/smart-bot/utils/save_stats.rb +3 -1
- data/lib/slack/smart-bot/utils/update_personal_settings.rb +18 -0
- data/lib/slack/smart-bot/utils/update_teams.rb +1 -1
- data/lib/slack/smart-bot/utils/update_vacations.rb +1 -1
- data/lib/slack/smart-bot/utils.rb +5 -3
- data/lib/slack-smart-bot.rb +12 -0
- data/whats_new.txt +13 -14
- metadata +63 -15
- data/lib/slack/smart-bot/commands/general/add_memo_team.rb +0 -117
- data/lib/slack/smart-bot/commands/general/add_team.rb +0 -81
- data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +0 -69
- data/lib/slack/smart-bot/commands/general/delete_team.rb +0 -55
- data/lib/slack/smart-bot/commands/general/ping_team.rb +0 -100
- data/lib/slack/smart-bot/commands/general/see_memos_team.rb +0 -202
- data/lib/slack/smart-bot/commands/general/see_teams.rb +0 -230
- data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +0 -136
- data/lib/slack/smart-bot/commands/general/set_memo_status.rb +0 -58
- data/lib/slack/smart-bot/commands/general/update_team.rb +0 -131
- data/lib/slack/smart-bot/utils/decrypt.rb +0 -15
- 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
|