fix: add call push category
This commit is contained in:
@@ -26,6 +26,7 @@ const (
|
||||
defaultPushPath = "/v3/push"
|
||||
defaultTTL = 86400
|
||||
defaultCallTTL = 30
|
||||
callCategory = "im_call"
|
||||
)
|
||||
|
||||
var handler Handler
|
||||
@@ -78,10 +79,11 @@ type androidPayload struct {
|
||||
}
|
||||
|
||||
type iosPayload struct {
|
||||
Alert iosAlert `json:"alert"`
|
||||
Sound string `json:"sound,omitempty"`
|
||||
Badge string `json:"badge,omitempty"`
|
||||
Extras map[string]string `json:"extras,omitempty"`
|
||||
Alert iosAlert `json:"alert"`
|
||||
Sound string `json:"sound,omitempty"`
|
||||
Badge string `json:"badge,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
Extras map[string]string `json:"extras,omitempty"`
|
||||
}
|
||||
|
||||
type iosAlert struct {
|
||||
@@ -327,6 +329,7 @@ func preparePayloads(rcpt *push.Receipt, config *configType) ([]*jpushPayload, [
|
||||
logs.Warn.Println("jpush: could not build payload:", err)
|
||||
return nil, nil
|
||||
}
|
||||
hydrateCallInviteData(rcpt.TenantID, data)
|
||||
if pushExpired(data) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -424,6 +427,9 @@ func pushData(pl push.Payload) (map[string]string, error) {
|
||||
if pl.From != "" {
|
||||
data["caller_id"] = pl.From
|
||||
}
|
||||
if display := payloadContentFirstString(pl.Content, "caller_display", "callerDisplay", "caller_name", "callerName", "display_name", "from_display"); display != "" {
|
||||
data["caller_display"] = display
|
||||
}
|
||||
}
|
||||
if pl.Replace != "" {
|
||||
data["replace"] = pl.Replace
|
||||
@@ -495,10 +501,11 @@ func buildPayload(audience map[string][]string, topic string, data map[string]st
|
||||
Priority: 1,
|
||||
},
|
||||
IOS: &iosPayload{
|
||||
Alert: iosAlert{Title: title, Body: alert},
|
||||
Sound: "default",
|
||||
Badge: badge,
|
||||
Extras: data,
|
||||
Alert: iosAlert{Title: title, Body: alert},
|
||||
Sound: "default",
|
||||
Badge: badge,
|
||||
Category: iosCategory(data),
|
||||
Extras: data,
|
||||
},
|
||||
HMOS: &hmosPayload{
|
||||
Title: title,
|
||||
@@ -514,9 +521,16 @@ func alertText(data map[string]string, config *configType) (string, string) {
|
||||
title := config.Title
|
||||
switch data["type"] {
|
||||
case "call_invite":
|
||||
caller := strings.TrimSpace(data["caller_display"])
|
||||
if data["call_type"] == "audio" {
|
||||
if caller != "" {
|
||||
return caller + "邀请你语音通话", "语音通话"
|
||||
}
|
||||
return "收到一通语音通话", "语音通话"
|
||||
}
|
||||
if caller != "" {
|
||||
return caller + "邀请你视频通话", "视频通话"
|
||||
}
|
||||
return "收到一通视频通话", "视频通话"
|
||||
case "call_missed":
|
||||
return "你有一通未接来电", "未接来电"
|
||||
@@ -527,6 +541,57 @@ func alertText(data map[string]string, config *configType) (string, string) {
|
||||
}
|
||||
}
|
||||
|
||||
func iosCategory(data map[string]string) string {
|
||||
if data["type"] == "call_invite" {
|
||||
return callCategory
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func hydrateCallInviteData(tenantID t.TenantID, data map[string]string) {
|
||||
if data["type"] != "call_invite" || strings.TrimSpace(data["caller_display"]) != "" {
|
||||
return
|
||||
}
|
||||
callerID := strings.TrimSpace(data["caller_id"])
|
||||
if callerID == "" || store.Users == nil {
|
||||
return
|
||||
}
|
||||
uid := t.ParseUserId(callerID)
|
||||
if uid.IsZero() {
|
||||
return
|
||||
}
|
||||
user, err := store.Users.Get(tenantID, uid)
|
||||
if err != nil || user == nil {
|
||||
return
|
||||
}
|
||||
if display := displayNameFromPublic(user.Public); display != "" {
|
||||
data["caller_display"] = display
|
||||
}
|
||||
}
|
||||
|
||||
func displayNameFromPublic(public any) string {
|
||||
for _, key := range []string{"display_name", "fn", "nickname", "name"} {
|
||||
if value := publicMapString(public, key); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func publicMapString(public any, key string) string {
|
||||
switch values := public.(type) {
|
||||
case map[string]any:
|
||||
value, ok := values[key]
|
||||
if !ok || value == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(fmt.Sprint(value))
|
||||
case map[string]string:
|
||||
return strings.TrimSpace(values[key])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cidFor(topic string, data map[string]string) string {
|
||||
parts := []string{data["type"], topic, data["seq"], data["replace"]}
|
||||
return strings.Trim(strings.Join(parts, "_"), "_")
|
||||
@@ -560,6 +625,15 @@ func payloadContentString(content any, key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func payloadContentFirstString(content any, keys ...string) string {
|
||||
for _, key := range keys {
|
||||
if value := payloadContentString(content, key); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func truncateRunes(value string, limit int) string {
|
||||
if limit <= 0 || len(value) <= limit {
|
||||
return value
|
||||
|
||||
@@ -174,11 +174,12 @@ func TestBuildPayloadUsesShortTTLForCalls(t *testing.T) {
|
||||
expiresAt := time.Now().Add(30 * time.Second).UTC().Format(time.RFC3339Nano)
|
||||
config := &configType{Title: "MeetingIM", DefaultTTLSeconds: 86400, CallTTLSeconds: 60}
|
||||
payload := buildPayload(map[string][]string{"alias": {"usr1"}}, "p2pTopic", map[string]string{
|
||||
"type": "call_invite",
|
||||
"topic": "p2pTopic",
|
||||
"seq": "30",
|
||||
"call_type": "audio",
|
||||
"expires_at": expiresAt,
|
||||
"type": "call_invite",
|
||||
"topic": "p2pTopic",
|
||||
"seq": "30",
|
||||
"call_type": "audio",
|
||||
"caller_display": "张三",
|
||||
"expires_at": expiresAt,
|
||||
}, 0, config)
|
||||
if payload.Options.TimeToLive > 30 {
|
||||
t.Fatalf("unexpected ttl: %d", payload.Options.TimeToLive)
|
||||
@@ -186,6 +187,12 @@ func TestBuildPayloadUsesShortTTLForCalls(t *testing.T) {
|
||||
if payload.Notification.Android.Title != "语音通话" {
|
||||
t.Fatalf("unexpected title: %s", payload.Notification.Android.Title)
|
||||
}
|
||||
if payload.Notification.IOS.Category != callCategory {
|
||||
t.Fatalf("unexpected iOS category: %s", payload.Notification.IOS.Category)
|
||||
}
|
||||
if payload.Notification.IOS.Alert.Body != "张三邀请你语音通话" {
|
||||
t.Fatalf("unexpected body: %s", payload.Notification.IOS.Alert.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushDataExtractsCallFields(t *testing.T) {
|
||||
@@ -198,8 +205,9 @@ func TestPushDataExtractsCallFields(t *testing.T) {
|
||||
Webrtc: "started",
|
||||
ExpiresAt: &expiresAt,
|
||||
Content: map[string]any{
|
||||
"callId": "call_1",
|
||||
"callType": "audio",
|
||||
"callId": "call_1",
|
||||
"callType": "audio",
|
||||
"caller_display": "张三",
|
||||
},
|
||||
Timestamp: time.Date(2026, 9, 4, 10, 0, 0, 0, time.UTC),
|
||||
})
|
||||
@@ -212,6 +220,9 @@ func TestPushDataExtractsCallFields(t *testing.T) {
|
||||
if data["expires_at"] != expiresAt.Format(time.RFC3339Nano) {
|
||||
t.Fatalf("unexpected expires_at: %#v", data)
|
||||
}
|
||||
if data["caller_display"] != "张三" {
|
||||
t.Fatalf("unexpected caller_display: %#v", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparePayloadsDropsExpiredCallInvite(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user