1
- import { EventEmitter } from 'events' ;
2
- import { Client as LinkedInClient } from 'linkedin-api' ;
3
- import { elizaLogger } from '@ai16z/eliza' ;
4
- import { stringToUuid , embeddingZeroVector } from '@ai16z/eliza' ;
1
+ import { EventEmitter } from "events" ;
2
+ // @ts -ignore
3
+ import { Client as LinkedInClient } from "linkedin-api" ;
4
+ import { elizaLogger } from "@ai16z/eliza" ;
5
+ import { stringToUuid , getEmbeddingZeroVector } from "@ai16z/eliza" ;
5
6
6
7
class RequestQueue {
7
- private queue : ( ( ) => Promise < any > ) [ ] = [ ] ;
8
- private processing = false ;
8
+ private queue : ( ( ) => Promise < any > ) [ ] = [ ] ;
9
+ private processing = false ;
10
+
11
+ async add < T > ( request : ( ) => Promise < T > ) : Promise < T > {
12
+ return new Promise ( ( resolve , reject ) => {
13
+ this . queue . push ( async ( ) => {
14
+ try {
15
+ const result = await request ( ) ;
16
+ resolve ( result ) ;
17
+ } catch ( error ) {
18
+ reject ( error ) ;
19
+ }
20
+ } ) ;
21
+ this . processQueue ( ) ;
22
+ } ) ;
23
+ }
9
24
10
- async add < T > ( request : ( ) => Promise < T > ) : Promise < T > {
11
- return new Promise ( ( resolve , reject ) => {
12
- this . queue . push ( async ( ) => {
13
- try {
14
- const result = await request ( ) ;
15
- resolve ( result ) ;
16
- } catch ( error ) {
17
- reject ( error ) ;
25
+ private async processQueue ( ) {
26
+ if ( this . processing || this . queue . length === 0 ) {
27
+ return ;
28
+ }
29
+
30
+ this . processing = true ;
31
+ while ( this . queue . length > 0 ) {
32
+ const request = this . queue . shift ( ) ;
33
+ if ( ! request ) continue ;
34
+ try {
35
+ await request ( ) ;
36
+ } catch ( error ) {
37
+ console . error ( "Error processing request:" , error ) ;
38
+ this . queue . unshift ( request ) ;
39
+ await this . exponentialBackoff ( this . queue . length ) ;
40
+ }
41
+ await this . randomDelay ( ) ;
18
42
}
19
- } ) ;
20
- this . processQueue ( ) ;
21
- } ) ;
22
- }
23
-
24
- private async processQueue ( ) {
25
- if ( this . processing || this . queue . length === 0 ) {
26
- return ;
43
+ this . processing = false ;
44
+ }
45
+
46
+ private async exponentialBackoff ( retryCount : number ) {
47
+ const delay = Math . pow ( 2 , retryCount ) * 1000 ;
48
+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
27
49
}
28
50
29
- this . processing = true ;
30
- while ( this . queue . length > 0 ) {
31
- const request = this . queue . shift ( ) ;
32
- try {
33
- await request ( ) ;
34
- } catch ( error ) {
35
- console . error ( 'Error processing request:' , error ) ;
36
- this . queue . unshift ( request ) ;
37
- await this . exponentialBackoff ( this . queue . length ) ;
38
- }
39
- await this . randomDelay ( ) ;
51
+ private async randomDelay ( ) {
52
+ const delay = Math . floor ( Math . random ( ) * 2000 ) + 1500 ;
53
+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
40
54
}
41
- this . processing = false ;
42
- }
43
-
44
- private async exponentialBackoff ( retryCount : number ) {
45
- const delay = Math . pow ( 2 , retryCount ) * 1000 ;
46
- await new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
47
- }
48
-
49
- private async randomDelay ( ) {
50
- const delay = Math . floor ( Math . random ( ) * 2000 ) + 1500 ;
51
- await new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
52
- }
53
55
}
54
56
55
57
export class ClientBase extends EventEmitter {
56
- private static _linkedInClient : LinkedInClient ;
57
- protected linkedInClient : LinkedInClient ;
58
- protected runtime : any ;
59
- protected profile : any ;
60
- protected requestQueue : RequestQueue = new RequestQueue ( ) ;
61
-
62
- constructor ( runtime : any ) {
63
- super ( ) ;
64
- this . runtime = runtime ;
65
-
66
- if ( ClientBase . _linkedInClient ) {
67
- this . linkedInClient = ClientBase . _linkedInClient ;
68
- } else {
69
- this . linkedInClient = new LinkedInClient ( ) ;
70
- ClientBase . _linkedInClient = this . linkedInClient ;
58
+ private static _linkedInClient : LinkedInClient ;
59
+ protected linkedInClient : LinkedInClient ;
60
+ protected runtime : any ;
61
+ protected profile : any ;
62
+ protected requestQueue : RequestQueue = new RequestQueue ( ) ;
63
+
64
+ constructor ( runtime : any ) {
65
+ super ( ) ;
66
+ this . runtime = runtime ;
67
+
68
+ if ( ClientBase . _linkedInClient ) {
69
+ this . linkedInClient = ClientBase . _linkedInClient ;
70
+ } else {
71
+ this . linkedInClient = new LinkedInClient ( ) ;
72
+ ClientBase . _linkedInClient = this . linkedInClient ;
73
+ }
71
74
}
72
- }
73
75
74
- async init ( ) {
75
- const username = this . runtime . getSetting ( ' LINKEDIN_USERNAME' ) ;
76
- const password = this . runtime . getSetting ( ' LINKEDIN_PASSWORD' ) ;
76
+ async init ( ) {
77
+ const username = this . runtime . getSetting ( " LINKEDIN_USERNAME" ) ;
78
+ const password = this . runtime . getSetting ( " LINKEDIN_PASSWORD" ) ;
77
79
78
- if ( ! username || ! password ) {
79
- throw new Error ( 'LinkedIn credentials not configured' ) ;
80
+ if ( ! username || ! password ) {
81
+ throw new Error ( "LinkedIn credentials not configured" ) ;
82
+ }
83
+
84
+ elizaLogger . log ( "Logging into LinkedIn..." ) ;
85
+
86
+ try {
87
+ await this . linkedInClient . login ( username , password ) ;
88
+ this . profile = await this . fetchProfile ( ) ;
89
+
90
+ if ( this . profile ) {
91
+ elizaLogger . log (
92
+ "LinkedIn profile loaded:" ,
93
+ JSON . stringify ( this . profile , null , 2 )
94
+ ) ;
95
+ this . runtime . character . linkedInProfile = {
96
+ id : this . profile . id ,
97
+ username : this . profile . username ,
98
+ fullName : this . profile . fullName ,
99
+ headline : this . profile . headline ,
100
+ summary : this . profile . summary ,
101
+ } ;
102
+ } else {
103
+ throw new Error ( "Failed to load LinkedIn profile" ) ;
104
+ }
105
+
106
+ await this . loadInitialState ( ) ;
107
+ } catch ( error ) {
108
+ elizaLogger . error ( "LinkedIn login failed:" , error ) ;
109
+ throw error ;
110
+ }
80
111
}
81
112
82
- elizaLogger . log ( 'Logging into LinkedIn...' ) ;
83
-
84
- try {
85
- await this . linkedInClient . login ( username , password ) ;
86
- this . profile = await this . fetchProfile ( ) ;
87
-
88
- if ( this . profile ) {
89
- elizaLogger . log ( 'LinkedIn profile loaded:' , JSON . stringify ( this . profile , null , 2 ) ) ;
90
- this . runtime . character . linkedInProfile = {
91
- id : this . profile . id ,
92
- username : this . profile . username ,
93
- fullName : this . profile . fullName ,
94
- headline : this . profile . headline ,
95
- summary : this . profile . summary
96
- } ;
97
- } else {
98
- throw new Error ( 'Failed to load LinkedIn profile' ) ;
99
- }
100
-
101
- await this . loadInitialState ( ) ;
102
- } catch ( error ) {
103
- elizaLogger . error ( 'LinkedIn login failed:' , error ) ;
104
- throw error ;
113
+ async fetchProfile ( ) {
114
+ const cachedProfile = await this . getCachedProfile ( ) ;
115
+ if ( cachedProfile ) return cachedProfile ;
116
+
117
+ try {
118
+ const profile = await this . requestQueue . add ( async ( ) => {
119
+ const profileData = await this . linkedInClient . getProfile ( ) ;
120
+ return {
121
+ id : profileData . id ,
122
+ username : profileData . username ,
123
+ fullName :
124
+ profileData . firstName + " " + profileData . lastName ,
125
+ headline : profileData . headline ,
126
+ summary : profileData . summary ,
127
+ } ;
128
+ } ) ;
129
+
130
+ await this . cacheProfile ( profile ) ;
131
+ return profile ;
132
+ } catch ( error ) {
133
+ console . error ( "Error fetching LinkedIn profile:" , error ) ;
134
+ return undefined ;
135
+ }
105
136
}
106
- }
107
-
108
- async fetchProfile ( ) {
109
- const cachedProfile = await this . getCachedProfile ( ) ;
110
- if ( cachedProfile ) return cachedProfile ;
111
-
112
- try {
113
- const profile = await this . requestQueue . add ( async ( ) => {
114
- const profileData = await this . linkedInClient . getProfile ( ) ;
115
- return {
116
- id : profileData . id ,
117
- username : profileData . username ,
118
- fullName : profileData . firstName + ' ' + profileData . lastName ,
119
- headline : profileData . headline ,
120
- summary : profileData . summary
121
- } ;
122
- } ) ;
123
137
124
- await this . cacheProfile ( profile ) ;
125
- return profile ;
126
- } catch ( error ) {
127
- console . error ( 'Error fetching LinkedIn profile:' , error ) ;
128
- return undefined ;
138
+ async loadInitialState ( ) {
139
+ await this . populateConnections ( ) ;
140
+ await this . populateRecentActivity ( ) ;
141
+ }
142
+
143
+ async populateConnections ( ) {
144
+ const connections = await this . requestQueue . add ( async ( ) => {
145
+ return await this . linkedInClient . getConnections ( ) ;
146
+ } ) ;
147
+
148
+ for ( const connection of connections ) {
149
+ const roomId = stringToUuid ( `linkedin-connection-${ connection . id } ` ) ;
150
+ await this . runtime . ensureConnection (
151
+ stringToUuid ( connection . id ) ,
152
+ roomId ,
153
+ connection . username ,
154
+ connection . fullName ,
155
+ "linkedin"
156
+ ) ;
157
+ }
158
+ }
159
+
160
+ async populateRecentActivity ( ) {
161
+ const activities = await this . requestQueue . add ( async ( ) => {
162
+ return await this . linkedInClient . getFeedPosts ( ) ;
163
+ } ) ;
164
+
165
+ for ( const activity of activities ) {
166
+ const roomId = stringToUuid ( `linkedin-post-${ activity . id } ` ) ;
167
+ await this . saveActivity ( activity , roomId ) ;
168
+ }
129
169
}
130
- }
131
-
132
- async loadInitialState ( ) {
133
- await this . populateConnections ( ) ;
134
- await this . populateRecentActivity ( ) ;
135
- }
136
-
137
- async populateConnections ( ) {
138
- const connections = await this . requestQueue . add ( async ( ) => {
139
- return await this . linkedInClient . getConnections ( ) ;
140
- } ) ;
141
-
142
- for ( const connection of connections ) {
143
- const roomId = stringToUuid ( `linkedin-connection- ${ connection . id } ` ) ;
144
- await this . runtime . ensureConnection (
145
- stringToUuid ( connection . id ) ,
146
- roomId ,
147
- connection . username ,
148
- connection . fullName ,
149
- 'linkedin'
150
- ) ;
170
+
171
+ private async saveActivity ( activity : any , roomId : string ) {
172
+ const content = {
173
+ text : activity . text ,
174
+ url : activity . url ,
175
+ source : "linkedin" ,
176
+ type : activity . type ,
177
+ } ;
178
+
179
+ await this . runtime . messageManager . createMemory ( {
180
+ id : stringToUuid ( ` ${ activity . id } - ${ this . runtime . agentId } ` ) ,
181
+ userId :
182
+ activity . authorId === this . profile . id
183
+ ? this . runtime . agentId
184
+ : stringToUuid ( activity . authorId ) ,
185
+ content ,
186
+ agentId : this . runtime . agentId ,
187
+ roomId ,
188
+ embedding : getEmbeddingZeroVector ( ) ,
189
+ createdAt : activity . timestamp ,
190
+ } ) ;
151
191
}
152
- }
153
192
154
- async populateRecentActivity ( ) {
155
- const activities = await this . requestQueue . add ( async ( ) => {
156
- return await this . linkedInClient . getFeedPosts ( ) ;
157
- } ) ;
193
+ private async getCachedProfile ( ) {
194
+ return await this . runtime . cacheManager . get (
195
+ `linkedin/${ this . runtime . getSetting ( "LINKEDIN_USERNAME" ) } /profile`
196
+ ) ;
197
+ }
158
198
159
- for ( const activity of activities ) {
160
- const roomId = stringToUuid ( `linkedin-post-${ activity . id } ` ) ;
161
- await this . saveActivity ( activity , roomId ) ;
199
+ private async cacheProfile ( profile : any ) {
200
+ await this . runtime . cacheManager . set (
201
+ `linkedin/${ profile . username } /profile` ,
202
+ profile
203
+ ) ;
162
204
}
163
- }
164
-
165
- private async saveActivity ( activity : any , roomId : string ) {
166
- const content = {
167
- text : activity . text ,
168
- url : activity . url ,
169
- source : 'linkedin' ,
170
- type : activity . type
171
- } ;
172
-
173
- await this . runtime . messageManager . createMemory ( {
174
- id : stringToUuid ( `${ activity . id } -${ this . runtime . agentId } ` ) ,
175
- userId : activity . authorId === this . profile . id ?
176
- this . runtime . agentId :
177
- stringToUuid ( activity . authorId ) ,
178
- content,
179
- agentId : this . runtime . agentId ,
180
- roomId,
181
- embedding : embeddingZeroVector ,
182
- createdAt : activity . timestamp
183
- } ) ;
184
- }
185
-
186
- private async getCachedProfile ( ) {
187
- return await this . runtime . cacheManager . get (
188
- `linkedin/${ this . runtime . getSetting ( 'LINKEDIN_USERNAME' ) } /profile`
189
- ) ;
190
- }
191
-
192
- private async cacheProfile ( profile : any ) {
193
- await this . runtime . cacheManager . set (
194
- `linkedin/${ profile . username } /profile` ,
195
- profile
196
- ) ;
197
- }
198
- }
205
+ }
0 commit comments