Skip to content

Commit

Permalink
enhance bot messaging
Browse files Browse the repository at this point in the history
  • Loading branch information
bincyber committed Sep 30, 2024
1 parent bbabe58 commit 7212e37
Show file tree
Hide file tree
Showing 49 changed files with 718 additions and 237 deletions.
2 changes: 1 addition & 1 deletion internal/bot/event_app_home.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func HandleAppHomeEvent(ctx context.Context, client *slack.Client, db *gorm.DB,
}

if _, err = client.PublishViewContext(ctx, p.UserID, view, ""); err != nil {
return err
return errors.Wrap(err, "failed to publish AppHome view")
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion internal/bot/job_check_pair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func Test_HandleCheckPairButtons(t *testing.T) {

err := json.NewDecoder(r.Body).Decode(&webhook)
assert.Nil(t, err)
assert.Len(t, webhook.Blocks.BlockSet, 2)
assert.Len(t, webhook.Blocks.BlockSet, 3)
assert.True(t, webhook.ReplaceOriginal)

// Assert that the response matches the right template
Expand Down
70 changes: 70 additions & 0 deletions internal/bot/job_create_matches_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,76 @@ func (s *CreateMatchesSuite) Test_CreateMatches() {
r.Equal(int64(1), count)
}

func (s *CreateMatchesSuite) Test_CreateMatches_SameGenderTwoParticipants() {
r := require.New(s.T())

resource, databaseURL, err := database.NewTestPostgresDB(false)
r.NoError(err)
defer resource.Close()

r.NoError(database.Migrate(databaseURL))

db, err := database.NewGormDB(databaseURL)
r.NoError(err)

channelID := "C0123456789"

// Write channel to the database
db.Create(&models.Channel{
ChannelID: channelID,
Inviter: "U9876543210",
ConnectionMode: models.VirtualConnectionMode,
Interval: models.Biweekly,
Weekday: time.Friday,
Hour: 12,
NextRound: time.Now().Add(24 * time.Hour),
})

// Add members to the database
members := []struct {
userID string
gender models.Gender
isActive bool
hasGenderPreference bool
}{
{"U0123456789", models.Male, true, false},
{"U8765432109", models.Male, true, true},
}

for _, member := range members {
db.Create(&models.Member{
ChannelID: channelID,
UserID: member.userID,
Gender: member.gender,
IsActive: &member.isActive,
HasGenderPreference: &member.hasGenderPreference,
})
}

// Write a record in the rounds table
db.Create(&models.Round{
ChannelID: channelID,
})

// Test
err = CreateMatches(s.ctx, db, nil, &CreateMatchesParams{
ChannelID: channelID,
RoundID: 1,
})
r.NoError(err)
r.Contains(s.buffer.String(), "added new match to the database")
r.Contains(s.buffer.String(), "paired active participants for chat-roulette")
r.Contains(s.buffer.String(), "participants=2")
r.Contains(s.buffer.String(), "pairs=1")
r.Contains(s.buffer.String(), "unpaired=0")

// Verify matches
var count int64
result := db.Model(&models.Job{}).Where("job_type = ?", models.JobTypeCreatePair).Count(&count)
r.NoError(result.Error)
r.Equal(int64(1), count)
}

func (s *CreateMatchesSuite) Test_QueueCreateMatchesJob() {
r := require.New(s.T())

Expand Down
13 changes: 11 additions & 2 deletions internal/bot/job_greet_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"net/http"
"net/url"
"path"
"time"

"github.com/hashicorp/go-hclog"
Expand Down Expand Up @@ -158,7 +160,7 @@ func HandleGreetAdminButton(ctx context.Context, client *slack.Client, interacti

// RenderOnboardingChannelView renders the view template for collecting settings
// to enable a new chat-roulette channel.
func RenderOnboardingChannelView(ctx context.Context, interaction *slack.InteractionCallback) ([]byte, error) {
func RenderOnboardingChannelView(ctx context.Context, interaction *slack.InteractionCallback, baseURL string) ([]byte, error) {
// Start new span
tracer := otel.Tracer("")
_, span := tracer.Start(ctx, "render.channel")
Expand All @@ -171,9 +173,16 @@ func RenderOnboardingChannelView(ctx context.Context, interaction *slack.Interac
}

// Render the template
u, err := url.Parse(baseURL)
if err != nil {
return nil, errors.Wrap(err, "failed to parse base URL")
}
u.Path = path.Join(u.Path, "static/img/coffee-machine.jpg")

t := onboardingTemplate{
ChannelID: pm.ChannelID,
PrivateMetadata: interaction.View.PrivateMetadata,
ImageURL: u.String(),
}

content, err := renderTemplate(onboardingChannelTemplateFilename, t)
Expand Down Expand Up @@ -237,7 +246,7 @@ func RespondGreetAdminWebhook(ctx context.Context, client *http.Client, interact
return errors.Wrap(err, "failed to decode base64 string to privateMetadata")
}

confirmationText := `*Chat Roulette is now enabled! I hope you enjoy using this app* :smile:`
confirmationText := `*Chat Roulette is now enabled! I hope you enjoy using this app* :grin:`

text := slack.NewTextBlockObject("mrkdwn", confirmationText, false, false)
section := slack.NewSectionBlock(text, nil, nil)
Expand Down
2 changes: 1 addition & 1 deletion internal/bot/job_greet_admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ b3N0L2FjdGlvbnMvYS9iL2MifQ==`,
},
}

content, err := RenderOnboardingChannelView(context.Background(), interaction)
content, err := RenderOnboardingChannelView(context.Background(), interaction, "http://localhost/")
assert.Nil(t, err)
assert.NotNil(t, content)

Expand Down
54 changes: 47 additions & 7 deletions internal/bot/job_greet_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,12 @@ func UpsertMemberLocationInfo(ctx context.Context, db *gorm.DB, interaction *sla

// Extract the values from the view state
country := interaction.View.State.Values["onboarding-country"]["onboarding-location-country"].SelectedOption.Value
city := templatex.Capitalize(interaction.View.State.Values["onboarding-city"]["onboarding-location-city"].Value)

city := strings.TrimSpace(
templatex.Capitalize(
interaction.View.State.Values["onboarding-city"]["onboarding-location-city"].Value,
),
)

// Schedule an UPDATE_MEMBER job to update the member's location.
// UpdateMember() could be called directly here, however
Expand Down Expand Up @@ -332,7 +337,7 @@ func RenderOnboardingGenderView(ctx context.Context, interaction *slack.Interact
if err != nil {
return nil, errors.Wrap(err, "failed to parse base URL")
}
u.Path = path.Join(u.Path, "static/img/social-icons.png")
u.Path = path.Join(u.Path, "static/img/gender.jpg")

// Render the template
t := onboardingTemplate{
Expand Down Expand Up @@ -367,7 +372,7 @@ func UpsertMemberGenderInfo(ctx context.Context, db *gorm.DB, interaction *slack
gender := interaction.View.State.Values["onboarding-gender-select"]["onboarding-gender-select"].SelectedOption.Value

hasGenderPreference := false
if len(interaction.View.State.Values["onboarding-gender-checkbox"]["onboarding-gender-checkbox"].SelectedOptions) > 0 {
if len(interaction.View.State.Values["onboarding-gender-checkbox"]["onboarding-gender-checkbox"].SelectedOptions) != 0 {
hasGenderPreference = true
}

Expand All @@ -378,7 +383,7 @@ func UpsertMemberGenderInfo(ctx context.Context, db *gorm.DB, interaction *slack
UserID: interaction.User.ID,
ChannelID: pm.ChannelID,
Gender: gender,
HasGenderPreference: hasGenderPreference,
HasGenderPreference: &hasGenderPreference,
}

if err := QueueUpdateMemberJob(ctx, db, p); err != nil {
Expand Down Expand Up @@ -460,7 +465,12 @@ func UpsertMemberProfileInfo(ctx context.Context, db *gorm.DB, interaction *slac

// Extract the values from the view state
profileType := interaction.View.State.Values["onboarding-profile-type"]["onboarding-profile-type"].SelectedOption.Value
profileLink := interaction.View.State.Values["onboarding-profile-link"]["onboarding-profile-link"].Value

profileLink := strings.ToLower(
strings.TrimSpace(
interaction.View.State.Values["onboarding-profile-link"]["onboarding-profile-link"].Value,
),
)

// Schedule an UPDATE_MEMBER job to update the member's location.
// UpdateMember() could be called directly here, however
Expand All @@ -470,7 +480,38 @@ func UpsertMemberProfileInfo(ctx context.Context, db *gorm.DB, interaction *slac
ChannelID: pm.ChannelID,
ProfileType: sqlcrypter.NewEncryptedBytes(profileType),
ProfileLink: sqlcrypter.NewEncryptedBytes(profileLink),
IsActive: true,
}

if err := QueueUpdateMemberJob(ctx, db, p); err != nil {
return errors.Wrap(err, "failed to add UPDATE_MEMBER job to the queue")
}

return nil
}

// SetMemberIsActive marks a new member as active in the database
// after they have completed the onboarding flow.
func SetMemberIsActive(ctx context.Context, db *gorm.DB, interaction *slack.InteractionCallback) error {
// Start new span
tracer := otel.Tracer("")
ctx, span := tracer.Start(ctx, "upsert.is_active")
defer span.End()

// Extract the ChannelID from the private_metadata field
var pm privateMetadata
if err := pm.Decode(interaction.View.PrivateMetadata); err != nil {
return errors.Wrap(err, "failed to decode base64 string to privateMetadata")
}

// Schedule an UPDATE_MEMBER job to update the member's location.
// UpdateMember() could be called directly here, however
// scheduling a background job will ensure it is reliably executed.
isActive := true

p := &UpdateMemberParams{
UserID: interaction.User.ID,
ChannelID: pm.ChannelID,
IsActive: &isActive,
}

if err := QueueUpdateMemberJob(ctx, db, p); err != nil {
Expand Down Expand Up @@ -553,7 +594,6 @@ func UpsertMemberCalendlyLink(ctx context.Context, db *gorm.DB, interaction *sla
UserID: interaction.User.ID,
ChannelID: pm.ChannelID,
CalendlyLink: sqlcrypter.NewEncryptedBytes(calendlyLink),
IsActive: true,
}

if err := QueueUpdateMemberJob(ctx, db, p); err != nil {
Expand Down
10 changes: 7 additions & 3 deletions internal/bot/job_greet_member_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,13 +500,15 @@ b3N0L2FjdGlvbnMvYS9iL2MifQ==`,

db, mock := database.NewMockedGormDB()

hasGenderPreference := true

database.MockQueueJob(
mock,
&UpdateMemberParams{
ChannelID: "C0123456789",
UserID: userID,
Gender: models.Male.String(),
HasGenderPreference: true,
HasGenderPreference: &hasGenderPreference,
},
models.JobTypeUpdateMember.String(),
models.JobPriorityHigh,
Expand Down Expand Up @@ -577,7 +579,6 @@ b3N0L2FjdGlvbnMvYS9iL2MifQ==`,
UserID: userID,
ProfileType: sqlcrypter.NewEncryptedBytes(profileType),
ProfileLink: sqlcrypter.NewEncryptedBytes(profileLink),
IsActive: true,
},
models.JobTypeUpdateMember.String(),
models.JobPriorityHigh,
Expand Down Expand Up @@ -774,7 +775,6 @@ b3N0L2FjdGlvbnMvYS9iL2MifQ==`,
ChannelID: "C0123456789",
UserID: userID,
CalendlyLink: sqlcrypter.NewEncryptedBytes(calendlyLink),
IsActive: true,
},
models.JobTypeUpdateMember.String(),
models.JobPriorityHigh,
Expand All @@ -784,6 +784,10 @@ b3N0L2FjdGlvbnMvYS9iL2MifQ==`,
assert.Nil(t, err)
}

func Test_SetMemberIsActive(t *testing.T) {
// TODO
}

func Test_RespondGreetMemberWebhook(t *testing.T) {
userID := "U0123456789"
channelID := "C9876543210"
Expand Down
4 changes: 3 additions & 1 deletion internal/bot/job_notify_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ func NotifyPair(ctx context.Context, db *gorm.DB, client *slack.Client, p *Notif
// We can marshal the template into View as it contains Blocks
var view slack.View
if err := json.Unmarshal([]byte(content), &view); err != nil {
return errors.Wrap(err, "failed to marshal JSON")
message := "failed to marshal JSON"
logger.Error(message, "error", err)
return errors.Wrap(err, message)
}

// Send the Slack group message to the pair
Expand Down
23 changes: 14 additions & 9 deletions internal/bot/job_notify_pair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func Test_notifyPairTemplate(t *testing.T) {
UserID: "U0123456789",
Country: sqlcrypter.NewEncryptedBytes("Kenya"),
City: sqlcrypter.NewEncryptedBytes("Nairobi"),
ProfileType: sqlcrypter.NewEncryptedBytes("Github"),
ProfileType: sqlcrypter.NewEncryptedBytes("GitHub"),
ProfileLink: sqlcrypter.NewEncryptedBytes("https://github.com/AhmedARmohamed"),
CalendlyLink: sqlcrypter.NewEncryptedBytes("https://calendly.com/AhmedARmohamed"),
},
Expand All @@ -56,7 +56,7 @@ func Test_notifyPairTemplate(t *testing.T) {
UserID: "U9876543210",
Country: sqlcrypter.NewEncryptedBytes("United States"),
City: sqlcrypter.NewEncryptedBytes("Phoenix"),
ProfileType: sqlcrypter.NewEncryptedBytes("Github"),
ProfileType: sqlcrypter.NewEncryptedBytes("GitHub"),
ProfileLink: sqlcrypter.NewEncryptedBytes("https://github.com/bincyber"),
},
PartnerTimezone: "MST (UTC-07:00)",
Expand Down Expand Up @@ -123,7 +123,7 @@ func (s *NotifyPairSuite) Test_NotifyPair() {
db.Create(&models.Channel{
ChannelID: channelID,
Inviter: "U9876543210",
ConnectionMode: models.VirtualConnectionMode,
ConnectionMode: models.PhysicalConnectionMode,
Interval: models.Biweekly,
Weekday: time.Friday,
Hour: 12,
Expand All @@ -142,6 +142,9 @@ func (s *NotifyPairSuite) Test_NotifyPair() {
HasGenderPreference: new(bool),
Country: sqlcrypter.NewEncryptedBytes("Canada"),
City: sqlcrypter.NewEncryptedBytes("Toronto"),
ProfileType: sqlcrypter.NewEncryptedBytes("GitHub"),
ProfileLink: sqlcrypter.NewEncryptedBytes("github.com/user1"),
CalendlyLink: sqlcrypter.NewEncryptedBytes("https://calendly.com/example"),
})

secondUserID := "U5555666778"
Expand All @@ -153,6 +156,8 @@ func (s *NotifyPairSuite) Test_NotifyPair() {
HasGenderPreference: new(bool),
Country: sqlcrypter.NewEncryptedBytes("United Kingdom"),
City: sqlcrypter.NewEncryptedBytes("Manchester"),
ProfileType: sqlcrypter.NewEncryptedBytes("Twitter"),
ProfileLink: sqlcrypter.NewEncryptedBytes("twitter.com/user2"),
})

// Write records in the rounds and matches table
Expand Down Expand Up @@ -186,15 +191,16 @@ func (s *NotifyPairSuite) Test_NotifyPair() {
w.Write([]byte(`{"ok":false}`))
}

r.Len(blocks.BlockSet, 8)
r.Len(blocks.BlockSet, 12)

participantSection, ok := blocks.BlockSet[5].(*slack.SectionBlock)
// Verify
participantSection, ok := blocks.BlockSet[4].(*slack.SectionBlock)
r.True(ok)
r.Contains(participantSection.Text.Text, "*Name:* <@U0123456789>\n*Location:* Toronto, Canada")
r.Contains(participantSection.Text.Text, fmt.Sprintf(":identification_card: *Name:* <@%s>", firstUserID))

partnerSection, ok := blocks.BlockSet[7].(*slack.SectionBlock)
partnerSection, ok := blocks.BlockSet[8].(*slack.SectionBlock)
r.True(ok)
r.Contains(partnerSection.Text.Text, "*Name:* <@U5555666778>\n*Location:* Manchester, United Kingdom")
r.Contains(partnerSection.Text.Text, fmt.Sprintf(":identification_card: *Name:* <@%s>", secondUserID))

w.Write([]byte(`{
"ok": true,
Expand All @@ -208,7 +214,6 @@ func (s *NotifyPairSuite) Test_NotifyPair() {
url := fmt.Sprintf("%s/", httpServer.URL)
client := slack.New("xoxb-test-token-here", slack.OptionAPIURL(url))

// Test
err = NotifyPair(s.ctx, db, client, &NotifyPairParams{
ChannelID: channelID,
MatchID: 1,
Expand Down
Loading

0 comments on commit 7212e37

Please sign in to comment.