1010 *   exempt labels that have been inactive for 90+ days. 
1111 * - Avoids sending duplicate Friendly Reminder comments if one was  
1212 *   posted within the last 7 days. 
13+  * - Moves issues labeled 'questions' to GitHub Discussions 
1314 */ 
1415
1516const  dedent  =  ( strings ,  ...values )  =>  { 
@@ -28,68 +29,112 @@ async function fetchAllOpenIssues(github, owner, repo) {
2829    let  page  =  1 ; 
2930
3031    while  ( true )  { 
31-         const  response  =  await  github . rest . issues . listForRepo ( { 
32+         try  { 
33+             const  response  =  await  github . rest . issues . listForRepo ( { 
34+                 owner, 
35+                 repo, 
36+                 state : 'open' , 
37+                 per_page : 100 , 
38+                 page, 
39+             } ) ; 
40+             const  data  =  response . data  ||  [ ] ; 
41+             if  ( data . length  ===  0 )  break ; 
42+             const  onlyIssues  =  data . filter ( issue  =>  ! issue . pull_request ) ; 
43+             issues . push ( ...onlyIssues ) ; 
44+             if  ( data . length  <  100 )  break ; 
45+             page ++ ; 
46+         }  catch  ( err )  { 
47+             console . error ( 'Error fetching issues:' ,  err ) ; 
48+             break ; 
49+         } 
50+     } 
51+     return  issues ; 
52+ } 
53+ 
54+ 
55+ async  function  migrateToDiscussion ( github ,  owner ,  repo ,  issue ,  categories )  { 
56+     const  discussionCategory  =  'Q&A' ; 
57+     try  { 
58+         const  category  =  categories . find ( cat  => 
59+             cat . name . toLowerCase ( )  ===  discussionCategory . toLowerCase ( ) 
60+         ) ; 
61+         if  ( ! category )  { 
62+             throw  new  Error ( `Discussion category '${ discussionCategory }  ) ; 
63+         } 
64+         const  {  data : discussion  }  =  await  github . rest . discussions . create ( { 
3265            owner, 
3366            repo, 
34-             state :  'open' , 
35-             per_page :  100 , 
36-             page , 
67+             title :  issue . title , 
68+             body :  `Originally created by @ ${ issue . user . login }  in # ${ issue . number } \n\n---\n\n ${ issue . body   ||   '' } ` , 
69+             category_id :  category . id , 
3770        } ) ; 
38- 
39-         const  data  =  response . data  ||  [ ] ; 
40-         if  ( data . length  ===  0 )  break ; 
41-         const  onlyIssues  =  data . filter ( issue  =>  ! issue . pull_request ) ; 
42-         issues . push ( ...onlyIssues ) ; 
43- 
44-         if  ( data . length  <  100 )  break ; 
45-         page ++ ; 
71+         await  github . rest . issues . createComment ( { 
72+             owner, 
73+             repo, 
74+             issue_number : issue . number , 
75+             body : `💬 This issue was moved to [Discussions](${ discussion . html_url }  , 
76+         } ) ; 
77+         await  github . rest . issues . update ( { 
78+             owner, 
79+             repo, 
80+             issue_number : issue . number , 
81+             state : 'closed' , 
82+         } ) ; 
83+         return  discussion . html_url ; 
84+     }  catch  ( err )  { 
85+         console . error ( `Error migrating issue #${ issue . number }  ,  err ) ; 
86+         return  null ; 
4687    } 
47-     return  issues ; 
4888} 
4989
90+ 
5091const  shouldSendReminder  =  ( issue ,  exemptLabels ,  closeLabels )  =>  { 
51-   const  hasExempt  =  issue . labels . some ( l  =>  exemptLabels . includes ( l . name ) ) ; 
52-   const  hasClose  =  issue . labels . some ( l  =>  closeLabels . includes ( l . name ) ) ; 
53-   return  issue . assignees . length  >  0  &&  ! hasExempt  &&  ! hasClose ; 
92+      const  hasExempt  =  issue . labels . some ( l  =>  exemptLabels . includes ( l . name ) ) ; 
93+      const  hasClose  =  issue . labels . some ( l  =>  closeLabels . includes ( l . name ) ) ; 
94+      return  issue . assignees . length  >  0  &&  ! hasExempt  &&  ! hasClose ; 
5495} ; 
5596
5697
5798module . exports  =  async  ( {  github,  context } )  =>  { 
99+     let  categories  =  [ ] ; 
100+     try  { 
101+         const  {  data }  =  await  github . rest . discussions . listCategories ( {  owner,  repo } ) ; 
102+         categories  =  data ; 
103+     }  catch  ( err )  { 
104+         console . error ( 'Error fetching discussion categories:' ,  err ) ; 
105+     } 
58106    const  {  owner,  repo }  =  context . repo ; 
59-     const  issues  =  await  fetchAllOpenIssues ( github ,  owner ,  repo ) ; 
107+     let  issues  =  [ ] ; 
108+     try  { 
109+         issues  =  await  fetchAllOpenIssues ( github ,  owner ,  repo ) ; 
110+     }  catch  ( err )  { 
111+         console . error ( 'Failed to fetch issues:' ,  err ) ; 
112+         return ; 
113+     } 
60114    const  now  =  new  Date ( ) ; 
61115    const  thresholdDays  =  90 ; 
62-     const  exemptLabels  =  [ 'to-be-discussed' ] ; 
63-     const  closeLabels  =  [ 'awaiting-response' ] ; 
116+     const  exemptLabels  =  [ 'Status: Community help needed' ,  'Status: Needs investigation' ] ; 
117+     const  closeLabels  =  [ 'Status: Awaiting Response' ] ; 
118+     const  discussionLabel  =  'Type: Question' ; 
64119    const  sevenDays  =  7  *  24  *  60  *  60  *  1000 ; 
65120
66121    let  totalClosed  =  0 ; 
67122    let  totalReminders  =  0 ; 
68123    let  totalSkipped  =  0 ; 
124+     let  totalMigrated  =  0 ;     
69125
70126    for  ( const  issue  of  issues )  { 
71127        const  isAssigned  =  issue . assignees  &&  issue . assignees . length  >  0 ; 
72128        const  lastUpdate  =  new  Date ( issue . updated_at ) ; 
73129        const  daysSinceUpdate  =  Math . floor ( ( now  -  lastUpdate )  /  ( 1000  *  60  *  60  *  24 ) ) ; 
74130
75-         if  ( daysSinceUpdate  <  thresholdDays )  { 
76-             totalSkipped ++ ; 
131+         if  ( issue . labels . some ( label  =>  label . name  ===  discussionLabel ) )  { 
132+             const  migrated  =  await  migrateToDiscussion ( github ,  owner ,  repo ,  issue ,  categories ) ; 
133+             if  ( migrated )  totalMigrated ++ ; 
77134            continue ; 
78135        } 
79136
80-         const  {  data : comments  }  =  await  github . rest . issues . listComments ( { 
81-             owner, 
82-             repo, 
83-             issue_number : issue . number , 
84-             per_page : 10 , 
85-         } ) ; 
86- 
87-         const  recentFriendlyReminder  =  comments . find ( comment  => 
88-             comment . user . login  ===  'github-actions[bot]'  && 
89-             comment . body . includes ( '⏰ Friendly Reminder' )  && 
90-             ( now  -  new  Date ( comment . created_at ) )  <  sevenDays 
91-         ) ; 
92-         if  ( recentFriendlyReminder )  { 
137+         if  ( daysSinceUpdate  <  thresholdDays )  { 
93138            totalSkipped ++ ; 
94139            continue ; 
95140        } 
@@ -100,19 +145,45 @@ module.exports = async ({ github, context }) => {
100145        } 
101146
102147        if  ( issue . labels . some ( label  =>  closeLabels . includes ( label . name ) )  ||  ! isAssigned )  { 
103-             await  github . rest . issues . createComment ( { 
104-                 owner, 
105-                 repo, 
106-                 issue_number : issue . number , 
107-                 body : '⚠️ This issue was closed automatically due to inactivity. Please reopen or open a new one if still relevant.' , 
108-             } ) ; 
109-             await  github . rest . issues . update ( { 
148+             try  { 
149+                 await  github . rest . issues . createComment ( { 
150+                     owner, 
151+                     repo, 
152+                     issue_number : issue . number , 
153+                     body : '⚠️ This issue was closed automatically due to inactivity. Please reopen or open a new one if still relevant.' , 
154+                 } ) ; 
155+                 await  github . rest . issues . update ( { 
156+                     owner, 
157+                     repo, 
158+                     issue_number : issue . number , 
159+                     state : 'closed' , 
160+                 } ) ; 
161+                 totalClosed ++ ; 
162+             }  catch  ( err )  { 
163+                 console . error ( `Error closing issue #${ issue . number }  ,  err ) ; 
164+             } 
165+             continue ; 
166+         } 
167+ 
168+         let  comments  =  [ ] ; 
169+         try  { 
170+             const  {  data }  =  await  github . rest . issues . listComments ( { 
110171                owner, 
111172                repo, 
112173                issue_number : issue . number , 
113-                 state :  'closed' , 
174+                 per_page :  50 , 
114175            } ) ; 
115-             totalClosed ++ ; 
176+             comments  =  data ; 
177+         }  catch  ( err )  { 
178+             console . error ( `Error fetching comments for issue #${ issue . number }  ,  err ) ; 
179+         } 
180+ 
181+         const  recentFriendlyReminder  =  comments . find ( comment  => 
182+             comment . user . login  ===  'github-actions[bot]'  && 
183+             comment . body . includes ( '⏰ Friendly Reminder' ) 
184+         ) ; 
185+         if  ( recentFriendlyReminder )  { 
186+             totalSkipped ++ ; 
116187            continue ; 
117188        } 
118189
@@ -129,14 +200,17 @@ module.exports = async ({ github, context }) => {
129200                - Or label it 'awaiting-response' if you're waiting on something 
130201
131202                This is just a reminder; the issue remains open for now.` ; 
132- 
133-             await  github . rest . issues . createComment ( { 
134-                 owner, 
135-                 repo, 
136-                 issue_number : issue . number , 
137-                 body : comment , 
138-             } ) ; 
139-             totalReminders ++ ; 
203+             try  { 
204+                 await  github . rest . issues . createComment ( { 
205+                     owner, 
206+                     repo, 
207+                     issue_number : issue . number , 
208+                     body : comment , 
209+                 } ) ; 
210+                 totalReminders ++ ; 
211+             }  catch  ( err )  { 
212+                 console . error ( `Error sending reminder for issue #${ issue . number }  ,  err ) ; 
213+             } 
140214        } 
141215    } 
142216
@@ -145,5 +219,6 @@ module.exports = async ({ github, context }) => {
145219        Total issues processed: ${ issues . length }  
146220        Total issues closed: ${ totalClosed }  
147221        Total reminders sent: ${ totalReminders }  
222+         Total migrated to discussions: ${ totalMigrated }  
148223        Total skipped: ${ totalSkipped }  ) ; 
149224} ; 
0 commit comments