Commit 58bbb632 authored by Mo Firouz's avatar Mo Firouz
Browse files

Add in-app notification feature. Merged #93

parent 40da17a4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p
- New storage list feature.
- Ban users and create groups from within the Runtime environment.
- New In-App Purchase validation feature. 
- New In-App Notification feature.

### Changed
- Run Facebook friends import after registration completes.
+3 −2
Original line number Diff line number Diff line
@@ -92,15 +92,16 @@ func main() {
	messageRouter := server.NewMessageRouterService(sessionRegistry)
	presenceNotifier := server.NewPresenceNotifier(jsonLogger, config.GetName(), trackerService, messageRouter)
	trackerService.AddDiffListener(presenceNotifier.HandleDiff)
	notificationService := server.NewNotificationService(jsonLogger, db, trackerService, messageRouter, config.GetSocial().Notification)

	runtime, err := server.NewRuntime(jsonLogger, multiLogger, db, config.GetRuntime())
	runtime, err := server.NewRuntime(jsonLogger, multiLogger, db, config.GetRuntime(), notificationService)
	if err != nil {
		multiLogger.Fatal("Failed initializing runtime modules.", zap.Error(err))
	}

	socialClient := social.NewClient(5 * time.Second)
	purchaseService := server.NewPurchaseService(jsonLogger, multiLogger, db, config.GetPurchase())
	pipeline := server.NewPipeline(config, db, trackerService, matchmakerService, messageRouter, sessionRegistry, socialClient, runtime, purchaseService)
	pipeline := server.NewPipeline(config, db, trackerService, matchmakerService, messageRouter, sessionRegistry, socialClient, runtime, purchaseService, notificationService)
	authService := server.NewAuthenticationService(jsonLogger, config, db, statsService, sessionRegistry, socialClient, pipeline, runtime)
	dashboardService := server.NewDashboardService(jsonLogger, multiLogger, semver, config, statsService)

+17 −0
Original line number Diff line number Diff line
@@ -51,5 +51,22 @@ CREATE INDEX IF NOT EXISTS purchase_user_id_created_at_provider_receipt_id_idx O
-- list purchases by most recent timestamp
CREATE INDEX IF NOT EXISTS purchase_created_at_user_id_provider_receipt_id_idx ON purchase (created_at, user_id, provider, receipt_id);

CREATE TABLE IF NOT EXISTS notification (
    PRIMARY KEY (id),
    id              BYTEA        NOT NULL,
    user_id         BYTEA        NOT NULL,
    subject         VARCHAR(255) NOT NULL,
    content         BYTEA        DEFAULT '{}' CHECK (length(content) < 16000) NOT NULL,
    code            SMALLINT     NOT NULL,      -- O to 100 is System reserved.
    sender_id       BYTEA,                      -- NULL for System messages
    created_at      BIGINT       CHECK (created_at > 0) NOT NULL,
    expires_at      BIGINT       CHECK (expires_at > created_at) NOT NULL,
    deleted_at      BIGINT       DEFAULT 0 NOT NULL
);

-- list notifications for a user that are not deleted or expired, starting from a given ID (cursor).
CREATE INDEX IF NOT EXISTS notification_user_id_deleted_at_expires_at_id_idx ON notification (user_id, deleted_at ASC, expires_at ASC, id);

-- +migrate Down
DROP TABLE IF EXISTS purchase;
DROP TABLE IF EXISTS notification;
+62 −7
Original line number Diff line number Diff line
@@ -258,6 +258,11 @@ message Envelope {

    TPurchaseValidation purchase = 65;
    TPurchaseRecord purchase_record = 66;

    TNotificationsList notifications_list = 67;
    TNotificationsRemove notifications_remove = 68;
    TNotifications notifications = 69;
    Notifications live_notifications = 70;
  }
}

@@ -567,14 +572,14 @@ message TGroupsRemove {
/**
 * TGroupsSelfList fetches a list of groups that the current user is part of.
 *
 * @returns TGroup
 * @returns TGroups
 */
message TGroupsSelfList {}

/**
 * TGroupsFetch fetches a list of groups that the match the group ID or group name.
 *
 * @returns TGroup
 * @returns TGroups
 */
message TGroupsFetch {
  message GroupFetch {
@@ -1280,15 +1285,65 @@ message TPurchaseValidation {
 * TPurchaseRecord is the response of purchase validation
 */
message TPurchaseRecord {
  // Whether or not the transaction is valid and all the information matches.
  /// Whether or not the transaction is valid and all the information matches.
  bool success = 1;
  // If this is a new transaction or if Nakama has a log of it.
  /// If this is a new transaction or if Nakama has a log of it.
  bool seen_before = 2;
  // Indicates whether or not Nakama was able to reach the remote purchase service.
  /// Indicates whether or not Nakama was able to reach the remote purchase service.
  bool purchase_provider_reachable = 3;
  // A string indicating why the purchase verification failed, if appropriate.
  /// A string indicating why the purchase verification failed, if appropriate.
  string message = 6;
  // The complete response Nakama received from the remote service.
  /// The complete response Nakama received from the remote service.
  string data = 5;
}

/**
 * Notification is the core domain type representing an in-app notification.
 */
message Notification {
  bytes id = 1;
  string subject = 2;
  bytes content = 3;
  int64 code = 4;
  bytes sender_id = 5;
  int64 created_at = 6;
  int64 expires_at = 7;
  bool persistent = 8;
}

/**
 * Notification is the core domain type representing a list of live in-app notification.
 */
message Notifications {
  repeated Notification notifications = 1;
}

/**
 * TNotificationsList is used to list unexpired notifications.
 */
message TNotificationsList {
  /// Max number of notifications to list. Between 10 and 100.
  int64 limit = 1;
  /// Use this cursor to paginate notifications.
  /// Cache this to catch up to new notifications.
  /// The value of this comes from TNotifications.resumable_cursor.
  bytes resumable_cursor = 2;
}

/**
 * TNotifications is the response of listing notifications
 */
message TNotifications {
  repeated Notification notifications = 1;
  /// Use this cursor to paginate notifications.
  /// Cache this to catch up to new notifications.
  bytes resumable_cursor = 2;
}

/**
 * TNotificationsRemove is used to delete notifications.
 */
message TNotificationsRemove {
  repeated bytes notification_ids = 1;
}
+10 −1
Original line number Diff line number Diff line
@@ -240,6 +240,7 @@ func NewDatabaseConfig() *DatabaseConfig {

// SocialConfig is configuration relevant to the Social providers
type SocialConfig struct {
	Notification *NotificationConfig `yaml:"notification" json:"notification" usage:"Notification configuration"`
	Steam        *SocialConfigSteam  `yaml:"steam" json:"steam" usage:"Steam configuration"`
}

@@ -249,6 +250,11 @@ type SocialConfigSteam struct {
	AppID        int    `yaml:"app_id" json:"app_id" usage:"Steam App ID."`
}

// NotificationConfig is configuration relevant to notification center
type NotificationConfig struct {
	ExpiryMs int64 `yaml:"expiry_ms" json:"expiry_ms" usage:"Notification expiry in milliseconds."`
}

// NewSocialConfig creates a new SocialConfig struct
func NewSocialConfig() *SocialConfig {
	return &SocialConfig{
@@ -256,6 +262,9 @@ func NewSocialConfig() *SocialConfig {
			PublisherKey: "",
			AppID:        0,
		},
		Notification: &NotificationConfig{
			ExpiryMs: 86400000, // one day expiry
		},
	}
}

Loading