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 logging #13

Merged
merged 3 commits into from
Apr 7, 2024
Merged
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
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**📦 A lightweight, cross-platform [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html) library for interacting with Discord Rich Presence.**

## 💎 Features
- Cross-platform compatibility (Windows, Linux, macOS*)
- Cross-platform compatibility (Windows, Linux, macOS)
- Fast and user-friendly, featuring DSL support
- Provides both JVM and Native implementations
- Respects the ratelimit of one update per 15 seconds. The library will always send the newest presence update once the client is free to do so
Expand All @@ -16,11 +16,13 @@

```gradle
dependencies {
implementation("io.github.vyfor:kpresence:0.5.0")
implementation("io.github.vyfor:kpresence:0.5.1")
}
```

## ✨ Examples

### Initial connection and presence updates
```kt
val client = RichClient(CLIENT_ID)

Expand Down Expand Up @@ -61,5 +63,8 @@ val activity = activity {
client.update(activity)
```

## Notes
> * To use on macOS, clone the repository and build it manually.
### Enable logging
```kt
val client = RichClient(CLIENT_ID)
client.logger = ILogger.default()
```
13 changes: 12 additions & 1 deletion src/commonMain/kotlin/io/github/vyfor/kpresence/RichClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package io.github.vyfor.kpresence

import io.github.vyfor.kpresence.exception.InvalidClientIdException
import io.github.vyfor.kpresence.ipc.*
import io.github.vyfor.kpresence.logger.ILogger
import io.github.vyfor.kpresence.rpc.Activity
import io.github.vyfor.kpresence.rpc.Packet
import io.github.vyfor.kpresence.rpc.PacketArgs
Expand All @@ -26,6 +27,7 @@ class RichClient(var clientId: Long) {
private var lastActivity: Activity? = null
private var lastUpdated = 0L
private var updateTimer: Job? = null
var logger: ILogger? = null

/**
* Establishes a connection to Discord.
Expand All @@ -34,12 +36,14 @@ class RichClient(var clientId: Long) {
*/
fun connect(callback: (RichClient.() -> Unit)? = null): RichClient {
if (connectionState != ConnectionState.DISCONNECTED) {
logger?.warn("Already connected to Discord. Skipping")
callback?.invoke(this)
return this
}

connection.open()
connectionState = ConnectionState.CONNECTED
logger?.info("Successfully connected to Discord")
handshake()

callback?.invoke(this)
Expand Down Expand Up @@ -78,6 +82,7 @@ class RichClient(var clientId: Long) {
lastUpdated = currentTime
} else if (updateTimer?.isActive != true) {
updateTimer = clientScope.launch {
logger?.info("Scheduled a presence update in ${UPDATE_INTERVAL - timeSinceLastUpdate}ms")
delay(UPDATE_INTERVAL - timeSinceLastUpdate)
sendActivityUpdate()
lastUpdated = epochMillis()
Expand All @@ -101,12 +106,16 @@ class RichClient(var clientId: Long) {
* @return The current Client instance for chaining.
*/
fun shutdown(): RichClient {
if (connectionState == ConnectionState.DISCONNECTED) return this
if (connectionState == ConnectionState.DISCONNECTED) {
logger?.warn("Already disconnected from Discord. Skipping")
return this
}
// TODO: Send valid payload
connection.write(2, "{\"v\": 1,\"client_id\":\"$clientId\"}")
connection.read()
connection.close()
connectionState = ConnectionState.DISCONNECTED
logger?.info("Successfully disconnected from Discord")
updateTimer?.cancel()
updateTimer = null
lastActivity = null
Expand All @@ -117,6 +126,7 @@ class RichClient(var clientId: Long) {
private fun sendActivityUpdate() {
if (connectionState != ConnectionState.SENT_HANDSHAKE) return
val packet = Json.encodeToString(Packet("SET_ACTIVITY", PacketArgs(getProcessId(), lastActivity), "-"))
logger?.info("Sending presence update with payload: $packet")
connection.write(1, packet)
connection.read()
}
Expand All @@ -127,6 +137,7 @@ class RichClient(var clientId: Long) {
throw InvalidClientIdException("'$clientId' is not a valid client ID")
}
connectionState = ConnectionState.SENT_HANDSHAKE
logger?.info("Performed initial handshake")
}

companion object {
Expand Down
36 changes: 36 additions & 0 deletions src/commonMain/kotlin/io/github/vyfor/kpresence/logger/ILogger.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@file:Suppress("unused")

package io.github.vyfor.kpresence.logger

import io.github.vyfor.kpresence.utils.epochMillis
import io.github.vyfor.kpresence.utils.formatTime

interface ILogger {
fun info(message: String)
fun warn(message: String)
fun error(message: String)
fun error(message: String, throwable: Throwable)

companion object {
/**
* Returns a default implementation of the ILogger interface.
*/
fun default(): ILogger = object : ILogger {
override fun info(message: String) {
println("${formatTime(epochMillis())} \u001B[34m[KPresence - INFO]\u001B[0m $message")
}

override fun warn(message: String) {
println("${formatTime(epochMillis())} \u001B[33m[KPresence - WARN]\u001B[0m $message")
}

override fun error(message: String) {
println("${formatTime(epochMillis())} \u001B[31m[KPresence - ERROR]\u001B[0m $message")
}

override fun error(message: String, throwable: Throwable) {
println("${formatTime(epochMillis())} \u001B[31m[KPresence - ERROR]\u001B[0m $message\n${throwable.stackTraceToString()}")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ internal fun ByteArray.byteArrayToInt(): Int {
(get(3).toInt() and 0xFF)
}

fun formatTime(epochMillis: Long): String {
val seconds = (epochMillis / 1000) % 60
val minutes = (epochMillis / (1000 * 60)) % 60
val hours = (epochMillis / (1000 * 60 * 60)) % 24

return "${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}"
}

expect fun epochMillis(): Long

expect fun getProcessId(): Int
2 changes: 2 additions & 0 deletions src/commonTest/kotlin/io/github/vyfor/kpresence/ClientTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.vyfor.kpresence

import io.github.vyfor.kpresence.logger.ILogger
import io.github.vyfor.kpresence.rpc.*
import io.github.vyfor.kpresence.utils.epochMillis
import kotlin.test.Test
Expand All @@ -8,6 +9,7 @@ class ClientTest {
@Test
fun testClient() {
val client = RichClient(1216296290451984424)
client.logger = ILogger.default()

client.connect {
println("Connected")
Expand Down
2 changes: 2 additions & 0 deletions src/jvmTest/kotlin/io/github/vyfor/kpresence/ClientTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.vyfor.kpresence

import io.github.vyfor.kpresence.logger.ILogger
import io.github.vyfor.kpresence.rpc.*
import kotlin.random.Random
import kotlin.test.Test
Expand All @@ -8,6 +9,7 @@ class JVMClientTest {
@Test
fun testClient() {
val client = RichClient(1216296290451984424)
client.logger = ILogger.default()

client.connect {
println("Connected")
Expand Down
Loading