opencode-discord 1.0.0 → 1.1.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 +112 -81
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -3,105 +3,136 @@ import { Client } from "@theuntraceable/discord-rpc"
|
|
|
3
3
|
|
|
4
4
|
const CLIENT_ID = process.env.DISCORD_CLIENT_ID ?? "YOUR_DISCORD_APP_ID"
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
? `Working on ${currentProject}`
|
|
19
|
-
: "Working on a project"
|
|
20
|
-
: "No active session"
|
|
21
|
-
|
|
22
|
-
const state = sessionActive
|
|
23
|
-
? lastEditedFile
|
|
24
|
-
? `Editing ${lastEditedFile}`
|
|
25
|
-
: "AI session active"
|
|
26
|
-
: "Idle"
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
await client.user?.setActivity({
|
|
30
|
-
details,
|
|
31
|
-
state,
|
|
32
|
-
startTimestamp,
|
|
33
|
-
largeImageKey: "opencode",
|
|
34
|
-
largeImageText: "OpenCode",
|
|
35
|
-
smallImageKey: sessionActive ? "ai" : "idle",
|
|
36
|
-
smallImageText: sessionActive ? "AI Active" : "Idle",
|
|
37
|
-
})
|
|
38
|
-
} catch {
|
|
39
|
-
// Presence update failed
|
|
6
|
+
export const DiscordStatus: Plugin = async ({ client: sdk, project }) => {
|
|
7
|
+
async function log(level: string, message: string, extra?: any) {
|
|
8
|
+
try {
|
|
9
|
+
await sdk.app.log({
|
|
10
|
+
service: "opencode-discord",
|
|
11
|
+
level,
|
|
12
|
+
message,
|
|
13
|
+
extra,
|
|
14
|
+
})
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error(`[opencode-discord] ${message}`, extra ?? "", e)
|
|
17
|
+
}
|
|
40
18
|
}
|
|
41
|
-
}
|
|
42
19
|
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
await log("info", "Plugin initializing", { clientId: CLIENT_ID, project: project?.name })
|
|
21
|
+
|
|
22
|
+
const rpc = new Client({ clientId: CLIENT_ID })
|
|
23
|
+
let connected = false
|
|
24
|
+
let sessionActive = false
|
|
25
|
+
let currentProject = project?.name ?? ""
|
|
26
|
+
let startTimestamp = Date.now()
|
|
27
|
+
let lastEditedFile: string | undefined
|
|
28
|
+
|
|
29
|
+
async function updatePresence() {
|
|
30
|
+
await log("debug", "updatePresence called", { connected, sessionActive, lastEditedFile })
|
|
31
|
+
if (!connected) {
|
|
32
|
+
await log("warn", "Skipping presence update - not connected to Discord")
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const details = sessionActive
|
|
37
|
+
? currentProject
|
|
38
|
+
? `Working on ${currentProject}`
|
|
39
|
+
: "Working on a project"
|
|
40
|
+
: "No active session"
|
|
41
|
+
|
|
42
|
+
const state = sessionActive
|
|
43
|
+
? lastEditedFile
|
|
44
|
+
? `Editing ${lastEditedFile}`
|
|
45
|
+
: "AI session active"
|
|
46
|
+
: "Idle"
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await rpc.user?.setActivity({
|
|
50
|
+
details,
|
|
51
|
+
state,
|
|
52
|
+
startTimestamp,
|
|
53
|
+
largeImageKey: "opencode",
|
|
54
|
+
largeImageText: "OpenCode",
|
|
55
|
+
smallImageKey: sessionActive ? "ai" : "idle",
|
|
56
|
+
smallImageText: sessionActive ? "AI Active" : "Idle",
|
|
57
|
+
})
|
|
58
|
+
await log("info", "Presence updated", { details, state })
|
|
59
|
+
} catch (e) {
|
|
60
|
+
await log("error", "Failed to update presence", { error: String(e) })
|
|
61
|
+
}
|
|
62
|
+
}
|
|
45
63
|
|
|
46
|
-
|
|
64
|
+
rpc.on("ready", async () => {
|
|
47
65
|
connected = true
|
|
48
66
|
startTimestamp = Date.now()
|
|
49
|
-
|
|
67
|
+
await log("info", "Discord RPC connected", { user: rpc.user?.username })
|
|
68
|
+
await updatePresence()
|
|
50
69
|
})
|
|
51
70
|
|
|
52
|
-
|
|
71
|
+
rpc.on("error", async (err: any) => {
|
|
72
|
+
await log("error", "Discord RPC error", { error: String(err) })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
rpc.on("disconnected", async () => {
|
|
53
76
|
connected = false
|
|
77
|
+
await log("warn", "Discord RPC disconnected")
|
|
54
78
|
})
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
80
|
+
try {
|
|
81
|
+
await log("info", "Attempting Discord RPC login...")
|
|
82
|
+
await rpc.login()
|
|
83
|
+
await log("info", "Discord RPC login successful")
|
|
84
|
+
} catch (e) {
|
|
85
|
+
connected = false
|
|
86
|
+
await log("error", "Discord RPC login failed", { error: String(e) })
|
|
87
|
+
}
|
|
63
88
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
return {
|
|
90
|
+
event: async ({ event }) => {
|
|
91
|
+
await log("debug", `Event received: ${event.type}`, { event })
|
|
92
|
+
|
|
93
|
+
switch (event.type) {
|
|
94
|
+
case "session.created":
|
|
95
|
+
sessionActive = true
|
|
96
|
+
startTimestamp = Date.now()
|
|
97
|
+
lastEditedFile = undefined
|
|
98
|
+
await log("info", "Session created")
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
case "session.idle":
|
|
102
|
+
case "session.error":
|
|
103
|
+
sessionActive = false
|
|
104
|
+
lastEditedFile = undefined
|
|
105
|
+
await log("info", `Session ${event.type}`)
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
case "session.deleted":
|
|
109
|
+
sessionActive = false
|
|
110
|
+
lastEditedFile = undefined
|
|
111
|
+
await log("info", "Session deleted")
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
case "file.edited":
|
|
115
|
+
lastEditedFile =
|
|
116
|
+
(event as any).file?.split("/").pop() ??
|
|
117
|
+
(event as any).file
|
|
118
|
+
await log("info", "File edited", { file: lastEditedFile })
|
|
119
|
+
break
|
|
68
120
|
}
|
|
69
|
-
await updatePresence()
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
"session.idle": async () => {
|
|
73
|
-
sessionActive = false
|
|
74
|
-
lastEditedFile = undefined
|
|
75
|
-
await updatePresence()
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
"session.error": async () => {
|
|
79
|
-
sessionActive = false
|
|
80
|
-
await updatePresence()
|
|
81
|
-
},
|
|
82
121
|
|
|
83
|
-
"session.deleted": async () => {
|
|
84
|
-
sessionActive = false
|
|
85
|
-
lastEditedFile = undefined
|
|
86
122
|
await updatePresence()
|
|
87
123
|
},
|
|
88
124
|
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
lastEditedFile = event.data.file.split("/").pop() ?? event.data.file
|
|
92
|
-
}
|
|
93
|
-
await updatePresence()
|
|
94
|
-
},
|
|
125
|
+
"tool.execute.after": async (input) => {
|
|
126
|
+
await log("debug", `Tool executed: ${input.tool}`, { args: input.args })
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
if (input.tool === "edit" || input.tool === "write") {
|
|
129
|
+
const path = input.args?.filePath as string | undefined
|
|
130
|
+
if (path) {
|
|
131
|
+
lastEditedFile = path.split("/").pop() ?? path
|
|
132
|
+
await log("info", "File tool detected", { file: lastEditedFile })
|
|
133
|
+
}
|
|
134
|
+
await updatePresence()
|
|
99
135
|
}
|
|
100
|
-
await updatePresence()
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
"session.diff": async () => {
|
|
104
|
-
await updatePresence()
|
|
105
136
|
},
|
|
106
137
|
}
|
|
107
138
|
}
|