Unverified Commit 6c855dc9 authored by Fernando Takagi's avatar Fernando Takagi Committed by GitHub
Browse files

Expose FriendsAdd and FriendsDelete to runtime (#805)

parent 912bd695
Loading
Loading
Loading
Loading
+117 −1
Original line number Diff line number Diff line
@@ -3377,7 +3377,7 @@ func (n *RuntimeGoNakamaModule) MetricsTimerRecord(name string, tags map[string]
// @group friends
// @summary List all friends, invites, invited, and blocked which belong to a user.
// @param ctx(type=context.Context) The context object represents information about the server and requester.
// @param userId(type=string) The ID of the user who's friends, invites, invited, and blocked you want to list.
// @param userId(type=string) The ID of the user whose friends, invites, invited, and blocked you want to list.
// @param limit(type=int) The number of friends to retrieve in this page of results. No more than 100 limit allowed per result.
// @param state(type=int, optional=true) The state of the friendship with the user. If unspecified this returns friends in all states for the user.
// @param cursor(type=string) The cursor returned from a previous listing request. Used to obtain the next page of results.
@@ -3411,6 +3411,122 @@ func (n *RuntimeGoNakamaModule) FriendsList(ctx context.Context, userID string,
	return friends.Friends, friends.Cursor, nil
}

// @group friends
// @summary Add friends to a user.
// @param ctx(type=context.Context) The context object represents information about the server and requester.
// @param userId(type=string) The ID of the user to whom you want to add friends.
// @param username(type=string) The name of the user to whom you want to add friends.
// @param ids(type=[]string) The IDs of the users you want to add as friends.
// @param usernames(type=[]string) The usernames of the users you want to add as friends.
// @return error(error) An optional error value if an error occurred.
func (n *RuntimeGoNakamaModule) FriendsAdd(ctx context.Context, userID string, username string, ids []string, usernames []string) error {
	userUUID, err := uuid.FromString(userID)
	if err != nil {
		return errors.New("expects user ID to be a valid identifier")
	}

	if len(ids) == 0 && len(usernames) == 0 {
		return nil
	}

	for _, id := range ids {
		if userID == id {
			return errors.New("cannot add self as friend")
		}
		if uid, err := uuid.FromString(id); err != nil || uid == uuid.Nil {
			return fmt.Errorf("invalid user ID '%v'", id)
		}
	}

	for _, u := range usernames {
		if u == "" {
			return errors.New("username to add must not be empty")
		}
		if username == u {
			return errors.New("cannot add self as friend")
		}
	}

	fetchIDs, err := fetchUserID(ctx, n.db, usernames)
	if err != nil {
		n.logger.Error("Could not fetch user IDs.", zap.Error(err), zap.Strings("usernames", usernames))
		return errors.New("error while trying to add friends")
	}

	if len(fetchIDs)+len(ids) == 0 {
		return errors.New("no valid ID or username was provided")
	}

	allIDs := make([]string, 0, len(ids)+len(fetchIDs))
	allIDs = append(allIDs, ids...)
	allIDs = append(allIDs, fetchIDs...)

	err = AddFriends(ctx, n.logger, n.db, n.router, userUUID, username, allIDs)
	if err != nil {
		return err
	}

	return nil
}

// @group friends
// @summary Delete friends from a user.
// @param ctx(type=context.Context) The context object represents information about the server and requester.
// @param userId(type=string) The ID of the user from whom you want to delete friends.
// @param username(type=string) The name of the user from whom you want to delete friends.
// @param ids(type=[]string) The IDs of the users you want to delete as friends.
// @param usernames(type=[]string) The usernames of the users you want to delete as friends.
// @return error(error) An optional error value if an error occurred.
func (n *RuntimeGoNakamaModule) FriendsDelete(ctx context.Context, userID string, username string, ids []string, usernames []string) error {
	userUUID, err := uuid.FromString(userID)
	if err != nil {
		return errors.New("expects user ID to be a valid identifier")
	}

	if len(ids) == 0 && len(usernames) == 0 {
		return nil
	}

	for _, id := range ids {
		if userID == id {
			return errors.New("cannot delete self")
		}
		if uid, err := uuid.FromString(id); err != nil || uid == uuid.Nil {
			return fmt.Errorf("invalid user ID '%v'", id)
		}
	}

	for _, u := range usernames {
		if u == "" {
			return errors.New("username to delete must not be empty")
		}
		if username == u {
			return errors.New("cannot delete self")
		}
	}

	fetchIDs, err := fetchUserID(ctx, n.db, usernames)
	if err != nil {
		n.logger.Error("Could not fetch user IDs.", zap.Error(err), zap.Strings("usernames", usernames))
		return errors.New("error while trying to delete friends")
	}

	if len(fetchIDs)+len(ids) == 0 {
		return errors.New("no valid ID or username was provided")
	}

	allIDs := make([]string, 0, len(ids)+len(fetchIDs))
	allIDs = append(allIDs, ids...)
	allIDs = append(allIDs, fetchIDs...)

	err = DeleteFriends(ctx, n.logger, n.db, userUUID, allIDs)
	if err != nil {
		return err
	}

	return nil
}

func (n *RuntimeGoNakamaModule) SetEventFn(fn RuntimeEventCustomFunction) {
	n.Lock()
	n.eventFn = fn
+183 −1
Original line number Diff line number Diff line
@@ -239,6 +239,8 @@ func (n *runtimeJavascriptNakamaModule) mappings(r *goja.Runtime) map[string]fun
		"groupUsersList":                  n.groupUsersList(r),
		"userGroupsList":                  n.userGroupsList(r),
		"friendsList":                     n.friendsList(r),
		"friendsAdd":                      n.friendsAdd(r),
		"friendsDelete":                   n.friendsDelete(r),
		"groupUserJoin":                   n.groupUserJoin(r),
		"groupUserLeave":                  n.groupUserLeave(r),
		"groupUsersAdd":                   n.groupUsersAdd(r),
@@ -6457,7 +6459,7 @@ func (n *runtimeJavascriptNakamaModule) userGroupsList(r *goja.Runtime) func(goj

// @group friends
// @summary List all friends, invites, invited, and blocked which belong to a user.
// @param userId(type=string) The ID of the user who's friends, invites, invited, and blocked you want to list.
// @param userId(type=string) The ID of the user whose friends, invites, invited, and blocked you want to list.
// @param limit(type=number, optional=true, default=100) The number of friends to retrieve in this page of results. No more than 100 limit allowed per result.
// @param state(type=number, optional=true) The state of the friendship with the user. If unspecified this returns friends in all states for the user.
// @param cursor(type=string, optional=true) The cursor returned from a previous listing request. Used to obtain the next page of results.
@@ -6530,6 +6532,186 @@ func (n *runtimeJavascriptNakamaModule) friendsList(r *goja.Runtime) func(goja.F
	}
}

// @group friends
// @summary Add friends to a user.
// @param userId(type=string) The ID of the user to whom you want to add friends.
// @param username(type=string) The name of the user to whom you want to add friends.
// @param ids(type=[]string) Table array of IDs of the users you want to add as friends.
// @param usernames(type=[]string) Table array of usernames of the users you want to add as friends.
// @return error(error) An optional error value if an error occurred.
func (n *runtimeJavascriptNakamaModule) friendsAdd(r *goja.Runtime) func(goja.FunctionCall) goja.Value {
	return func(f goja.FunctionCall) goja.Value {
		userIDString := getJsString(r, f.Argument(0))
		userID, err := uuid.FromString(userIDString)
		if err != nil {
			panic(r.NewTypeError("expects user ID to be a valid identifier"))
		}

		username := getJsString(r, f.Argument(1))
		if username == "" {
			panic(r.NewTypeError("expects a username string"))
		}

		var userIDs []string
		if f.Argument(2) != goja.Undefined() && f.Argument(2) != goja.Null() {
			var ok bool
			userIdsIn, ok := f.Argument(2).Export().([]interface{})
			if !ok {
				panic(r.NewTypeError("Invalid argument - user ids must be an array."))
			}
			uIds := make([]string, 0, len(userIdsIn))
			for _, userID := range userIdsIn {
				id, ok := userID.(string)
				if !ok {
					panic(r.NewTypeError(fmt.Sprintf("invalid user id: %v - must be a string", userID)))
				} else if uid, err := uuid.FromString(id); err != nil || uid == uuid.Nil {
					panic(r.NewTypeError(fmt.Sprintf("invalid user id: %v", userID)))
				} else if userIDString == id {
					panic(r.NewTypeError("cannot add self as friend"))
				}
				uIds = append(uIds, id)
			}
			userIDs = uIds
		}

		var usernames []string
		if f.Argument(3) != goja.Undefined() && f.Argument(3) != goja.Null() {
			usernamesIn, ok := f.Argument(3).Export().([]interface{})
			if !ok {
				panic(r.NewTypeError("Invalid argument - usernames must be an array."))
			}
			unames := make([]string, 0, len(usernamesIn))
			for _, unameIn := range usernamesIn {
				uname, ok := unameIn.(string)
				if !ok {
					panic(r.NewTypeError("Invalid argument - username must be a string"))
				} else if uname == "" {
					panic(r.NewTypeError("username to add must not be empty"))
				} else if uname == username {
					panic(r.NewTypeError("cannot add self as friend"))
				}
				unames = append(unames, uname)
			}
			usernames = unames
		}

		if userIDs == nil && usernames == nil {
			return goja.Undefined()
		}

		fetchIDs, err := fetchUserID(context.Background(), n.db, usernames)
		if err != nil {
			n.logger.Error("Could not fetch user IDs.", zap.Error(err), zap.Strings("usernames", usernames))
			panic(r.NewTypeError("error while trying to add friends"))
		}

		if len(fetchIDs)+len(userIDs) == 0 {
			panic(r.NewTypeError("no valid ID or username was provided"))
		}

		allIDs := make([]string, 0, len(userIDs)+len(fetchIDs))
		allIDs = append(allIDs, userIDs...)
		allIDs = append(allIDs, fetchIDs...)

		err = AddFriends(context.Background(), n.logger, n.db, n.router, userID, username, allIDs)
		if err != nil {
			panic(r.NewTypeError(err.Error()))
		}

		return goja.Undefined()
	}
}

// @group friends
// @summary Delete friends from a user.
// @param userId(type=string) The ID of the user from whom you want to delete friends.
// @param username(type=string) The name of the user from whom you want to delete friends.
// @param ids(type=[]string) Table array of IDs of the users you want to delete as friends.
// @param usernames(type=[]string) Table array of usernames of the users you want to delete as friends.
// @return error(error) An optional error value if an error occurred.
func (n *runtimeJavascriptNakamaModule) friendsDelete(r *goja.Runtime) func(goja.FunctionCall) goja.Value {
	return func(f goja.FunctionCall) goja.Value {
		userIDString := getJsString(r, f.Argument(0))
		userID, err := uuid.FromString(userIDString)
		if err != nil {
			panic(r.NewTypeError("expects user ID to be a valid identifier"))
		}

		username := getJsString(r, f.Argument(1))
		if username == "" {
			panic(r.NewTypeError("expects a username string"))
		}

		var userIDs []string
		if f.Argument(2) != goja.Undefined() && f.Argument(2) != goja.Null() {
			var ok bool
			userIdsIn, ok := f.Argument(2).Export().([]interface{})
			if !ok {
				panic(r.NewTypeError("Invalid argument - user ids must be an array."))
			}
			uIds := make([]string, 0, len(userIdsIn))
			for _, userID := range userIdsIn {
				id, ok := userID.(string)
				if !ok {
					panic(r.NewTypeError(fmt.Sprintf("invalid user id: %v - must be a string", userID)))
				} else if uid, err := uuid.FromString(id); err != nil || uid == uuid.Nil {
					panic(r.NewTypeError(fmt.Sprintf("invalid user id: %v", userID)))
				} else if userIDString == id {
					panic(r.NewTypeError("cannot delete self"))
				}
				uIds = append(uIds, id)
			}
			userIDs = uIds
		}

		var usernames []string
		if f.Argument(3) != goja.Undefined() && f.Argument(3) != goja.Null() {
			usernamesIn, ok := f.Argument(3).Export().([]interface{})
			if !ok {
				panic(r.NewTypeError("Invalid argument - usernames must be an array."))
			}
			unames := make([]string, 0, len(usernamesIn))
			for _, unameIn := range usernamesIn {
				uname, ok := unameIn.(string)
				if !ok {
					panic(r.NewTypeError("Invalid argument - username must be a string"))
				} else if uname == "" {
					panic(r.NewTypeError("username to delete must not be empty"))
				} else if uname == username {
					panic(r.NewTypeError("cannot delete self"))
				}
				unames = append(unames, uname)
			}
			usernames = unames
		}

		if userIDs == nil && usernames == nil {
			return goja.Undefined()
		}

		fetchIDs, err := fetchUserID(context.Background(), n.db, usernames)
		if err != nil {
			n.logger.Error("Could not fetch user IDs.", zap.Error(err), zap.Strings("usernames", usernames))
			panic(r.NewTypeError("error while trying to delete friends"))
		}

		if len(fetchIDs)+len(userIDs) == 0 {
			panic(r.NewTypeError("no valid ID or username was provided"))
		}

		allIDs := make([]string, 0, len(userIDs)+len(fetchIDs))
		allIDs = append(allIDs, userIDs...)
		allIDs = append(allIDs, fetchIDs...)

		err = DeleteFriends(context.Background(), n.logger, n.db, userID, allIDs)
		if err != nil {
			panic(r.NewTypeError(err.Error()))
		}

		return goja.Undefined()
	}
}

// @group groups
// @summary Join a group for a particular user.
// @param groupId(type=string) The ID of the group to join.
+205 −1
Original line number Diff line number Diff line
@@ -266,6 +266,8 @@ func (n *RuntimeLuaNakamaModule) Loader(l *lua.LState) int {
		"groups_list":                        n.groupsList,
		"user_groups_list":                   n.userGroupsList,
		"friends_list":                       n.friendsList,
		"friends_add":                        n.friendsAdd,
		"friends_delete":                     n.friendsDelete,
		"file_read":                          n.fileRead,
		"channel_message_send":               n.channelMessageSend,
		"channel_message_update":             n.channelMessageUpdate,
@@ -8488,7 +8490,7 @@ func (n *RuntimeLuaNakamaModule) accountExportId(l *lua.LState) int {

// @group friends
// @summary List all friends, invites, invited, and blocked which belong to a user.
// @param userId(type=string) The ID of the user who's friends, invites, invited, and blocked you want to list.
// @param userId(type=string) The ID of the user whose friends, invites, invited, and blocked you want to list.
// @param limit(type=OptNumber, optional=true) The number of friends to retrieve in this page of results. No more than 100 limit allowed per result.
// @param state(type=OptNumber, optional=true) The state of the friendship with the user. If unspecified this returns friends in all states for the user.
// @param cursor(type=OptString, optional=true) The cursor returned from a previous listing request. Used to obtain the next page of results.
@@ -8553,6 +8555,208 @@ func (n *RuntimeLuaNakamaModule) friendsList(l *lua.LState) int {
	return 2
}

// @group friends
// @summary Add friends to a user.
// @param userId(type=string) The ID of the user to whom you want to add friends.
// @param username(type=string) The name of the user to whom you want to add friends.
// @param ids(type=table) The IDs of the users you want to add as friends.
// @param usernames(type=table) The usernames of the users you want to add as friends.
// @return error(error) An optional error value if an error occurred.
func (n *RuntimeLuaNakamaModule) friendsAdd(l *lua.LState) int {
	userID, err := uuid.FromString(l.CheckString(1))
	if err != nil {
		l.ArgError(1, "expects user ID to be a valid identifier")
		return 0
	}

	username := l.CheckString(2)
	if username == "" {
		l.ArgError(2, "expects username string")
		return 0
	}

	userIDsIn := l.OptTable(3, nil)
	var userIDs []string
	if userIDsIn != nil {
		userIDsTable, ok := RuntimeLuaConvertLuaValue(userIDsIn).([]interface{})
		if !ok {
			l.ArgError(3, "invalid user ids list")
			return 0
		}

		userIDStrings := make([]string, 0, len(userIDsTable))
		for _, id := range userIDsTable {
			if ids, ok := id.(string); !ok || ids == "" {
				l.ArgError(3, "each user id must be a string")
				return 0
			} else if uid, err := uuid.FromString(ids); err != nil || uid == uuid.Nil {
				l.ArgError(3, "invalid user ID "+ids)
				return 0
			} else if userID.String() == ids {
				l.ArgError(3, "cannot add self as friend")
				return 0
			} else {
				userIDStrings = append(userIDStrings, ids)
			}
		}
		userIDs = userIDStrings
	}

	usernamesIn := l.OptTable(4, nil)
	var usernames []string
	if usernamesIn != nil {
		usernamesIDsTable, ok := RuntimeLuaConvertLuaValue(usernamesIn).([]interface{})
		if !ok {
			l.ArgError(4, "invalid username list")
			return 0
		}

		usernameStrings := make([]string, 0, len(usernamesIDsTable))
		for _, name := range usernamesIDsTable {
			if names, ok := name.(string); !ok || names == "" {
				l.ArgError(4, "each username must be a non-empty string")
				return 0
			} else if username == names {
				l.ArgError(4, "cannot add self as friend")
				return 0
			} else {
				usernameStrings = append(usernameStrings, names)
			}
		}
		usernames = usernameStrings
	}

	if len(userIDs) == 0 && len(usernames) == 0 {
		return 0
	}

	fetchIDs, err := fetchUserID(l.Context(), n.db, usernames)
	if err != nil {
		n.logger.Error("Could not fetch user IDs.", zap.Error(err), zap.Strings("usernames", usernames))
		l.RaiseError("error while trying to add friends")
		return 0
	}

	if len(fetchIDs)+len(userIDs) == 0 {
		l.RaiseError("no valid ID or username was provided")
		return 0
	}

	allIDs := make([]string, 0, len(userIDs)+len(fetchIDs))
	allIDs = append(allIDs, userIDs...)
	allIDs = append(allIDs, fetchIDs...)

	err = AddFriends(l.Context(), n.logger, n.db, n.router, userID, username, allIDs)
	if err != nil {
		l.RaiseError(err.Error())
		return 0
	}

	return 0

}

// @group friends
// @summary Delete friends to a user.
// @param userId(type=string) The ID of the user from whom you want to delete friends.
// @param username(type=string) The name of the user from whom you want to delete friends.
// @param ids(type=table) The IDs of the users you want to delete as friends.
// @param usernames(type=table) The usernames of the users you want to delete as friends.
// @return error(error) An optional error value if an error occurred.
func (n *RuntimeLuaNakamaModule) friendsDelete(l *lua.LState) int {
	userID, err := uuid.FromString(l.CheckString(1))
	if err != nil {
		l.ArgError(1, "expects user ID to be a valid identifier")
		return 0
	}

	username := l.CheckString(2)
	if username == "" {
		l.ArgError(2, "expects username string")
		return 0
	}

	userIDsIn := l.OptTable(3, nil)
	var userIDs []string
	if userIDsIn != nil {
		userIDsTable, ok := RuntimeLuaConvertLuaValue(userIDsIn).([]interface{})
		if !ok {
			l.ArgError(3, "invalid user ids list")
			return 0
		}

		userIDStrings := make([]string, 0, len(userIDsTable))
		for _, id := range userIDsTable {
			if ids, ok := id.(string); !ok || ids == "" {
				l.ArgError(3, "each user id must be a string")
				return 0
			} else if uid, err := uuid.FromString(ids); err != nil || uid == uuid.Nil {
				l.ArgError(3, "invalid user ID "+ids)
				return 0
			} else if userID.String() == ids {
				l.ArgError(3, "cannot delete self")
				return 0
			} else {
				userIDStrings = append(userIDStrings, ids)
			}
		}
		userIDs = userIDStrings
	}

	usernamesIn := l.OptTable(4, nil)
	var usernames []string
	if usernamesIn != nil {
		usernamesIDsTable, ok := RuntimeLuaConvertLuaValue(usernamesIn).([]interface{})
		if !ok {
			l.ArgError(4, "invalid username list")
			return 0
		}

		usernameStrings := make([]string, 0, len(usernamesIDsTable))
		for _, name := range usernamesIDsTable {
			if names, ok := name.(string); !ok || names == "" {
				l.ArgError(4, "each username must be a non-empty string")
				return 0
			} else if username == names {
				l.ArgError(4, "cannot delete self")
				return 0
			} else {
				usernameStrings = append(usernameStrings, names)
			}
		}
		usernames = usernameStrings
	}

	if len(userIDs) == 0 && len(usernames) == 0 {
		return 0
	}

	fetchIDs, err := fetchUserID(l.Context(), n.db, usernames)
	if err != nil {
		n.logger.Error("Could not fetch user IDs.", zap.Error(err), zap.Strings("usernames", usernames))
		l.RaiseError("error while trying to delete friends")
		return 0
	}

	if len(fetchIDs)+len(userIDs) == 0 {
		l.RaiseError("no valid ID or username was provided")
		return 0
	}

	allIDs := make([]string, 0, len(userIDs)+len(fetchIDs))
	allIDs = append(allIDs, userIDs...)
	allIDs = append(allIDs, fetchIDs...)

	err = DeleteFriends(l.Context(), n.logger, n.db, userID, allIDs)
	if err != nil {
		l.RaiseError(err.Error())
		return 0
	}

	return 0

}

// @group utils
// @summary Read file from user device.
// @param relPath(type=string) Relative path to the file to be read.