Skip to content

Commit f4aed83

Browse files
committed
Remove slack messages sent by our bot in the event that a user changes a kudos from public to private or deletes a public kudos.
1 parent e1878b3 commit f4aed83

File tree

4 files changed

+154
-21
lines changed

4 files changed

+154
-21
lines changed

server/src/main/java/com/objectcomputing/checkins/notifications/social_media/SlackSender.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import com.slack.api.methods.MethodsClient;
77
import com.slack.api.methods.request.chat.ChatPostMessageRequest;
88
import com.slack.api.methods.response.chat.ChatPostMessageResponse;
9+
import com.slack.api.methods.request.chat.ChatDeleteRequest;
10+
import com.slack.api.methods.response.chat.ChatDeleteResponse;
911
import com.slack.api.methods.request.conversations.ConversationsOpenRequest;
1012
import com.slack.api.methods.response.conversations.ConversationsOpenResponse;
1113

@@ -42,9 +44,28 @@ public boolean send(List<String> userIds, String slackBlocks) {
4244
return false;
4345
}
4446

47+
return send(openResponse.getChannel().getId(), slackBlocks);
48+
} catch(Exception ex) {
49+
LOG.error("SlackSender.send: " + ex.toString());
50+
return false;
51+
}
52+
} else {
53+
LOG.error("Missing token or missing slack blocks");
54+
return false;
55+
}
56+
}
57+
58+
public boolean send(String channelId, String slackBlocks) {
59+
// See if we have a token.
60+
String token = configuration.getApplication()
61+
.getSlack().getBotToken();
62+
if (token != null && !slackBlocks.isEmpty()) {
63+
MethodsClient client = Slack.getInstance().methods(token);
64+
65+
try {
4566
ChatPostMessageRequest request = ChatPostMessageRequest
4667
.builder()
47-
.channel(openResponse.getChannel().getId())
68+
.channel(channelId)
4869
.blocksAsString(slackBlocks)
4970
.build();
5071

@@ -66,5 +87,38 @@ public boolean send(List<String> userIds, String slackBlocks) {
6687
return false;
6788
}
6889
}
90+
91+
public boolean delete(String channel, String ts) {
92+
// See if we have a token.
93+
String token = configuration.getApplication()
94+
.getSlack().getBotToken();
95+
if (token != null) {
96+
MethodsClient client = Slack.getInstance().methods(token);
97+
98+
try {
99+
ChatDeleteRequest request = ChatDeleteRequest
100+
.builder()
101+
.channel(channel)
102+
.ts(ts)
103+
.build();
104+
105+
// Send it to Slack
106+
ChatDeleteResponse response = client.chatDelete(request);
107+
108+
if (!response.isOk()) {
109+
LOG.error("Unable to delete the chat message: " +
110+
response.getError());
111+
}
112+
113+
return response.isOk();
114+
} catch(Exception ex) {
115+
LOG.error("SlackSender.delete: " + ex.toString());
116+
return false;
117+
}
118+
} else {
119+
LOG.error("Missing token or missing slack blocks");
120+
return false;
121+
}
122+
}
69123
}
70124

server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosConverter.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@
2222

2323
@Singleton
2424
public class KudosConverter {
25-
private record InternalBlock(
26-
List<LayoutBlock> blocks
27-
) {}
28-
2925
private final MemberProfileServices memberProfileServices;
3026
private final KudosRecipientServices kudosRecipientServices;
3127
private final SlackSearch slackSearch;
@@ -61,9 +57,8 @@ public String toSlackBlock(Kudos kudos) {
6157
.elements(content).build();
6258
RichTextBlock richTextBlock = RichTextBlock.builder()
6359
.elements(List.of(element)).build();
64-
InternalBlock block = new InternalBlock(List.of(richTextBlock));
6560
Gson mapper = GsonFactory.createSnakeCase();
66-
return mapper.toJson(block);
61+
return mapper.toJson(List.of(richTextBlock));
6762
}
6863

6964
private RichTextSectionElement.TextStyle boldItalic() {

server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosServicesImpl.java

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import com.objectcomputing.checkins.configuration.CheckInsConfiguration;
77
import com.objectcomputing.checkins.notifications.email.EmailSender;
88
import com.objectcomputing.checkins.notifications.email.MailJetFactory;
9-
import com.objectcomputing.checkins.notifications.social_media.SlackPoster;
9+
import com.objectcomputing.checkins.notifications.social_media.SlackSender;
10+
import com.objectcomputing.checkins.services.slack.SlackReader;
11+
import com.objectcomputing.checkins.services.slack.kudos.BotSentKudosLocator;
1012
import com.objectcomputing.checkins.exceptions.BadArgException;
1113
import com.objectcomputing.checkins.exceptions.NotFoundException;
1214
import com.objectcomputing.checkins.exceptions.PermissionException;
@@ -23,13 +25,15 @@
2325
import com.objectcomputing.checkins.services.team.Team;
2426
import com.objectcomputing.checkins.services.team.TeamRepository;
2527
import com.objectcomputing.checkins.util.Util;
28+
import com.objectcomputing.checkins.configuration.CheckInsConfiguration;
29+
2630
import io.micronaut.core.annotation.Nullable;
2731
import io.micronaut.transaction.annotation.Transactional;
28-
import io.micronaut.http.HttpResponse;
29-
import io.micronaut.http.HttpStatus;
3032

3133
import jakarta.inject.Named;
3234
import jakarta.inject.Singleton;
35+
import jakarta.inject.Inject;
36+
3337
import org.slf4j.Logger;
3438
import org.slf4j.LoggerFactory;
3539

@@ -58,13 +62,17 @@ class KudosServicesImpl implements KudosServices {
5862
private final CheckInsConfiguration checkInsConfiguration;
5963
private final RoleServices roleServices;
6064
private final MemberProfileServices memberProfileServices;
61-
private final SlackPoster slackPoster;
65+
private final SlackSender slackSender;
6266
private final KudosConverter converter;
67+
private final BotSentKudosLocator botSentKudosLocator;
6368

6469
private enum NotificationType {
6570
creation, approval
6671
}
6772

73+
@Inject
74+
private CheckInsConfiguration configuration;
75+
6876
KudosServicesImpl(KudosRepository kudosRepository,
6977
KudosRecipientServices kudosRecipientServices,
7078
KudosRecipientRepository kudosRecipientRepository,
@@ -75,8 +83,9 @@ private enum NotificationType {
7583
MemberProfileServices memberProfileServices,
7684
@Named(MailJetFactory.HTML_FORMAT) EmailSender emailSender,
7785
CheckInsConfiguration checkInsConfiguration,
78-
SlackPoster slackPoster,
79-
KudosConverter converter
86+
SlackSender slackSender,
87+
KudosConverter converter,
88+
BotSentKudosLocator botSentKudosLocator
8089
) {
8190
this.kudosRepository = kudosRepository;
8291
this.kudosRecipientServices = kudosRecipientServices;
@@ -88,8 +97,9 @@ private enum NotificationType {
8897
this.currentUserServices = currentUserServices;
8998
this.emailSender = emailSender;
9099
this.checkInsConfiguration = checkInsConfiguration;
91-
this.slackPoster = slackPoster;
100+
this.slackSender = slackSender;
92101
this.converter = converter;
102+
this.botSentKudosLocator = botSentKudosLocator;
93103
}
94104

95105
@Override
@@ -152,9 +162,9 @@ public Kudos update(KudosUpdateDTO kudos) {
152162

153163
boolean existingPublic = existingKudos.getPubliclyVisible();
154164
boolean proposedPublic = kudos.getPubliclyVisible();
165+
boolean removePublicSlack = false;
155166
if (existingPublic && !proposedPublic) {
156-
// TODO: Search for and remove the Slack Kudos that the Check-Ins
157-
// Integration posted.
167+
removePublicSlack = true;
158168
existingKudos.setDateApproved(null);
159169
} else if ((!existingPublic && proposedPublic) ||
160170
(proposedPublic &&
@@ -195,6 +205,12 @@ public Kudos update(KudosUpdateDTO kudos) {
195205
sendNotification(updated, NotificationType.creation);
196206
}
197207

208+
if (removePublicSlack) {
209+
// Search for and remove the Slack Kudos that the Check-Ins
210+
// Integration posted.
211+
removeSlackMessage(existingKudos);
212+
}
213+
198214
return updated;
199215
}
200216

@@ -245,6 +261,12 @@ public void delete(UUID id) {
245261
kudosRecipientRepository.deleteAll(recipients);
246262

247263
kudosRepository.deleteById(id);
264+
265+
if (kudos.getPubliclyVisible()) {
266+
// Search for and remove the Slack Kudos that the Check-Ins
267+
// Integration posted.
268+
removeSlackMessage(kudos);
269+
}
248270
}
249271

250272
@Override
@@ -441,11 +463,9 @@ private void sendNotification(Kudos kudos, NotificationType notificationType) {
441463
}
442464

443465
private void slackApprovedKudos(Kudos kudos) {
444-
HttpResponse httpResponse =
445-
slackPoster.post(converter.toSlackBlock(kudos));
446-
if (httpResponse.status() != HttpStatus.OK) {
447-
LOG.error("Unable to POST to Slack: " + httpResponse.reason());
448-
}
466+
slackSender.send(configuration.getApplication()
467+
.getSlack().getKudosChannel(),
468+
converter.toSlackBlock(kudos));
449469
}
450470

451471
private boolean hasAdministerKudosPermission() {
@@ -507,4 +527,13 @@ private void updateRecipients(Kudos updated,
507527
}
508528
}
509529
}
530+
531+
private void removeSlackMessage(Kudos kudos) {
532+
String ts = botSentKudosLocator.find(kudos);
533+
if (ts != null) {
534+
slackSender.delete(configuration.getApplication()
535+
.getSlack().getKudosChannel(),
536+
ts);
537+
}
538+
}
510539
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.objectcomputing.checkins.services.slack.kudos;
2+
3+
import com.objectcomputing.checkins.services.kudos.Kudos;
4+
import com.objectcomputing.checkins.services.slack.SlackReader;
5+
import com.objectcomputing.checkins.configuration.CheckInsConfiguration;
6+
7+
import com.slack.api.model.Message;
8+
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
import jakarta.inject.Singleton;
13+
import jakarta.inject.Inject;
14+
15+
import java.util.List;
16+
import java.time.LocalDateTime;
17+
18+
@Singleton
19+
public class BotSentKudosLocator {
20+
private static final Logger LOG = LoggerFactory.getLogger(BotSentKudosLocator.class);
21+
22+
@Inject
23+
private CheckInsConfiguration configuration;
24+
25+
@Inject
26+
private SlackReader slackReader;
27+
28+
// The identifiers needed to identify a message is the channel id and the
29+
// time stamp. We are always looking at a specific channel. So if we find
30+
// a message, we will return the timestamp as a string. Otherwise, we will
31+
// return null.
32+
public String find(Kudos kudos) {
33+
String channelId = configuration.getApplication()
34+
.getSlack().getKudosChannel();
35+
List<Message> messages =
36+
slackReader.read(channelId, kudos.getDateCreated().atStartOfDay());
37+
38+
String kudosText = kudos.getMessage().trim();
39+
for (Message message : messages) {
40+
// We only care about messages sent by our bot.
41+
if (message.getBotId() != null) {
42+
// The first line is the "kudos from" line and is not part of
43+
// the kudos message.
44+
int cut = message.getText().indexOf("\n");
45+
if (cut >= 0) {
46+
String actual = message.getText().substring(cut + 1).trim();
47+
if (actual.equals(kudosText)) {
48+
return message.getTs();
49+
}
50+
}
51+
}
52+
}
53+
return null;
54+
}
55+
}

0 commit comments

Comments
 (0)