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

Add "moderation." channel prefix for delivering Moderation events to users #121

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
*.podspec
**/*.podspec
node_modules
js-chat/dist

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dokka = "1.9.20"
kotlinx_datetime = "0.6.0"
kotlinx_coroutines = "1.8.1"
kotlinx_serialization = "1.7.1"
pubnub = "10.1.0"
pubnub = "10.2.0"
marcin-cebo marked this conversation as resolved.
Show resolved Hide resolved

[libraries]
pubnub-kotlin-api = { module = "com.pubnub:pubnub-kotlin-api", version.ref = "pubnub" }
Expand Down
3 changes: 3 additions & 0 deletions pubnub-chat-api/api/pubnub-chat-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,9 @@ public final class com/pubnub/chat/types/EmitEventMethod : java/lang/Enum {
public abstract class com/pubnub/chat/types/EventContent {
public static final field Companion Lcom/pubnub/chat/types/EventContent$Companion;
public synthetic fun <init> (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCustomMessageType ()Ljava/lang/String;
public static final synthetic fun write$Self (Lcom/pubnub/chat/types/EventContent;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ class File(
/**
* Represents the content of various types of events emitted during chat operations.
* This is a sealed class with different subclasses representing specific types of events.
*
* @property customMessageType the `customMessageType` that will be passed as a parameter into
* [com.pubnub.api.PubNub.publish] or [com.pubnub.api.PubNub.signal] when sending this event.
*/
@Serializable
sealed class EventContent {
sealed class EventContent(
@Transient val customMessageType: String? = null
wkal-pubnub marked this conversation as resolved.
Show resolved Hide resolved
) {
/**
* Represents a typing event that indicates whether a user is typing.
*
Expand Down Expand Up @@ -120,7 +125,7 @@ sealed class EventContent {
@Serializable
@SerialName("moderation")
class Moderation(val channelId: String, val restriction: RestrictionType, val reason: String? = null) :
EventContent()
EventContent(CUSTOM_MESSAGE_TYPE_MODERATED)

/**
* Represents a text message event, containing the message text and any associated files.
marcin-cebo marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -189,3 +194,5 @@ enum class EmitEventMethod {
*/
PUBLISH
}

private const val CUSTOM_MESSAGE_TYPE_MODERATED = "moderated"
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,17 @@ class ChatImpl(
): PNFuture<PNPublishResult> {
val emitMethod = payload::class.getEmitMethod() ?: (payload as? EventContent.Custom)?.method
return if (emitMethod == EmitEventMethod.SIGNAL) {
pubNub.signal(channel = channelId, message = payload.encodeForSending(mergePayloadWith))
pubNub.signal(
channel = channelId,
message = payload.encodeForSending(mergePayloadWith),
customMessageType = payload.customMessageType
)
} else {
pubNub.publish(channel = channelId, message = payload.encodeForSending(mergePayloadWith))
pubNub.publish(
channel = channelId,
message = payload.encodeForSending(mergePayloadWith),
customMessageType = payload.customMessageType
)
}
}

Expand Down Expand Up @@ -699,7 +707,7 @@ class ChatImpl(
pubNub.removeChannelMembers(channel = channel, uuids = listOf(userId))
.alsoAsync { _ ->
emitEvent(
channelId = userId,
channelId = INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId,
payload = EventContent.Moderation(
channelId = channel,
restriction = RestrictionType.LIFT,
Expand All @@ -710,16 +718,16 @@ class ChatImpl(
} else {
val custom = createCustomObject(
mapOf(
"ban" to restriction.ban,
"mute" to restriction.mute,
"reason" to restriction.reason
RESTRICTION_BAN to restriction.ban,
RESTRICTION_MUTE to restriction.mute,
RESTRICTION_REASON to restriction.reason
)
)
val uuids = listOf(PNMember.Partial(uuidId = userId, custom = custom, null))
pubNub.setChannelMembers(channel = channel, uuids = uuids)
.alsoAsync { _ ->
emitEvent(
channelId = userId,
channelId = INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId,
payload = EventContent.Moderation(
channelId = channel,
restriction = if (restriction.ban) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal const val ORIGINAL_PUBLISHER = "originalPublisher"
internal const val ORIGINAL_CHANNEL_ID = "originalChannelId"
internal const val HTTP_ERROR_404 = 404
internal const val INTERNAL_MODERATION_PREFIX = "PUBNUB_INTERNAL_MODERATION_"
internal const val INTERNAL_USER_MODERATION_CHANNEL_PREFIX = "moderation."
internal const val PUBNUB_INTERNAL_AUTOMODERATED = "PUBNUB_INTERNAL_AUTOMODERATED"
internal const val INTERNAL_MODERATOR_DATA_ID = "PUBNUB_INTERNAL_MODERATOR"
internal const val INTERNAL_MODERATOR_DATA_TYPE = "internal"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() {
@Test
fun getPinnedMessage() = runTest {
val timetoken = channel01.sendText("Text text text").await()
delayInMillis(250)
val message = channel01.getMessage(timetoken.timetoken).await()!!

val updatedChannel = channel01.pinMessage(message).await()
Expand Down Expand Up @@ -638,6 +639,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() {
val messageText = "message1"
val pnPublishResult = channel01.sendText(text = messageText).await()
val timetoken = pnPublishResult.timetoken
delayInMillis(250)
val message = channel01.getMessage(timetoken).await()!!
val assertionErrorInCallback = CompletableDeferred<AssertionError?>()

Expand Down Expand Up @@ -704,7 +706,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() {
val channel01withChat = channel01
val join01 = channel01withChat.join { }.await()
val join02 = channel01Chat02.join { }.await()
delayInMillis(1000)
delayInMillis(1500)
val whoIsPresent01: Collection<String> = channel01withChat.whoIsPresent().await()
val whoIsPresent02: Collection<String> = channel01Chat02.whoIsPresent().await()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.pubnub.chat.User
import com.pubnub.chat.config.ChatConfiguration
import com.pubnub.chat.config.PushNotificationsConfig
import com.pubnub.chat.internal.ChatImpl
import com.pubnub.chat.internal.INTERNAL_USER_MODERATION_CHANNEL_PREFIX
import com.pubnub.chat.internal.UserImpl
import com.pubnub.chat.internal.error.PubNubErrorMessage
import com.pubnub.chat.internal.utils.cyrb53a
Expand Down Expand Up @@ -619,10 +620,10 @@ class ChatIntegrationTest : BaseChatIntegrationTest() {
val restrictionUnban = Restriction(userId = userId, channelId = channelId, ban = false, mute = false, reason = "ok")
pubnub.test(backgroundScope, checkAllEvents = false) {
var removeListenerAndUnsubscribe: AutoCloseable? = null
pubnub.awaitSubscribe(channels = listOf(userId)) {
pubnub.awaitSubscribe(channels = listOf(INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId)) {
removeListenerAndUnsubscribe = chat.listenForEvents(
type = EventContent.Moderation::class,
channelId = userId
channelId = INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId
) { event: Event<EventContent.Moderation> ->
val restrictionType: RestrictionType = event.payload.restriction
if (restrictionType == RestrictionType.BAN) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,20 @@ class MessageIntegrationTest : BaseChatIntegrationTest() {
val reactionValue = "wow"
val pnPublishResult = channel01.sendText(text = messageText).await()
val publishTimetoken = pnPublishResult.timetoken
delayInMillis(250)
val message: Message = channel01.getMessage(publishTimetoken).await()!!
val threadChannel: ThreadChannel = message.createThread().await()
// we need to call sendText because addMessageAction is called in sendText that stores details about thread
threadChannel.sendText("message in thread_${randomString()}").await()

delayInMillis(250)
val history: HistoryResponse<ThreadMessage> = threadChannel.getHistory().await()

val messageWithThread = channel01.getMessage(publishTimetoken).await()
val messageWithReaction = messageWithThread!!.toggleReaction(reactionValue).await()
val deletedMessage: Message = messageWithReaction.delete(soft = true).await()!!

val restoredMessage: Message = deletedMessage.restore().await()
delayInMillis(250)
val restoredThread: ThreadChannel = restoredMessage.getThread().await()
val historyAfterRestore: HistoryResponse<ThreadMessage> = restoredThread.getHistory().await()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ import com.pubnub.chat.config.ChatConfiguration
import com.pubnub.chat.config.PushNotificationsConfig
import com.pubnub.chat.internal.ChatImpl
import com.pubnub.chat.internal.ChatInternal
import com.pubnub.chat.internal.INTERNAL_USER_MODERATION_CHANNEL_PREFIX
import com.pubnub.chat.internal.message.MessageImpl
import com.pubnub.chat.internal.timer.TimerManager
import com.pubnub.chat.message.GetUnreadMessagesCounts
import com.pubnub.chat.restrictions.Restriction
import com.pubnub.chat.restrictions.RestrictionType
import com.pubnub.chat.types.ChannelType
import com.pubnub.chat.types.EventContent
import com.pubnub.chat.types.GetEventsHistoryResult
Expand Down Expand Up @@ -650,6 +652,31 @@ class ChatTest : BaseTest() {
assertEquals(forwardedChannelId, actualForwardedChannelId)
}

@Test
fun shouldPassCustomMessageTypeOfEventContentToPublish() {
every { pubnub.publish(any(), any(), any(), any(), any(), any(), any(), any()) } returns publishEndpoint
every { publishEndpoint.async(any()) } calls { (callback1: Consumer<Result<PNPublishResult>>) ->
callback1.accept(Result.success(PNPublishResult(timetoken)))
}
val payload = EventContent.Moderation("a", RestrictionType.BAN, null)

objectUnderTest.emitEvent(
channelId = channelId,
payload = payload,
).async { result ->
assertTrue(result.isSuccess)
assertEquals(timetoken, result.getOrNull()?.timetoken)
}

verify {
pubnub.publish(
channel = channelId,
message = mapOf("type" to "moderation", "channelId" to "a", "restriction" to "banned", "reason" to null),
customMessageType = payload.customMessageType
)
}
}

@Test
fun shouldCalSignalWhenEmitEventWithMethodSignal() {
every { pubnub.signal(any(), any()) } returns signalEndpoint
Expand Down Expand Up @@ -1336,7 +1363,7 @@ class ChatTest : BaseTest() {
every { manageChannelMembersEndpoint.async(any()) } calls { (callback: Consumer<Result<PNMemberArrayResult>>) ->
callback.accept(Result.success(pnMemberArrayResult))
}
every { pubnub.publish(channel = capture(userIdSlot), message = any()) } returns publishEndpoint
every { pubnub.publish(channel = capture(userIdSlot), message = any(), customMessageType = "moderated") } returns publishEndpoint
every { publishEndpoint.async(any()) } calls { (callback1: Consumer<Result<PNPublishResult>>) ->
callback1.accept(Result.success(PNPublishResult(timetoken)))
}
Expand All @@ -1350,7 +1377,7 @@ class ChatTest : BaseTest() {
val actualModerationEventChannelId = userIdSlot.get()
assertEquals(restrictedUserId, actualRestrictedUserId)
assertEquals("PUBNUB_INTERNAL_MODERATION_$restrictedChannelId", actualRestrictedChannelId)
assertEquals(restrictedUserId, actualModerationEventChannelId)
assertEquals(INTERNAL_USER_MODERATION_CHANNEL_PREFIX + restrictedUserId, actualModerationEventChannelId)
}

@Test
Expand Down Expand Up @@ -1390,7 +1417,8 @@ class ChatTest : BaseTest() {
every {
pubnub.publish(
channel = capture(userIdSlot),
message = capture(encodedMessageSlot)
message = capture(encodedMessageSlot),
customMessageType = any()
)
} returns publishEndpoint
every { publishEndpoint.async(any()) } calls { (callback1: Consumer<Result<PNPublishResult>>) ->
Expand All @@ -1409,7 +1437,7 @@ class ChatTest : BaseTest() {
assertEquals(reason, actualRestriction.get("reason"))
assertEquals("banned", actualEncodedMessageSlot["restriction"])
assertEquals("PUBNUB_INTERNAL_MODERATION_$restrictedChannelId", actualRestrictedChannelId)
assertEquals(restrictedUserId, actualModerationEventChannelId)
assertEquals(INTERNAL_USER_MODERATION_CHANNEL_PREFIX + restrictedUserId, actualModerationEventChannelId)
}

private fun createMessage(chId: String = channelId, uId: String = userId): Message {
Expand Down
2 changes: 1 addition & 1 deletion pubnub-kotlin