opencode-discord 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +81 -50
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { Plugin } from "@opencode-ai/plugin"
|
|
|
2
2
|
import { Client } from "@xhayper/discord-rpc"
|
|
3
3
|
|
|
4
4
|
const CLIENT_ID = process.env.DISCORD_CLIENT_ID ?? "1486144419940929676"
|
|
5
|
+
const RECONNECT_DELAY = 5_000
|
|
6
|
+
const MAX_RECONNECT_ATTEMPTS = 5
|
|
5
7
|
|
|
6
8
|
type Activity = "idle" | "thinking" | "editing" | "running" | "reading"
|
|
7
9
|
type LogLevel = "debug" | "info" | "error" | "warn"
|
|
@@ -18,28 +20,31 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
18
20
|
let connected = false
|
|
19
21
|
let sessionActive = false
|
|
20
22
|
const currentProject = basename(directory) ?? "Unknown Project"
|
|
21
|
-
let startTimestamp = Date
|
|
23
|
+
let startTimestamp = new Date()
|
|
22
24
|
let lastEditedFile: string | undefined
|
|
23
25
|
let activity: Activity = "idle"
|
|
24
26
|
let sessionCount = 0
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
let reconnectAttempts = 0
|
|
28
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | undefined
|
|
29
|
+
let pendingPresence = false
|
|
30
|
+
let destroyed = false
|
|
31
|
+
|
|
32
|
+
function log(level: LogLevel, message: string, extra?: Record<string, unknown>) {
|
|
33
|
+
sdk.app.log({
|
|
34
|
+
body: {
|
|
35
|
+
service: "discord-status",
|
|
36
|
+
level,
|
|
37
|
+
message,
|
|
38
|
+
extra,
|
|
39
|
+
},
|
|
40
|
+
}).catch(() => {})
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
log("info", "Plugin initializing", { clientId: CLIENT_ID, project: currentProject })
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
if (!connected) return
|
|
45
|
+
function updatePresence() {
|
|
46
|
+
if (!connected || pendingPresence) return
|
|
47
|
+
pendingPresence = true
|
|
43
48
|
|
|
44
49
|
let details: string
|
|
45
50
|
let state: string
|
|
@@ -74,61 +79,84 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
74
79
|
smallImageText = activity === "thinking" ? "Generating" : "Active"
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
rpc.user?.setActivity({
|
|
83
|
+
details,
|
|
84
|
+
state,
|
|
85
|
+
startTimestamp,
|
|
86
|
+
largeImageKey: "opencode",
|
|
87
|
+
largeImageText: `OpenCode · ${sessionCount} session${sessionCount !== 1 ? "s" : ""}`,
|
|
88
|
+
smallImageKey,
|
|
89
|
+
smallImageText,
|
|
90
|
+
})
|
|
91
|
+
.then(() => log("debug", "Presence updated", { details, state }))
|
|
92
|
+
.catch((e) => log("error", "Failed to set presence", { error: String(e) }))
|
|
93
|
+
.finally(() => { pendingPresence = false })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function scheduleReconnect() {
|
|
97
|
+
if (destroyed || reconnectTimer) return
|
|
98
|
+
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
99
|
+
log("error", "Max reconnect attempts reached, giving up")
|
|
100
|
+
return
|
|
90
101
|
}
|
|
102
|
+
reconnectAttempts++
|
|
103
|
+
log("info", "Scheduling reconnect", { attempt: reconnectAttempts })
|
|
104
|
+
reconnectTimer = setTimeout(() => {
|
|
105
|
+
reconnectTimer = undefined
|
|
106
|
+
rpc.login()
|
|
107
|
+
.then(() => {
|
|
108
|
+
log("info", "Reconnected to Discord RPC")
|
|
109
|
+
reconnectAttempts = 0
|
|
110
|
+
})
|
|
111
|
+
.catch((e) => {
|
|
112
|
+
log("error", "Reconnect failed", { error: String(e) })
|
|
113
|
+
scheduleReconnect()
|
|
114
|
+
})
|
|
115
|
+
}, RECONNECT_DELAY)
|
|
91
116
|
}
|
|
92
117
|
|
|
93
|
-
rpc.on("ready",
|
|
118
|
+
rpc.on("ready", () => {
|
|
94
119
|
connected = true
|
|
95
|
-
startTimestamp = Date
|
|
96
|
-
|
|
97
|
-
|
|
120
|
+
startTimestamp = new Date()
|
|
121
|
+
log("info", "Discord RPC connected")
|
|
122
|
+
updatePresence()
|
|
98
123
|
})
|
|
99
124
|
|
|
100
|
-
rpc.on("
|
|
101
|
-
|
|
125
|
+
rpc.on("disconnected", () => {
|
|
126
|
+
connected = false
|
|
127
|
+
log("warn", "Discord RPC disconnected")
|
|
128
|
+
scheduleReconnect()
|
|
102
129
|
})
|
|
103
130
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
process.on("exit", () => {
|
|
132
|
+
destroyed = true
|
|
133
|
+
if (reconnectTimer) clearTimeout(reconnectTimer)
|
|
134
|
+
if (connected) rpc.user?.clearActivity().catch(() => {})
|
|
135
|
+
rpc.destroy().catch(() => {})
|
|
107
136
|
})
|
|
108
137
|
|
|
109
138
|
try {
|
|
110
|
-
|
|
139
|
+
log("info", "Logging in to Discord RPC...")
|
|
111
140
|
await rpc.login()
|
|
112
|
-
|
|
141
|
+
reconnectAttempts = 0
|
|
142
|
+
log("info", "Discord RPC login successful")
|
|
113
143
|
} catch (e) {
|
|
114
144
|
connected = false
|
|
115
|
-
|
|
145
|
+
log("error", "Discord RPC login failed", { error: String(e) })
|
|
146
|
+
scheduleReconnect()
|
|
116
147
|
}
|
|
117
148
|
|
|
118
149
|
return {
|
|
119
150
|
event: async ({ event }) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
switch (eventType) {
|
|
151
|
+
switch (event.type) {
|
|
123
152
|
case "session.created":
|
|
124
153
|
sessionActive = true
|
|
125
154
|
sessionCount += 1
|
|
126
|
-
startTimestamp = Date
|
|
155
|
+
startTimestamp = new Date()
|
|
127
156
|
lastEditedFile = undefined
|
|
128
157
|
activity = "thinking"
|
|
129
158
|
break
|
|
130
159
|
case "session.idle":
|
|
131
|
-
sessionActive = true
|
|
132
160
|
activity = "idle"
|
|
133
161
|
break
|
|
134
162
|
case "session.error":
|
|
@@ -139,7 +167,10 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
139
167
|
sessionActive = false
|
|
140
168
|
lastEditedFile = undefined
|
|
141
169
|
activity = "idle"
|
|
142
|
-
|
|
170
|
+
if (connected) {
|
|
171
|
+
rpc.user?.clearActivity().catch(() => {})
|
|
172
|
+
}
|
|
173
|
+
return
|
|
143
174
|
case "session.compacted":
|
|
144
175
|
activity = "thinking"
|
|
145
176
|
break
|
|
@@ -151,7 +182,7 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
151
182
|
}
|
|
152
183
|
}
|
|
153
184
|
|
|
154
|
-
|
|
185
|
+
updatePresence()
|
|
155
186
|
},
|
|
156
187
|
|
|
157
188
|
"tool.execute.before": async (input, output) => {
|
|
@@ -167,7 +198,7 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
167
198
|
lastEditedFile = basename(args?.filePath) ?? lastEditedFile
|
|
168
199
|
}
|
|
169
200
|
|
|
170
|
-
|
|
201
|
+
updatePresence()
|
|
171
202
|
},
|
|
172
203
|
|
|
173
204
|
"tool.execute.after": async (input) => {
|
|
@@ -179,7 +210,7 @@ export const DiscordStatus: Plugin = async ({ client: sdk, directory }) => {
|
|
|
179
210
|
activity = sessionActive ? "thinking" : "idle"
|
|
180
211
|
}
|
|
181
212
|
|
|
182
|
-
|
|
213
|
+
updatePresence()
|
|
183
214
|
},
|
|
184
215
|
}
|
|
185
216
|
}
|