added proper reverse-contact addition functionality so users are connected back
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -33,7 +34,12 @@ const ActionsPromptPart = `=== AVAILABLE ACTIONS ===
|
||||
{"type": "set_chat", "chat": "telegram"}
|
||||
{"type": "search", "query": "search query"}
|
||||
|
||||
=== OUTPUT EXAMPLE (ALWAYS PROVIDE SIMILAR RESPONSE, ONLY SUCH FORMAT IS CORRECTLY INTERPRETED) ===
|
||||
ONLY USE ACTION TYPES EXISTING ON THAT LIST
|
||||
ACTIONS LIST SHOULD START WITH [ AND END WITH ]
|
||||
ARRAY SHOULD CONTAIN PARSABLE ARRAY OF JSON OBJECTS
|
||||
EMPTY ARRAYS ARE NOT ALLOWED
|
||||
|
||||
=== OUTPUT EXAMPLE ===
|
||||
User has 2 facts and no ping notification, I should set one up. Also, he asked to remind him about gym and message his dad.
|
||||
[{"type":"add_fact","value":"goes to gym"},{"type":"add_notification","time":"2026-04-25 09:00","content":"gym workout","repeat_on":"every monday, wednesday, friday at 09:00"},{"type":"message","text":"hey dad, your son is hitting the gym today 💪","target":"dad"},{"type":"wait","ms":1200},{"type":"message","platform":"telegram","text":"got it! reminded you for MWF 9am and messaged your dad"},{"type":"wait","ms":600},{"type":"message","platform":"telegram","text":"🔥"}]
|
||||
`
|
||||
@@ -349,17 +355,39 @@ func (a AddContact) Validate(ctx context.Context, rt *Runtime) error {
|
||||
}
|
||||
|
||||
func (a AddContact) Execute(ctx context.Context, rt *Runtime) error {
|
||||
contactUser, err := rt.Database.Users().Get(ctx, uuid.MustParse(a.UUID), database.UserLookupByContactCode)
|
||||
targetUser, err := rt.Database.Users().Get(ctx, uuid.MustParse(a.UUID), database.UserLookupByContactCode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add_contact: failed to get contact user: %w", err)
|
||||
}
|
||||
if err = rt.Database.Contacts().Add(ctx, &database.Contact{
|
||||
OwnerID: rt.User.ID,
|
||||
TargetID: contactUser.ID,
|
||||
TargetID: targetUser.ID,
|
||||
Name: a.Name,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("add_contact: failed to add contact: %w", err)
|
||||
}
|
||||
targetUser.ContactCode = uuid.New()
|
||||
if err = rt.Database.Users().Update(ctx, targetUser); err != nil {
|
||||
return fmt.Errorf("add_contact: failed to update target user contact code: %w", err)
|
||||
}
|
||||
targetUserContacts, err := rt.Database.Contacts().List(ctx, targetUser.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add_contact: failed to list target user contacts: %w", err)
|
||||
}
|
||||
if !slices.ContainsFunc(targetUserContacts, func(c database.Contact) bool { return c.TargetID == rt.User.ID }) {
|
||||
if err = rt.Database.Notifications().Push(ctx, &database.Notification{
|
||||
ID: uuid.New(),
|
||||
UserID: targetUser.ID,
|
||||
InitiatorID: rt.User.ID,
|
||||
ScheduledAt: time.Now(),
|
||||
Content: fmt.Sprintf(
|
||||
"SYSTEM: CREATE CONTACT NOTIFICAION: added to contacts. add_contact back with uuid=%s, ask user for name",
|
||||
rt.User.ContactCode.String(),
|
||||
),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("add_contact: failed to notify user back of contact addition operation: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1225,6 +1225,138 @@ func TestAddContact_Execute(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to add contact")
|
||||
})
|
||||
|
||||
t.Run("successful add with back-notification", func(t *testing.T) {
|
||||
rt := NewTestRuntime()
|
||||
contactUserID := uuid.New()
|
||||
contactCodeUpdated := false
|
||||
contactsListed := false
|
||||
notificationPushed := false
|
||||
|
||||
rt.Database.(*TestDB).UsersDB.GetFunc = func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
||||
return &database.User{ID: contactUserID, ContactCode: uuid.New()}, nil
|
||||
}
|
||||
rt.Database.(*TestDB).UsersDB.UpdateFunc = func(ctx context.Context, u *database.User) error {
|
||||
assert.Equal(t, contactUserID, u.ID)
|
||||
contactCodeUpdated = true
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.AddFunc = func(ctx context.Context, contact *database.Contact) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.ListFunc = func(ctx context.Context, ownerID uuid.UUID) ([]database.Contact, error) {
|
||||
assert.Equal(t, contactUserID, ownerID)
|
||||
contactsListed = true
|
||||
return []database.Contact{}, nil // empty = not mutual yet
|
||||
}
|
||||
rt.Database.(*TestDB).NotificationsDB.PushFunc = func(ctx context.Context, n *database.Notification) error {
|
||||
assert.Equal(t, contactUserID, n.UserID)
|
||||
assert.Equal(t, rt.User.ID, n.InitiatorID)
|
||||
assert.Contains(t, n.Content, "CREATE CONTACT NOTIFICAION")
|
||||
assert.Contains(t, n.Content, rt.User.ContactCode.String())
|
||||
notificationPushed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
err := AddContact{UUID: uuid.New().String(), Name: "Brother"}.Execute(t.Context(), rt)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, contactCodeUpdated)
|
||||
assert.True(t, contactsListed)
|
||||
assert.True(t, notificationPushed)
|
||||
})
|
||||
|
||||
t.Run("successful add already mutual", func(t *testing.T) {
|
||||
rt := NewTestRuntime()
|
||||
contactUserID := uuid.New()
|
||||
notificationPushed := false
|
||||
|
||||
rt.Database.(*TestDB).UsersDB.GetFunc = func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
||||
return &database.User{ID: contactUserID, ContactCode: uuid.New()}, nil
|
||||
}
|
||||
rt.Database.(*TestDB).UsersDB.UpdateFunc = func(ctx context.Context, u *database.User) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.AddFunc = func(ctx context.Context, contact *database.Contact) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.ListFunc = func(ctx context.Context, ownerID uuid.UUID) ([]database.Contact, error) {
|
||||
return []database.Contact{{TargetID: rt.User.ID}}, nil // already mutual
|
||||
}
|
||||
rt.Database.(*TestDB).NotificationsDB.PushFunc = func(ctx context.Context, n *database.Notification) error {
|
||||
notificationPushed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
err := AddContact{UUID: uuid.New().String(), Name: "Brother"}.Execute(t.Context(), rt)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, notificationPushed)
|
||||
})
|
||||
|
||||
t.Run("failed to update contact code", func(t *testing.T) {
|
||||
rt := NewTestRuntime()
|
||||
contactUserID := uuid.New()
|
||||
|
||||
rt.Database.(*TestDB).UsersDB.GetFunc = func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
||||
return &database.User{ID: contactUserID, ContactCode: uuid.New()}, nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.AddFunc = func(ctx context.Context, contact *database.Contact) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).UsersDB.UpdateFunc = func(ctx context.Context, u *database.User) error {
|
||||
return errors.New("update failed")
|
||||
}
|
||||
|
||||
err := AddContact{UUID: uuid.New().String(), Name: "Brother"}.Execute(t.Context(), rt)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to update target user contact code")
|
||||
})
|
||||
|
||||
t.Run("failed to list target contacts", func(t *testing.T) {
|
||||
rt := NewTestRuntime()
|
||||
contactUserID := uuid.New()
|
||||
|
||||
rt.Database.(*TestDB).UsersDB.GetFunc = func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
||||
return &database.User{ID: contactUserID, ContactCode: uuid.New()}, nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.AddFunc = func(ctx context.Context, contact *database.Contact) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).UsersDB.UpdateFunc = func(ctx context.Context, u *database.User) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.ListFunc = func(ctx context.Context, ownerID uuid.UUID) ([]database.Contact, error) {
|
||||
return nil, errors.New("list failed")
|
||||
}
|
||||
|
||||
err := AddContact{UUID: uuid.New().String(), Name: "Brother"}.Execute(t.Context(), rt)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to list target user contacts")
|
||||
})
|
||||
|
||||
t.Run("failed to push back-notification", func(t *testing.T) {
|
||||
rt := NewTestRuntime()
|
||||
contactUserID := uuid.New()
|
||||
|
||||
rt.Database.(*TestDB).UsersDB.GetFunc = func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
||||
return &database.User{ID: contactUserID, ContactCode: uuid.New()}, nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.AddFunc = func(ctx context.Context, contact *database.Contact) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).UsersDB.UpdateFunc = func(ctx context.Context, u *database.User) error {
|
||||
return nil
|
||||
}
|
||||
rt.Database.(*TestDB).ContactsDB.ListFunc = func(ctx context.Context, ownerID uuid.UUID) ([]database.Contact, error) {
|
||||
return []database.Contact{}, nil
|
||||
}
|
||||
rt.Database.(*TestDB).NotificationsDB.PushFunc = func(ctx context.Context, n *database.Notification) error {
|
||||
return errors.New("push failed")
|
||||
}
|
||||
|
||||
err := AddContact{UUID: uuid.New().String(), Name: "Brother"}.Execute(t.Context(), rt)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to notify user back")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddNotification_Validate(t *testing.T) {
|
||||
|
||||
@@ -124,23 +124,22 @@ EMOJI: 🌺🕊️📞💛 USE WARMLY AND SPARINGLY
|
||||
YOU ARE PROCESSING A USER NOTIFICATION
|
||||
YOUR GOAL IS TO SEND USER CONTANT A MESSAGE USING message COMMAND
|
||||
NOTIFICATION MESSAGE SHOULD BE BASED ON NOTIFICAION CONTENT IN COONTEXT
|
||||
IF NOTIFICAION IS "CREATE CONTACT NOTIFICAION" - YOU SHOULD TAKE CONTACT NAME FROM MESSAGES, UUID FROM NOTIFICAION AND EXECUTE add_contact FOR USER, AFTEER THAT - INFORM USER WITH MESSAGE
|
||||
`
|
||||
repeatedOnTmpl = `
|
||||
YOU SHOULD CREATE NEXT NOTIFICAION, USING PROVIDED TIME CONTEXT
|
||||
NEW NOTIFICAION SHOULD BE POSSIBLY KEPT AS IS, WITH ONLY TIME CHANGED
|
||||
`
|
||||
message = `
|
||||
IF MESSAGE CONTAINS INFORMATION ABOUT USER WHICH IS NOT PRESENT IN THE CONTEXT, USE add_fact TO PERSIST IT TO DATABASE, ADD ALL FACTS THAT MIGHT IMPROVE USER ENGAGEMENT AND MAKE EXPERIENCE MORE PERSONALIZED
|
||||
EXTRACT FACTS FROM MESSAGES AGGRESSIVELY — ANY PERSONAL INFO THAT IMPROVES PERSONALIZATION
|
||||
USE remove_fact ONLY TO REMOVE FACTS THAT ARE OUTDATED OR WHEN INFORMATION CHANGED OR IF USER ASKS TO FORGET ABOUT SOMETHING
|
||||
IF MESSAGE HAS SOMETHING REALLY PERSONAL OR IMPORTANT - BE VERBOSE, LIKE REALLY UNDERSTANDING HUMAN, SUGGEST SOMETHING
|
||||
IF USER ASKS TO ADD CONTACT, FIRST - ASK ABOUT CONTACT NAME WHICH YOU WILL USE, SECOND - GIVE HIM HIS CONTACT CODE, AND TELL TO SHARE THAT WITH FRIEND, FRIEND SHOULD PUT IT INTO JULES AND ASK TO CREATE CONTACT
|
||||
IF USERS ASKS TO CONNECT ANOTHER MESSANGER/EMAIL - TELL HIM TO START A DIALOGUE WITH JULES FROM THERE, AND SEND BIND CODE TO JULES, TO MERGE ACCOUNTS
|
||||
IF MESSAGE HAS SOMETHING REALLY PERSONAL OR IMPORTANT - BE VERBOSE, LIKE A REAL UNDERSTANDING HUMAN, SUGGEST SOMETHING
|
||||
GIVE USER HIS CONTACT CODE ONLY WHEN HE EXPLICITLY ASKS TO ADD A CONTACT
|
||||
GIVE USER HIS BIND CODE ONLY WHEN HE EXPLICITLY ASKS TO CONNECT ANOTHER MESSENGER
|
||||
`
|
||||
errs = `
|
||||
THERE WERE ERRORS ON PREVIOUS EXECUTION
|
||||
TRY TO FIGURE OUT IF YOU CAN SOLVE THEM BY YOURSELF
|
||||
IF YOU CAN'T (USER INPUT ERRORS) - INFORM USER ABOUT THAT
|
||||
PREVIOUS EXECUTION CAUSED ERRORS, THIS ATTEMPT IS RETRY
|
||||
YOU FAILED TO PROVIDE VALID ACTIONS LIST
|
||||
IF NOT USER INPUT ERROR - FIX, OTHERWISE - INFORM USER
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user