opencode-discord 1.8.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.
Files changed (2) hide show
  1. package/index.ts +78 -47
  2. 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.now()
23
+ let startTimestamp = new Date()
22
24
  let lastEditedFile: string | undefined
23
25
  let activity: Activity = "idle"
24
26
  let sessionCount = 0
25
-
26
- async function log(level: LogLevel, message: string, extra?: Record<string, unknown>) {
27
- try {
28
- await sdk.app.log({
29
- body: {
30
- service: "discord-status",
31
- level,
32
- message,
33
- extra,
34
- },
35
- })
36
- } catch { }
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
- await log("info", "Plugin initializing", { clientId: CLIENT_ID, project: currentProject })
43
+ log("info", "Plugin initializing", { clientId: CLIENT_ID, project: currentProject })
40
44
 
41
- async function updatePresence() {
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
- try {
78
- await rpc.user?.setActivity({
79
- details,
80
- state,
81
- startTimestamp,
82
- largeImageKey: "opencode",
83
- largeImageText: `OpenCode · ${sessionCount} session${sessionCount !== 1 ? "s" : ""}`,
84
- smallImageKey,
85
- smallImageText,
86
- })
87
- await log("debug", "Presence updated", { details, state })
88
- } catch (e) {
89
- await log("error", "Failed to set presence", { error: String(e) })
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", async () => {
118
+ rpc.on("ready", () => {
94
119
  connected = true
95
- startTimestamp = Date.now()
96
- await log("info", "Discord RPC connected")
97
- await updatePresence()
120
+ startTimestamp = new Date()
121
+ log("info", "Discord RPC connected")
122
+ updatePresence()
98
123
  })
99
124
 
100
- rpc.on("error", async (err: any) => {
101
- await log("error", "Discord RPC error", { error: String(err) })
125
+ rpc.on("disconnected", () => {
126
+ connected = false
127
+ log("warn", "Discord RPC disconnected")
128
+ scheduleReconnect()
102
129
  })
103
130
 
104
- rpc.on("disconnected", async () => {
105
- connected = false
106
- await log("warn", "Discord RPC disconnected")
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
- await log("info", "Logging in to Discord RPC...")
139
+ log("info", "Logging in to Discord RPC...")
111
140
  await rpc.login()
112
- await log("info", "Discord RPC login successful")
141
+ reconnectAttempts = 0
142
+ log("info", "Discord RPC login successful")
113
143
  } catch (e) {
114
144
  connected = false
115
- await log("error", "Discord RPC login failed", { error: String(e) })
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
- const eventType = event.type as string
121
-
122
- switch (eventType) {
151
+ switch (event.type) {
123
152
  case "session.created":
124
153
  sessionActive = true
125
154
  sessionCount += 1
126
- startTimestamp = Date.now()
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
- break
170
+ if (connected) {
171
+ rpc.user?.clearActivity().catch(() => {})
172
+ }
173
+ return
143
174
  case "session.compacted":
144
175
  activity = "thinking"
145
176
  break
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-discord",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Discord Rich Presence plugin for OpenCode — shows your AI coding activity in Discord",
5
5
  "type": "module",
6
6
  "main": "index.ts",