@@ -7,23 +7,6 @@ const timeFormat = {
77 hour12 : true
88}
99
10- // Listen for extension updates
11- chrome . runtime . onUpdateAvailable . addListener ( ( details ) => {
12- console . log ( "Extension update available:" , details . version )
13- // Check if there is an active meeting
14- chrome . storage . local . get ( [ "meetingTabId" ] , function ( result ) {
15- if ( result . meetingTabId ) {
16- // There is an active meeting, defer the update
17- chrome . storage . local . set ( { deferredUpdateAvailableTimestamp : Date . now ( ) } , function ( ) {
18- console . log ( "Deferred update flag set" )
19- } )
20- } else {
21- // No active meeting, apply the update immediately
22- console . log ( "No active meeting, applying update immediately" )
23- chrome . runtime . reload ( )
24- }
25- } )
26- } )
2710
2811chrome . runtime . onMessage . addListener ( function ( message , sender , sendResponse ) {
2912 console . log ( message . type )
@@ -39,15 +22,17 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
3922 }
4023
4124 if ( message . type == "meeting_ended" ) {
42- // Invalidate tab id since transcript is downloaded, prevents double downloading of transcript from tab closed event listener
43- clearTabIdAndApplyUpdate ( )
44- downloadAndPostWebhook ( )
45-
25+ downloadAndPostWebhook ( ) . finally ( ( ) => {
26+ // Invalidate tab id since transcript is downloaded, prevents double downloading of transcript from tab closed event listener
27+ clearTabIdAndApplyUpdate ( )
28+ } )
4629 }
4730
4831 if ( message . type == "download_transcript_at_index" ) {
4932 // Download the requested item
50- downloadTranscript ( message . index , false )
33+ downloadTranscript ( message . index , false ) . then ( ( ) => {
34+ sendResponse ( { success : true } )
35+ } )
5136 }
5237
5338 if ( message . type == "retry_webhook_at_index" ) {
@@ -62,9 +47,14 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
6247 } )
6348 }
6449
65- if ( message . type == "recover_last_transcript" ) {
66- downloadAndPostWebhook ( )
67- sendResponse ( { message : "Recovery process started" } )
50+ if ( message . type == "recover_last_meeting" ) {
51+ downloadAndPostWebhook ( ) . then ( ( ) => {
52+ sendResponse ( { success : true } )
53+ } ) . catch ( ( error ) => {
54+ // Fails if transcript is empty or webhook request fails
55+ console . error ( "Recovery process failed:" , error )
56+ sendResponse ( { success : false , error : error . message } )
57+ } )
6858 }
6959 return true
7060} )
@@ -75,35 +65,76 @@ chrome.tabs.onRemoved.addListener(function (tabid) {
7565 if ( tabid == data . meetingTabId ) {
7666 console . log ( "Successfully intercepted tab close" )
7767
78- // Clearing meetingTabId to prevent misfires of onRemoved until next meeting actually starts
79- clearTabIdAndApplyUpdate ( )
80- downloadAndPostWebhook ( )
68+ downloadAndPostWebhook ( ) . finally ( ( ) => {
69+ // Clearing meetingTabId to prevent misfires of onRemoved until next meeting actually starts
70+ clearTabIdAndApplyUpdate ( )
71+ } )
8172 }
8273 } )
8374} )
8475
85- // Download transcripts, post webhook if URL is enabled and available
76+ // Listen for extension updates
77+ chrome . runtime . onUpdateAvailable . addListener ( ( ) => {
78+ // Check if there is an active meeting
79+ chrome . storage . local . get ( [ "meetingTabId" ] , function ( result ) {
80+ if ( result . meetingTabId ) {
81+ // There is an active meeting, defer the update
82+ chrome . storage . local . set ( { isDeferredUpdatedAvailable : true } , function ( ) {
83+ console . log ( "Deferred update flag set" )
84+ } )
85+ } else {
86+ // No active meeting, apply the update immediately. Meeting tab id is invalidated only post meeting operations are done, so no race conditions.
87+ console . log ( "No active meeting, applying update immediately" )
88+ chrome . runtime . reload ( )
89+ }
90+ } )
91+ } )
92+
93+ // Download transcripts, post webhook if URL is enabled and available
94+ // Fails if transcript is empty or webhook request fails
8695function downloadAndPostWebhook ( ) {
87- chrome . storage . local . get ( [ "transcript" , "chatMessages" ] , function ( resultLocal ) {
88- // Check if at least one of transcript or chatMessages exist. To prevent downloading empty transcripts.
89- if ( ( resultLocal . transcript != "" ) || ( resultLocal . chatMessages != "" ) ) {
90- processTranscript ( ) . then ( ( ) => {
91- chrome . storage . local . get ( [ "meetings" ] , function ( resultLocal ) {
92- chrome . storage . sync . get ( [ "webhookUrl" , "autoPostWebhookAfterMeeting" ] , function ( resultSync ) {
93- // Download the last transcript
94- const lastIndex = resultLocal . meetings . length - 1
95- downloadTranscript ( lastIndex , ( resultSync . webhookUrl && resultSync . autoPostWebhookAfterMeeting ) ? true : false )
96-
97- // Post the last transcript to webhook if auto-post is enabled and available
98- if ( resultSync . autoPostWebhookAfterMeeting && resultSync . webhookUrl ) {
99- postTranscriptToWebhook ( lastIndex ) . catch ( error => {
100- console . error ( "Webhook post failed:" , error )
101- } )
102- }
96+ return new Promise ( ( resolve , reject ) => {
97+ chrome . storage . local . get ( [ "transcript" , "chatMessages" ] , function ( resultLocal ) {
98+ // Check if at least one of transcript or chatMessages exist. To prevent downloading empty transcripts.
99+ if ( ( resultLocal . transcript != "" ) || ( resultLocal . chatMessages != "" ) ) {
100+ processTranscript ( ) . then ( ( ) => {
101+ chrome . storage . local . get ( [ "meetings" ] , function ( resultLocal ) {
102+ chrome . storage . sync . get ( [ "webhookUrl" , "autoPostWebhookAfterMeeting" ] , function ( resultSync ) {
103+ // Create an array of promises to execute in parallel
104+ const promises = [ ]
105+
106+ // Promise to download transcript
107+ const lastIndex = resultLocal . meetings . length - 1
108+ promises . push ( [
109+ downloadTranscript (
110+ lastIndex ,
111+ resultSync . webhookUrl && resultSync . autoPostWebhookAfterMeeting ? true : false
112+ )
113+ ] )
114+
115+ // Promise to post webhook if enabled
116+ if ( resultSync . autoPostWebhookAfterMeeting && resultSync . webhookUrl ) {
117+ promises . push ( postTranscriptToWebhook ( lastIndex ) )
118+ }
119+
120+ // Execute all promises in parallel
121+ // First promise will always resolve, second one will fail if webhook request fails
122+ Promise . all ( promises )
123+ . then ( ( ) => {
124+ resolve ( )
125+ } )
126+ . catch ( error => {
127+ console . error ( "Operation failed:" , error )
128+ reject ( error )
129+ } )
130+ } )
103131 } )
104132 } )
105- } )
106- }
133+ }
134+ else {
135+ reject ( "Empty transcript and empty chatMessages" )
136+ }
137+ } )
107138 } )
108139}
109140
@@ -154,80 +185,84 @@ function processTranscript() {
154185
155186
156187function downloadTranscript ( index , webhookEnabled ) {
157- chrome . storage . local . get ( [ "meetings" ] , function ( result ) {
158- if ( result . meetings && result . meetings [ index ] ) {
159- const meeting = result . meetings [ index ]
160-
161- // Sanitise meeting title to prevent invalid file name errors
162- // https://stackoverflow.com/a/78675894
163- const invalidFilenameRegex = / [: ? " * < > | ~ / \\ \u{1} - \u{1f} \u{7f} \u{80} - \u{9f} \p{ Cf} \p{ Cn} ] | ^ [ . \u{0} \p{ Zl} \p{ Zp} \p{ Zs} ] | [ . \u{0} \p{ Zl} \p{ Zp} \p{ Zs} ] $ | ^ ( C O N | P R N | A U X | N U L | C O M [ 1 - 9 ] | L P T [ 1 - 9 ] ) (? = \. | $ ) / gui
164- const sanitisedMeetingTitle = meeting . title . replaceAll ( invalidFilenameRegex , "_" )
165-
166- // Format timestamp for human-readable filename
167- const timestamp = new Date ( meeting . meetingStartTimestamp )
168- const formattedTimestamp = timestamp . toLocaleString ( "default" , {
169- year : "numeric" ,
170- month : "2-digit" ,
171- day : "2-digit" ,
172- hour : "2-digit" ,
173- minute : "2-digit" ,
174- second : "2-digit" ,
175- hour12 : false
176- } ) . replace ( / [ \/ : ] / g, "-" )
177-
178- const fileName = `TranscripTonic/Transcript-${ sanitisedMeetingTitle } at ${ formattedTimestamp } .txt`
179-
180-
181- // Format transcript and chatMessages content
182- let content = getTranscriptString ( meeting . transcript )
183- content += `\n\n---------------\nCHAT MESSAGES\n---------------\n\n`
184- content += getChatMessagesString ( meeting . chatMessages )
185-
186- // Add branding
187- content += "\n\n---------------\n"
188- content += "Transcript saved using TranscripTonic Chrome extension (https://chromewebstore.google.com/detail/ciepnfnceimjehngolkijpnbappkkiag)"
189- content += "\n---------------"
190-
191- const blob = new Blob ( [ content ] , { type : "text/plain" } )
192-
193- // Read the blob as a data URL
194- const reader = new FileReader ( )
195-
196- // Download once blob is read
197- reader . onload = function ( event ) {
198- const dataUrl = event . target . result
199-
200- // Create a download with Chrome Download API
201- chrome . downloads . download ( {
202- url : dataUrl ,
203- filename : fileName ,
204- conflictAction : "uniquify"
205- } ) . then ( ( ) => {
206- console . log ( "Transcript downloaded" )
207- // Increment anonymous transcript generated count to a Google sheet
208- fetch ( `https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${ chrome . runtime . getManifest ( ) . version } &webhookEnabled=${ webhookEnabled } ` , {
209- mode : "no-cors"
210- } )
211- } ) . catch ( ( err ) => {
212- console . error ( err )
188+ return new Promise ( ( resolve ) => {
189+ chrome . storage . local . get ( [ "meetings" ] , function ( result ) {
190+ if ( result . meetings && result . meetings [ index ] ) {
191+ const meeting = result . meetings [ index ]
192+
193+ // Sanitise meeting title to prevent invalid file name errors
194+ // https://stackoverflow.com/a/78675894
195+ const invalidFilenameRegex = / [: ? " * < > | ~ / \\ \u{1} - \u{1f} \u{7f} \u{80} - \u{9f} \p{ Cf} \p{ Cn} ] | ^ [ . \u{0} \p{ Zl} \p{ Zp} \p{ Zs} ] | [ . \u{0} \p{ Zl} \p{ Zp} \p{ Zs} ] $ | ^ ( C O N | P R N | A U X | N U L | C O M [ 1 - 9 ] | L P T [ 1 - 9 ] ) (? = \. | $ ) / gui
196+ const sanitisedMeetingTitle = meeting . title . replaceAll ( invalidFilenameRegex , "_" )
197+
198+ // Format timestamp for human-readable filename
199+ const timestamp = new Date ( meeting . meetingStartTimestamp )
200+ const formattedTimestamp = timestamp . toLocaleString ( "default" , {
201+ year : "numeric" ,
202+ month : "2-digit" ,
203+ day : "2-digit" ,
204+ hour : "2-digit" ,
205+ minute : "2-digit" ,
206+ second : "2-digit" ,
207+ hour12 : false
208+ } ) . replace ( / [ \/ : ] / g, "-" )
209+
210+ const fileName = `TranscripTonic/Transcript-${ sanitisedMeetingTitle } at ${ formattedTimestamp } .txt`
211+
212+
213+ // Format transcript and chatMessages content
214+ let content = getTranscriptString ( meeting . transcript )
215+ content += `\n\n---------------\nCHAT MESSAGES\n---------------\n\n`
216+ content += getChatMessagesString ( meeting . chatMessages )
217+
218+ // Add branding
219+ content += "\n\n---------------\n"
220+ content += "Transcript saved using TranscripTonic Chrome extension (https://chromewebstore.google.com/detail/ciepnfnceimjehngolkijpnbappkkiag)"
221+ content += "\n---------------"
222+
223+ const blob = new Blob ( [ content ] , { type : "text/plain" } )
224+
225+ // Read the blob as a data URL
226+ const reader = new FileReader ( )
227+
228+ // Read the blob and download as text file
229+ reader . readAsDataURL ( blob )
230+
231+ // Download once blob is read
232+ reader . onload = function ( event ) {
233+ const dataUrl = event . target . result
234+
235+ // Create a download with Chrome Download API
213236 chrome . downloads . download ( {
214237 url : dataUrl ,
215- filename : "TranscripTonic/Transcript.txt" ,
238+ filename : fileName ,
216239 conflictAction : "uniquify"
240+ } ) . then ( ( ) => {
241+ console . log ( "Transcript downloaded" )
242+ // Increment anonymous transcript generated count to a Google sheet
243+ fetch ( `https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${ chrome . runtime . getManifest ( ) . version } &webhookEnabled=${ webhookEnabled } ` , {
244+ mode : "no-cors"
245+ } )
246+ resolve ( )
247+ } ) . catch ( ( err ) => {
248+ console . error ( err )
249+ chrome . downloads . download ( {
250+ url : dataUrl ,
251+ filename : "TranscripTonic/Transcript.txt" ,
252+ conflictAction : "uniquify"
253+ } )
254+ console . log ( "Invalid file name. Transcript downloaded to TranscripTonic directory with simple file name." )
255+ // Logs anonymous errors to a Google sheet for swift debugging
256+ fetch ( `https://script.google.com/macros/s/AKfycbxiyQSDmJuC2onXL7pKjXgELK1vA3aLGZL5_BLjzCp7fMoQ8opTzJBNfEHQX_QIzZ-j4Q/exec?version=${ chrome . runtime . getManifest ( ) . version } &code=009&error=${ encodeURIComponent ( err ) } ` , { mode : "no-cors" } )
257+ // Increment anonymous transcript generated count to a Google sheet
258+ fetch ( `https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${ chrome . runtime . getManifest ( ) . version } &webhookEnabled=${ webhookEnabled } ` , {
259+ mode : "no-cors"
260+ } )
261+ resolve ( )
217262 } )
218- console . log ( "Invalid file name. Transcript downloaded to TranscripTonic directory with simple file name." )
219- // Logs anonymous errors to a Google sheet for swift debugging
220- fetch ( `https://script.google.com/macros/s/AKfycbxiyQSDmJuC2onXL7pKjXgELK1vA3aLGZL5_BLjzCp7fMoQ8opTzJBNfEHQX_QIzZ-j4Q/exec?version=${ chrome . runtime . getManifest ( ) . version } &code=009&error=${ encodeURIComponent ( err ) } ` , { mode : "no-cors" } )
221- // Increment anonymous transcript generated count to a Google sheet
222- fetch ( `https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${ chrome . runtime . getManifest ( ) . version } &webhookEnabled=${ webhookEnabled } ` , {
223- mode : "no-cors"
224- } )
225- } )
263+ }
226264 }
227-
228- // Read the blob and download as text file
229- reader . readAsDataURL ( blob )
230- }
265+ } )
231266 } )
232267}
233268
@@ -345,19 +380,14 @@ function getChatMessagesString(chatMessages) {
345380function clearTabIdAndApplyUpdate ( ) {
346381 chrome . storage . local . set ( { meetingTabId : null } , function ( ) {
347382 console . log ( "Meeting tab id cleared for next meeting" )
383+
348384 // Check if there's a deferred update
349- chrome . storage . local . get ( [ "deferredUpdateAvailableTimestamp" ] , function ( result ) {
350- if ( result . deferredUpdateAvailableTimestamp ) {
351- const timeSinceUpdateAvailable = Date . now ( ) - result . deferredUpdateAvailableTimestamp
352- const timeToWait = timeSinceUpdateAvailable > 10000 ? 0 : 10000 - timeSinceUpdateAvailable
353-
354- console . log ( `Applying deferred update in ${ timeToWait } ms` )
355- setTimeout ( ( ) => {
356- console . log ( "Applying deferred update" )
357- chrome . storage . local . set ( { deferredUpdateAvailableTimestamp : null } , function ( ) {
358- chrome . runtime . reload ( )
359- } )
360- } , timeToWait )
385+ chrome . storage . local . get ( [ "isDeferredUpdatedAvailable" ] , function ( result ) {
386+ if ( result . isDeferredUpdatedAvailable ) {
387+ console . log ( "Applying deferred update" )
388+ chrome . storage . local . set ( { isDeferredUpdatedAvailable : false } , function ( ) {
389+ chrome . runtime . reload ( )
390+ } )
361391 }
362392 } )
363393 } )
0 commit comments