added proper reverse-contact addition functionality so users are connected back

This commit is contained in:
d1nch8g
2026-04-26 23:32:07 +03:00
parent ffd82073a7
commit 4b9e0f9231
3 changed files with 170 additions and 11 deletions
+31 -3
View File
@@ -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
}
+132
View File
@@ -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) {
+7 -8
View File
@@ -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
`
)