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,113 @@ 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 } )  =>  { 
58-     const  {  owner,  repo }  =  context . repo ; 
59-     const  issues  =  await  fetchAllOpenIssues ( github ,  owner ,  repo ) ; 
6099    const  now  =  new  Date ( ) ; 
61100    const  thresholdDays  =  90 ; 
62-     const  exemptLabels  =  [ 'to-be-discussed' ] ; 
63-     const  closeLabels  =  [ 'awaiting-response' ] ; 
64-     const  sevenDays  =  7  *  24  *  60  *  60  *  1000 ; 
101+     const  exemptLabels  =  [ 'Status: Community help needed' ,  'Status: Needs investigation' ] ; 
102+     const  closeLabels  =  [ 'Status: Awaiting Response' ] ; 
103+     const  discussionLabel  =  'Type: Question' ; 
104+     const  {  owner,  repo }  =  context . repo ; 
65105
66106    let  totalClosed  =  0 ; 
67107    let  totalReminders  =  0 ; 
68108    let  totalSkipped  =  0 ; 
109+     let  totalMigrated  =  0 ; 
110+     let  issues  =  [ ] ; 
111+     let  categories  =  [ ] ; 
112+ 
113+     try  { 
114+         issues  =  await  fetchAllOpenIssues ( github ,  owner ,  repo ) ; 
115+     }  catch  ( err )  { 
116+         console . error ( 'Failed to fetch issues:' ,  err ) ; 
117+         return ; 
118+     } 
119+ 
120+     try  { 
121+         const  {  data }  =  await  github . rest . discussions . listCategories ( {  owner,  repo } ) ; 
122+         categories  =  data ; 
123+     }  catch  ( err )  { 
124+         console . error ( 'Error fetching discussion categories:' ,  err ) ; 
125+     } 
69126
70127    for  ( const  issue  of  issues )  { 
71128        const  isAssigned  =  issue . assignees  &&  issue . assignees . length  >  0 ; 
72129        const  lastUpdate  =  new  Date ( issue . updated_at ) ; 
73130        const  daysSinceUpdate  =  Math . floor ( ( now  -  lastUpdate )  /  ( 1000  *  60  *  60  *  24 ) ) ; 
74131
75-         if  ( daysSinceUpdate  <  thresholdDays )  { 
76-             totalSkipped ++ ; 
132+         if  ( issue . labels . some ( label  =>  label . name  ===  discussionLabel ) )  { 
133+             const  migrated  =  await  migrateToDiscussion ( github ,  owner ,  repo ,  issue ,  categories ) ; 
134+             if  ( migrated )  totalMigrated ++ ; 
77135            continue ; 
78136        } 
79137
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 )  { 
138+         if  ( daysSinceUpdate  <  thresholdDays )  { 
93139            totalSkipped ++ ; 
94140            continue ; 
95141        } 
@@ -100,19 +146,45 @@ module.exports = async ({ github, context }) => {
100146        } 
101147
102148        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 ( { 
149+             try  { 
150+                 await  github . rest . issues . createComment ( { 
151+                     owner, 
152+                     repo, 
153+                     issue_number : issue . number , 
154+                     body : '⚠️ This issue was closed automatically due to inactivity. Please reopen or open a new one if still relevant.' , 
155+                 } ) ; 
156+                 await  github . rest . issues . update ( { 
157+                     owner, 
158+                     repo, 
159+                     issue_number : issue . number , 
160+                     state : 'closed' , 
161+                 } ) ; 
162+                 totalClosed ++ ; 
163+             }  catch  ( err )  { 
164+                 console . error ( `Error closing issue #${ issue . number }  ,  err ) ; 
165+             } 
166+             continue ; 
167+         } 
168+ 
169+         let  comments  =  [ ] ; 
170+         try  { 
171+             const  {  data }  =  await  github . rest . issues . listComments ( { 
110172                owner, 
111173                repo, 
112174                issue_number : issue . number , 
113-                 state :  'closed' , 
175+                 per_page :  50 , 
114176            } ) ; 
115-             totalClosed ++ ; 
177+             comments  =  data ; 
178+         }  catch  ( err )  { 
179+             console . error ( `Error fetching comments for issue #${ issue . number }  ,  err ) ; 
180+         } 
181+ 
182+         const  recentFriendlyReminder  =  comments . find ( comment  => 
183+             comment . user . login  ===  'github-actions[bot]'  && 
184+             comment . body . includes ( '⏰ Friendly Reminder' ) 
185+         ) ; 
186+         if  ( recentFriendlyReminder )  { 
187+             totalSkipped ++ ; 
116188            continue ; 
117189        } 
118190
@@ -129,14 +201,17 @@ module.exports = async ({ github, context }) => {
129201                - Or label it 'awaiting-response' if you're waiting on something 
130202
131203                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 ++ ; 
204+             try  { 
205+                 await  github . rest . issues . createComment ( { 
206+                     owner, 
207+                     repo, 
208+                     issue_number : issue . number , 
209+                     body : comment , 
210+                 } ) ; 
211+                 totalReminders ++ ; 
212+             }  catch  ( err )  { 
213+                 console . error ( `Error sending reminder for issue #${ issue . number }  ,  err ) ; 
214+             } 
140215        } 
141216    } 
142217
@@ -145,5 +220,6 @@ module.exports = async ({ github, context }) => {
145220        Total issues processed: ${ issues . length }  
146221        Total issues closed: ${ totalClosed }  
147222        Total reminders sent: ${ totalReminders }  
223+         Total migrated to discussions: ${ totalMigrated }  
148224        Total skipped: ${ totalSkipped }  ) ; 
149225} ; 
0 commit comments