@elizaos/plugin-twitch 2.0.0-alpha.3

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/src/index.ts ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Twitch chat integration plugin for ElizaOS.
3
+ *
4
+ * This plugin provides Twitch chat integration using the @twurple library.
5
+ */
6
+
7
+ import type { IAgentRuntime, Plugin } from "@elizaos/core";
8
+ import { logger } from "@elizaos/core";
9
+
10
+ // Service
11
+ export { TwitchService } from "./service.js";
12
+
13
+ // Types
14
+ export * from "./types.js";
15
+
16
+ import { joinChannel } from "./actions/joinChannel.js";
17
+ import { leaveChannel } from "./actions/leaveChannel.js";
18
+ import { listChannels } from "./actions/listChannels.js";
19
+ // Actions
20
+ import { sendMessage } from "./actions/sendMessage.js";
21
+
22
+ export { sendMessage, joinChannel, leaveChannel, listChannels };
23
+
24
+ // Providers
25
+ import { channelStateProvider } from "./providers/channelState.js";
26
+ import { userContextProvider } from "./providers/userContext.js";
27
+
28
+ export { channelStateProvider, userContextProvider };
29
+
30
+ // Import service for plugin
31
+ import { TwitchService } from "./service.js";
32
+
33
+ /**
34
+ * Twitch plugin definition.
35
+ */
36
+ const twitchPlugin: Plugin = {
37
+ name: "twitch",
38
+ description:
39
+ "Twitch chat integration plugin for ElizaOS with real-time messaging",
40
+
41
+ services: [TwitchService],
42
+
43
+ actions: [sendMessage, joinChannel, leaveChannel, listChannels],
44
+
45
+ providers: [channelStateProvider, userContextProvider],
46
+
47
+ tests: [],
48
+
49
+ init: async (
50
+ _config: Record<string, string>,
51
+ runtime: IAgentRuntime,
52
+ ): Promise<void> => {
53
+ const username = runtime.getSetting("TWITCH_USERNAME");
54
+ const clientId = runtime.getSetting("TWITCH_CLIENT_ID");
55
+ const accessToken = runtime.getSetting("TWITCH_ACCESS_TOKEN");
56
+ const channel = runtime.getSetting("TWITCH_CHANNEL");
57
+
58
+ logger.info("=".repeat(60));
59
+ logger.info("Twitch Plugin Configuration");
60
+ logger.info("=".repeat(60));
61
+ logger.info(` Username: ${username ? "✓ Set" : "✗ Missing (required)"}`);
62
+ logger.info(` Client ID: ${clientId ? "✓ Set" : "✗ Missing (required)"}`);
63
+ logger.info(
64
+ ` Access Token: ${accessToken ? "✓ Set" : "✗ Missing (required)"}`,
65
+ );
66
+ logger.info(
67
+ ` Channel: ${channel ? `✓ ${channel}` : "✗ Missing (required)"}`,
68
+ );
69
+ logger.info("=".repeat(60));
70
+
71
+ // Validate required settings
72
+ const missing: string[] = [];
73
+ if (!username) missing.push("TWITCH_USERNAME");
74
+ if (!clientId) missing.push("TWITCH_CLIENT_ID");
75
+ if (!accessToken) missing.push("TWITCH_ACCESS_TOKEN");
76
+ if (!channel) missing.push("TWITCH_CHANNEL");
77
+
78
+ if (missing.length > 0) {
79
+ logger.warn(
80
+ `Twitch plugin: Missing required configuration: ${missing.join(", ")}`,
81
+ );
82
+ }
83
+
84
+ // Additional optional settings
85
+ const clientSecret = runtime.getSetting("TWITCH_CLIENT_SECRET");
86
+ const refreshToken = runtime.getSetting("TWITCH_REFRESH_TOKEN");
87
+ const additionalChannels = runtime.getSetting("TWITCH_CHANNELS");
88
+ const requireMention = runtime.getSetting("TWITCH_REQUIRE_MENTION");
89
+ const allowedRoles = runtime.getSetting("TWITCH_ALLOWED_ROLES");
90
+
91
+ if (clientSecret && refreshToken) {
92
+ logger.info(
93
+ " Token Refresh: ✓ Enabled (client secret and refresh token set)",
94
+ );
95
+ } else if (clientSecret || refreshToken) {
96
+ logger.warn(
97
+ " Token Refresh: ⚠ Partial (need both TWITCH_CLIENT_SECRET and TWITCH_REFRESH_TOKEN)",
98
+ );
99
+ }
100
+
101
+ if (additionalChannels) {
102
+ logger.info(` Additional Channels: ${additionalChannels}`);
103
+ }
104
+
105
+ if (requireMention === "true") {
106
+ logger.info(
107
+ " Require Mention: ✓ Enabled (will only respond to @mentions)",
108
+ );
109
+ }
110
+
111
+ if (allowedRoles) {
112
+ logger.info(` Allowed Roles: ${allowedRoles}`);
113
+ }
114
+ },
115
+ };
116
+
117
+ export default twitchPlugin;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Channel state provider for Twitch plugin.
3
+ */
4
+
5
+ import type {
6
+ IAgentRuntime,
7
+ Memory,
8
+ Provider,
9
+ ProviderResult,
10
+ State,
11
+ } from "@elizaos/core";
12
+ import type { TwitchService } from "../service.js";
13
+ import {
14
+ formatChannelForDisplay,
15
+ normalizeChannel,
16
+ TWITCH_SERVICE_NAME,
17
+ } from "../types.js";
18
+
19
+ /**
20
+ * Provider that gives the agent information about the current Twitch channel context.
21
+ */
22
+ export const channelStateProvider: Provider = {
23
+ name: "twitchChannelState",
24
+ description: "Provides information about the current Twitch channel context",
25
+
26
+ get: async (
27
+ runtime: IAgentRuntime,
28
+ message: Memory,
29
+ state: State,
30
+ ): Promise<ProviderResult> => {
31
+ // Only provide context for Twitch messages
32
+ if (message.content.source !== "twitch") {
33
+ return {
34
+ data: {},
35
+ values: {},
36
+ text: "",
37
+ };
38
+ }
39
+
40
+ const twitchService =
41
+ runtime.getService<TwitchService>(TWITCH_SERVICE_NAME);
42
+
43
+ if (!twitchService || !twitchService.isConnected()) {
44
+ return {
45
+ data: {
46
+ connected: false,
47
+ },
48
+ values: {
49
+ connected: false,
50
+ },
51
+ text: "",
52
+ };
53
+ }
54
+
55
+ const agentName = state?.agentName || "The agent";
56
+
57
+ // Get room from state if available
58
+ const room = state?.data?.room;
59
+ const channelId = room?.channelId as string | undefined;
60
+ const channel = channelId
61
+ ? normalizeChannel(channelId)
62
+ : twitchService.getPrimaryChannel();
63
+
64
+ const joinedChannels = twitchService.getJoinedChannels();
65
+ const isPrimaryChannel = channel === twitchService.getPrimaryChannel();
66
+ const botUsername = twitchService.getBotUsername();
67
+
68
+ let responseText = `${agentName} is currently in Twitch channel ${formatChannelForDisplay(channel)}.`;
69
+
70
+ if (isPrimaryChannel) {
71
+ responseText += " This is the primary channel.";
72
+ }
73
+
74
+ responseText += `\n\nTwitch is a live streaming platform. Chat messages are public and visible to all viewers.`;
75
+ responseText += ` ${agentName} is logged in as @${botUsername}.`;
76
+ responseText += ` Currently connected to ${joinedChannels.length} channel(s).`;
77
+
78
+ return {
79
+ data: {
80
+ channel,
81
+ displayChannel: formatChannelForDisplay(channel),
82
+ isPrimaryChannel,
83
+ botUsername,
84
+ joinedChannels,
85
+ channelCount: joinedChannels.length,
86
+ connected: true,
87
+ },
88
+ values: {
89
+ channel,
90
+ displayChannel: formatChannelForDisplay(channel),
91
+ isPrimaryChannel,
92
+ botUsername,
93
+ channelCount: joinedChannels.length,
94
+ },
95
+ text: responseText,
96
+ };
97
+ },
98
+ };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * User context provider for Twitch plugin.
3
+ */
4
+
5
+ import type {
6
+ IAgentRuntime,
7
+ Memory,
8
+ Provider,
9
+ ProviderResult,
10
+ State,
11
+ } from "@elizaos/core";
12
+ import type { TwitchService } from "../service.js";
13
+ import {
14
+ getTwitchUserDisplayName,
15
+ TWITCH_SERVICE_NAME,
16
+ type TwitchUserInfo,
17
+ } from "../types.js";
18
+
19
+ /**
20
+ * Provider that gives the agent information about the Twitch user context.
21
+ */
22
+ export const userContextProvider: Provider = {
23
+ name: "twitchUserContext",
24
+ description:
25
+ "Provides information about the Twitch user in the current conversation",
26
+
27
+ get: async (
28
+ runtime: IAgentRuntime,
29
+ message: Memory,
30
+ state: State,
31
+ ): Promise<ProviderResult> => {
32
+ // Only provide context for Twitch messages
33
+ if (message.content.source !== "twitch") {
34
+ return {
35
+ data: {},
36
+ values: {},
37
+ text: "",
38
+ };
39
+ }
40
+
41
+ const twitchService =
42
+ runtime.getService<TwitchService>(TWITCH_SERVICE_NAME);
43
+
44
+ if (!twitchService || !twitchService.isConnected()) {
45
+ return {
46
+ data: {},
47
+ values: {},
48
+ text: "",
49
+ };
50
+ }
51
+
52
+ const agentName = state?.agentName || "The agent";
53
+
54
+ // Try to get user info from message metadata
55
+ const metadata = message.content.metadata as
56
+ | Record<string, unknown>
57
+ | undefined;
58
+ const userInfo = metadata?.user as TwitchUserInfo | undefined;
59
+
60
+ if (!userInfo) {
61
+ return {
62
+ data: {},
63
+ values: {},
64
+ text: "",
65
+ };
66
+ }
67
+
68
+ const displayName = getTwitchUserDisplayName(userInfo);
69
+ const roles: string[] = [];
70
+
71
+ if (userInfo.isBroadcaster) {
72
+ roles.push("broadcaster");
73
+ }
74
+ if (userInfo.isModerator) {
75
+ roles.push("moderator");
76
+ }
77
+ if (userInfo.isVip) {
78
+ roles.push("VIP");
79
+ }
80
+ if (userInfo.isSubscriber) {
81
+ roles.push("subscriber");
82
+ }
83
+
84
+ const roleText = roles.length > 0 ? roles.join(", ") : "viewer";
85
+
86
+ let responseText = `${agentName} is talking to ${displayName} (${roleText}) in Twitch chat.`;
87
+
88
+ if (userInfo.isBroadcaster) {
89
+ responseText += ` ${displayName} is the channel owner/broadcaster.`;
90
+ } else if (userInfo.isModerator) {
91
+ responseText += ` ${displayName} is a channel moderator.`;
92
+ }
93
+
94
+ return {
95
+ data: {
96
+ userId: userInfo.userId,
97
+ username: userInfo.username,
98
+ displayName,
99
+ isBroadcaster: userInfo.isBroadcaster,
100
+ isModerator: userInfo.isModerator,
101
+ isVip: userInfo.isVip,
102
+ isSubscriber: userInfo.isSubscriber,
103
+ roles,
104
+ color: userInfo.color,
105
+ },
106
+ values: {
107
+ userId: userInfo.userId,
108
+ username: userInfo.username,
109
+ displayName,
110
+ roleText,
111
+ isBroadcaster: userInfo.isBroadcaster,
112
+ isModerator: userInfo.isModerator,
113
+ },
114
+ text: responseText,
115
+ };
116
+ },
117
+ };