Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send Notification during ticket assignment #2520

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions db/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package db
import (
"errors"
"fmt"
"github.com/google/uuid"
"gorm.io/gorm"
"net/http"
"strings"
"time"
Expand Down Expand Up @@ -391,3 +393,36 @@ func (db database) UpdateFeatureStatus(uuid string, status FeatureStatus) (Works
db.db.Where("uuid = ?", uuid).First(&feature)
return feature, nil
}

func (db database) SaveNotification(pubkey, event, content, status string) error {
notification := Notification{
UUID: uuid.New().String(),
PubKey: pubkey,
Event: event,
Content: content,
Status: NotificationStatus(status),
}

result := db.db.Create(&notification)
if result.Error != nil {
return fmt.Errorf("error saving notification: %v", result.Error)
}

return nil
}

func (db database) GetNotificationsByStatus(status string) []Notification {
var notifications []Notification
db.db.Where("status = ?", status).Find(&notifications)
return notifications
}

func (db database) IncrementNotificationRetry(notificationUUID string) {
db.db.Model(&Notification{}).Where("uuid = ?", notificationUUID).
Update("retries", gorm.Expr("retries + 1"))
}

func (db database) UpdateNotificationStatus(notificationUUID string, status string) {
db.db.Model(&Notification{}).Where("uuid = ?", notificationUUID).
Updates(map[string]interface{}{"status": status, "updated_at": time.Now()})
}
14 changes: 9 additions & 5 deletions db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,14 @@ type Database interface {
DeleteTicketGroup(TicketGroupUUID uuid.UUID) error
PauseBountyTiming(bountyID uint) error
ResumeBountyTiming(bountyID uint) error
SaveNotification(pubkey, event, content, status string) error
GetNotificationsByStatus(status string) []Notification
IncrementNotificationRetry(notificationUUID string)
UpdateNotificationStatus(notificationUUID string, status string)
CreateOrEditTicketPlan(plan *TicketPlan) (*TicketPlan, error)
GetTicketPlan(uuid string) (*TicketPlan, error)
DeleteTicketPlan(uuid string) error
GetTicketPlansByFeature(featureUUID string) ([]TicketPlan, error)
GetTicketPlansByPhase(phaseUUID string) ([]TicketPlan, error)
GetTicketPlansByWorkspace(workspaceUUID string) ([]TicketPlan, error)
GetTicketPlan(uuid string) (*TicketPlan, error)
DeleteTicketPlan(uuid string) error
GetTicketPlansByFeature(featureUUID string) ([]TicketPlan, error)
GetTicketPlansByPhase(phaseUUID string) ([]TicketPlan, error)
GetTicketPlansByWorkspace(workspaceUUID string) ([]TicketPlan, error)
}
110 changes: 110 additions & 0 deletions handlers/bounty.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,113 @@ func (h *bountyHandler) GetPersonAssignedBounties(w http.ResponseWriter, r *http
}
}

func getContactKey(pubkey string) (*string, error) {
url := fmt.Sprintf("%s/contact/%s", config.V2BotUrl, pubkey)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %v", err)
}
req.Header.Set("x-admin-token", config.V2BotToken)

resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("error fetching contact: %v", err)
}
defer resp.Body.Close()

var contactResp struct {
ContactKey *string `json:"contact_key"`
}
if err := json.NewDecoder(resp.Body).Decode(&contactResp); err != nil {
return nil, fmt.Errorf("error decoding contact response: %v", err)
}

return contactResp.ContactKey, nil
}

func ProcessWaitingNotifications() {
notifications := db.DB.GetNotificationsByStatus("WAITING_KEY_EXCHANGE")

for _, n := range notifications {
contactKey, err := getContactKey(n.PubKey)
if err != nil {
logger.Log.Error("Error checking contact key for pubkey %s: %v", n.PubKey, err)
db.DB.IncrementNotificationRetry(n.UUID)
continue
}

if contactKey == nil {
db.DB.IncrementNotificationRetry(n.UUID)
continue
}

// Contact key is available, proceed with sending
sendRespStatus := sendNotification(n.PubKey, n.Content)
db.DB.UpdateNotificationStatus(n.UUID, sendRespStatus)
}
}

func sendNotification(pubkey, content string) string {
sendURL := fmt.Sprintf("%s/send", config.V2BotUrl)
msgBody, _ := json.Marshal(map[string]interface{}{
"dest": pubkey,
"amt_msat": 0,
"content": content,
"is_tribe": false,
"wait": true,
})

req, err := http.NewRequest(http.MethodPost, sendURL, bytes.NewBuffer(msgBody))
if err != nil {
logger.Log.Error("Error creating send request: %v", err)
return "FAILED"
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-admin-token", config.V2BotToken)

resp, err := http.DefaultClient.Do(req)
if err != nil {
logger.Log.Error("Error sending notification: %v", err)
return "FAILED"
}
defer resp.Body.Close()

var sendResp struct {
Status string `json:"status"`
}
if err := json.NewDecoder(resp.Body).Decode(&sendResp); err != nil {
logger.Log.Error("Error decoding send response: %v", err)
return "FAILED"
}

return sendResp.Status
}

func processNotification(pubkey, event, content, alias string) string {
contactKey, err := getContactKey(pubkey)
if err != nil {
logger.Log.Error("Error checking contact key: %v", err)
return "FAILED"
}

if contactKey == nil {
addContactURL := fmt.Sprintf("%s/add_contact", config.V2BotUrl)
body, _ := json.Marshal(map[string]string{"contact_info": pubkey, "alias": alias})
req, _ := http.NewRequest(http.MethodPost, addContactURL, bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-admin-token", config.V2BotToken)
http.DefaultClient.Do(req)

contactKey, err = getContactKey(pubkey)
if err != nil || contactKey == nil {
db.DB.SaveNotification(pubkey, event, content, "WAITING_KEY_EXCHANGE")
return "FAILED"
}
}

return sendNotification(pubkey, content)
}

func (h *bountyHandler) CreateOrEditBounty(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
Expand Down Expand Up @@ -285,6 +392,9 @@ func (h *bountyHandler) CreateOrEditBounty(w http.ResponseWriter, r *http.Reques
handleTimingError(w, "start_timing", err)
}
}

msg := fmt.Sprintf("You have been assigned a new ticket: %s.", bounty.Title)
processNotification(bounty.Assignee, "bounty_assigned", msg, user.OwnerAlias)
}

if bounty.Tribe == "" {
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func main() {
func runCron() {
c := cron.New()
c.AddFunc("@every 0h30m0s", handlers.InitV2PaymentsCron)
c.AddFunc("@every 0h0m30s", handlers.ProcessWaitingNotifications)
c.Start()
}

Expand Down
Loading
Loading