1
+ use crate :: db:: schedule_job;
1
2
use crate :: github;
2
3
use crate :: jobs:: Job ;
3
4
use crate :: zulip:: BOT_EMAIL ;
4
5
use crate :: zulip:: { to_zulip_id, MembersApiResponse } ;
5
6
use anyhow:: { format_err, Context as _} ;
6
7
use async_trait:: async_trait;
7
- use chrono:: { Duration , Utc } ;
8
+ use chrono:: { Datelike , Duration , NaiveTime , TimeZone , Utc } ;
9
+ use serde:: { Deserialize , Serialize } ;
8
10
9
- pub struct TypesPlanningUpdatesJob ;
11
+ const TYPES_REPO : & ' static str = "rust-lang/types-team" ;
12
+
13
+ pub struct TypesPlanningMeetingThreadOpenJob ;
10
14
11
15
#[ async_trait]
12
- impl Job for TypesPlanningUpdatesJob {
16
+ impl Job for TypesPlanningMeetingThreadOpenJob {
13
17
fn name ( & self ) -> & ' static str {
14
- "types_planning_updates "
18
+ "types_planning_meeting_thread_open "
15
19
}
16
20
17
21
async fn run ( & self , ctx : & super :: Context , _metadata : & serde_json:: Value ) -> anyhow:: Result < ( ) > {
18
- request_updates ( ctx) . await ?;
22
+ // On the last week of the month, we open a thread on zulip for the next Monday
23
+ let today = chrono:: Utc :: now ( ) . date ( ) . naive_utc ( ) ;
24
+ let first_monday = today + chrono:: Duration :: days ( 7 ) ;
25
+ let meeting_date_string = first_monday. format ( "%Y-%m-%d" ) . to_string ( ) ;
26
+ let message = format ! ( "\
27
+ Hello @*T-types/meetings*. Monthly planning meeting in one week.\n \
28
+ This is a reminder to update the current [roadmap tracking issues](https://github.com/rust-lang/types-team/issues?q=is%3Aissue+is%3Aopen+label%3Aroadmap-tracking-issue).\n \
29
+ Extra reminders will be sent later this week.") ;
30
+ let zulip_req = crate :: zulip:: MessageApiRequest {
31
+ recipient : crate :: zulip:: Recipient :: Stream {
32
+ id : 326132 ,
33
+ topic : & format ! ( "{meeting_date_string} planning meeting" ) ,
34
+ } ,
35
+ content : & message,
36
+ } ;
37
+ zulip_req. send ( & ctx. github . raw ( ) ) . await ?;
38
+
39
+ // Then, we want to schedule the next Thursday after this
40
+ let mut thursday = today;
41
+ while thursday. weekday ( ) . num_days_from_monday ( ) != 3 {
42
+ thursday = thursday. succ ( ) ;
43
+ }
44
+ let thursday_at_noon =
45
+ Utc . from_utc_datetime ( & thursday. and_time ( NaiveTime :: from_hms ( 12 , 0 , 0 ) ) ) ;
46
+ let metadata = serde_json:: value:: to_value ( PlanningMeetingUpdatesPingMetadata {
47
+ date_string : meeting_date_string,
48
+ } )
49
+ . unwrap ( ) ;
50
+ schedule_job (
51
+ & * ctx. db . get ( ) . await ,
52
+ TypesPlanningMeetingUpdatesPing . name ( ) ,
53
+ metadata,
54
+ thursday_at_noon,
55
+ )
56
+ . await ?;
57
+
19
58
Ok ( ( ) )
20
59
}
21
60
}
22
61
23
- const TYPES_REPO : & ' static str = "rust-lang/types-team" ;
62
+ #[ derive( Serialize , Deserialize ) ]
63
+ pub struct PlanningMeetingUpdatesPingMetadata {
64
+ pub date_string : String ,
65
+ }
66
+
67
+ pub struct TypesPlanningMeetingUpdatesPing ;
68
+
69
+ #[ async_trait]
70
+ impl Job for TypesPlanningMeetingUpdatesPing {
71
+ fn name ( & self ) -> & ' static str {
72
+ "types_planning_meeting_updates_ping"
73
+ }
74
+
75
+ async fn run ( & self , ctx : & super :: Context , metadata : & serde_json:: Value ) -> anyhow:: Result < ( ) > {
76
+ let metadata = serde_json:: from_value ( metadata. clone ( ) ) ?;
77
+ // On the thursday before the first monday, we want to ping for updates
78
+ request_updates ( ctx, metadata) . await ?;
79
+ Ok ( ( ) )
80
+ }
81
+ }
24
82
25
- pub async fn request_updates ( ctx : & super :: Context ) -> anyhow:: Result < ( ) > {
83
+ pub async fn request_updates (
84
+ ctx : & super :: Context ,
85
+ metadata : PlanningMeetingUpdatesPingMetadata ,
86
+ ) -> anyhow:: Result < ( ) > {
26
87
let gh = & ctx. github ;
27
88
let types_repo = gh. repository ( TYPES_REPO ) . await ?;
28
89
@@ -36,19 +97,28 @@ pub async fn request_updates(ctx: &super::Context) -> anyhow::Result<()> {
36
97
. await
37
98
. with_context ( || "Unable to get issues." ) ?;
38
99
100
+ let mut issues_needs_updates = vec ! [ ] ;
39
101
for issue in issues {
102
+ // Github doesn't have a nice way to get the *last* comment; we would have to paginate all comments to get it.
103
+ // For now, just bail out if there are more than 100 comments (if this ever becomes a problem, we will have to fix).
40
104
let comments = issue. get_first100_comments ( gh) . await ?;
41
105
if comments. len ( ) >= 100 {
42
106
anyhow:: bail!(
43
107
"Encountered types tracking issue with 100 or more comments; needs implementation."
44
108
) ;
45
109
}
46
- let older_than_28_days = comments
110
+
111
+ // If there are any comments in the past 7 days, we consider this "updated". We *could* be more clever, but
112
+ // this is fine under the assumption that tracking issues should only contain updates.
113
+ let older_than_7_days = comments
47
114
. last ( )
48
- . map_or ( true , |c| c. updated_at < ( Utc :: now ( ) - Duration :: days ( 28 ) ) ) ;
49
- if !older_than_28_days {
115
+ . map_or ( true , |c| c. updated_at < ( Utc :: now ( ) - Duration :: days ( 7 ) ) ) ;
116
+ if !older_than_7_days {
50
117
continue ;
51
118
}
119
+ // In the future, we should reach out to specific people in charge of specific issues. For now, because our tracking
120
+ // method is crude and will over-estimate the issues that need updates.
121
+ /*
52
122
let mut dmed_assignee = false;
53
123
for assignee in issue.assignees {
54
124
let zulip_id_and_email = zulip_id_and_email(ctx, assignee.id.unwrap()).await?;
@@ -85,11 +155,28 @@ pub async fn request_updates(ctx: &super::Context) -> anyhow::Result<()> {
85
155
};
86
156
zulip_req.send(&ctx.github.raw()).await?;
87
157
}
158
+ */
159
+ issues_needs_updates. push ( format ! ( "- [Issue #{}]({})" , issue. number, issue. html_url) ) ;
88
160
}
89
161
162
+ let issue_list = issues_needs_updates. join ( "\n " ) ;
163
+
164
+ let message = format ! ( "The following issues still need updates:\n \n {issue_list}" ) ;
165
+
166
+ let meeting_date_string = metadata. date_string ;
167
+ let zulip_req = crate :: zulip:: MessageApiRequest {
168
+ recipient : crate :: zulip:: Recipient :: Stream {
169
+ id : 326132 ,
170
+ topic : & format ! ( "{meeting_date_string} planning meeting" ) ,
171
+ } ,
172
+ content : & message,
173
+ } ;
174
+ zulip_req. send ( & ctx. github . raw ( ) ) . await ?;
175
+
90
176
Ok ( ( ) )
91
177
}
92
178
179
+ #[ allow( unused) ] // Needed for commented out bit above
93
180
async fn zulip_id_and_email (
94
181
ctx : & super :: Context ,
95
182
github_id : i64 ,
0 commit comments