@access-mcp/announcements 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,166 +4,91 @@ MCP server providing access to ACCESS support announcements, service updates, ma
4
4
 
5
5
  ## Usage Examples
6
6
 
7
- ### **Stay Updated on Recent Activity**
7
+ ### **Recent Updates**
8
8
 
9
9
  ```
10
- "What's new with ACCESS?"
11
- "Show me recent announcements"
12
- "Any updates from ACCESS Support this week?"
13
- "What are the latest community announcements?"
10
+ "Recent ACCESS announcements"
11
+ "Updates from past week"
12
+ "Latest community news"
13
+ "Announcements since January 2024"
14
14
  ```
15
15
 
16
- ### **Monitor System Maintenance & Issues**
16
+ ### **System & Maintenance**
17
17
 
18
18
  ```
19
- "Are there any GPU maintenance announcements?"
20
- "Show me maintenance notices for DELTA"
21
- "What system updates have been posted recently?"
22
- "Any announcements about Bridges-2 issues?"
23
- "Tell me about network-related announcements"
19
+ "GPU maintenance announcements"
20
+ "Delta maintenance notices"
21
+ "Bridges-2 system updates"
22
+ "Network-related announcements"
24
23
  ```
25
24
 
26
- ### **Find Training & Educational Content**
25
+ ### **By Topic**
27
26
 
28
27
  ```
29
- "Are there any training workshops announced?"
30
- "Show me machine learning training announcements"
31
- "What professional development opportunities are available?"
32
- "Any upcoming webinars or educational events?"
33
- ```
34
-
35
- ### **Track Specific Systems or Communities**
36
-
37
- ```
38
- "Show me all announcements about Anvil"
39
- "What updates are there for the CSSN community?"
40
- "Any news from the Open OnDemand team?"
41
- "Show me Pegasus-related announcements"
42
- ```
43
-
44
- ### **Search by Topic or Technology**
45
-
46
- ```
47
- "Find announcements about cloud computing"
48
- "Show me AI and machine learning updates"
49
- "Any announcements about allocation proposals?"
50
- "What's new with data science resources?"
51
- "Find announcements about Python or PyTorch"
52
- ```
53
-
54
- ### **Time-Based Searches**
55
-
56
- ```
57
- "Show me announcements from the past month"
58
- "What was announced last week?"
59
- "Find announcements since January 1st, 2024"
60
- "Show me announcements from the past 6 months"
28
+ "Training workshops announced"
29
+ "Machine learning updates"
30
+ "Cloud computing announcements"
31
+ "Allocation proposal news"
32
+ "Python and PyTorch updates"
61
33
  ```
62
34
 
63
35
  ## Tools
64
36
 
65
- ### get_announcements
37
+ ### search_announcements
66
38
 
67
- Search ACCESS support announcements with comprehensive filtering options.
39
+ Search and filter ACCESS support announcements with flexible filtering options.
68
40
 
69
41
  **Parameters:**
70
42
  - `tags` - Filter by topics (comma-separated). Examples: 'gpu,nvidia', 'machine-learning,ai', 'training,workshop'
71
- - `ag` - Filter by system/community group. Examples: 'DELTA', 'Anvil', 'ACCESS Support', 'CSSN (Computational Science Support Network)'
72
- - `relative_start_date` - Filter from relative date. Examples: 'today', '-1 week', '-1 month', '-3 months'
43
+ - `ag` - Filter by system/community group. Examples: 'Anvil', 'ACCESS Support', 'DARWIN', 'Stampede-3'
44
+ - `relative_start_date` - Filter from relative date. Examples: 'today', '-1 week', '-1 month', '-1 year'
73
45
  - `relative_end_date` - Filter to relative date. Examples: 'now', 'today', '-1 week', '+1 week'
74
46
  - `start_date` - Filter from exact date (YYYY-MM-DD). Example: '2024-01-01'
75
47
  - `end_date` - Filter to exact date (YYYY-MM-DD). Example: '2024-12-31'
76
- - `limit` - Maximum results (default: 20)
48
+ - `limit` - Maximum results (default: 25). Automatically rounded to valid API page sizes (5, 10, 25, or 50)
77
49
 
78
50
  **Usage Examples:**
79
51
  ```javascript
80
- // Recent GPU-related announcements
81
- get_announcements({
82
- tags: "gpu,nvidia",
52
+ // Recent announcements
53
+ search_announcements({
83
54
  relative_start_date: "-1 month",
84
55
  limit: 10
85
56
  })
86
57
 
87
- // DELTA system announcements from this year
88
- get_announcements({
89
- ag: "DELTA",
90
- start_date: "2024-01-01",
91
- limit: 15
92
- })
93
-
94
- // Training announcements from the past 3 months
95
- get_announcements({
96
- tags: "training,professional-development",
97
- relative_start_date: "-3 months",
98
- limit: 20
58
+ // Topic-specific search - GPU announcements
59
+ search_announcements({
60
+ tags: "gpu,nvidia",
61
+ relative_start_date: "-1 year",
62
+ limit: 25
99
63
  })
100
- ```
101
-
102
- ### get_announcements_by_tags
103
-
104
- Find announcements about specific topics or systems.
105
-
106
- **Parameters:**
107
- - `tags` (required) - Specific topics (comma-separated). Examples: 'gpu,nvidia', 'data-science,python', 'cloud-computing'
108
- - `limit` - Maximum results (default: 10)
109
64
 
110
- **Usage Examples:**
111
- ```javascript
112
- // Machine learning and AI announcements
113
- get_announcements_by_tags({
114
- tags: "machine-learning,ai,deep-learning",
65
+ // System-specific search - Anvil announcements
66
+ search_announcements({
67
+ ag: "Anvil",
68
+ relative_start_date: "-6 months",
115
69
  limit: 15
116
70
  })
117
71
 
118
- // Cloud computing updates
119
- get_announcements_by_tags({
120
- tags: "cloud-computing,openstack",
121
- limit: 10
122
- })
123
- ```
124
-
125
- ### get_announcements_by_affinity_group
126
-
127
- Get announcements for specific ACCESS systems or community groups.
128
-
129
- **Parameters:**
130
- - `ag` (required) - System or community name. Examples: 'DELTA', 'Anvil', 'Bridges-2', 'ACCESS Support', 'Open OnDemand'
131
- - `limit` - Maximum results (default: 10)
132
-
133
- **Usage Examples:**
134
- ```javascript
135
- // All DELTA-related announcements
136
- get_announcements_by_affinity_group({
137
- ag: "DELTA",
72
+ // Machine learning announcements
73
+ search_announcements({
74
+ tags: "machine-learning,ai",
75
+ relative_start_date: "-1 year",
138
76
  limit: 20
139
77
  })
140
78
 
141
- // Open OnDemand community updates
142
- get_announcements_by_affinity_group({
143
- ag: "Open OnDemand",
79
+ // Combined filters - recent announcements for ACCESS Support
80
+ search_announcements({
81
+ ag: "ACCESS Support",
82
+ tags: "maintenance",
83
+ relative_start_date: "-3 months",
144
84
  limit: 10
145
85
  })
146
- ```
147
-
148
- ### get_recent_announcements
149
-
150
- Get the latest announcements from a recent time period.
151
86
 
152
- **Parameters:**
153
- - `period` - Time period to look back. Examples: '1 week', '2 weeks', '1 month', '3 months' (default: '1 month')
154
- - `limit` - Maximum results (default: 10)
155
-
156
- **Usage Examples:**
157
- ```javascript
158
- // Past week's announcements
159
- get_recent_announcements({
160
- period: "1 week",
161
- limit: 15
162
- })
163
-
164
- // Past 3 months overview
165
- get_recent_announcements({
166
- period: "3 months",
87
+ // Exact date range search
88
+ search_announcements({
89
+ tags: "training,professional-development",
90
+ start_date: "2024-01-01",
91
+ end_date: "2024-12-31",
167
92
  limit: 25
168
93
  })
169
94
  ```
package/dist/index.js CHANGED
@@ -1,24 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { AnnouncementsServer } from "./server.js";
3
3
  async function main() {
4
- // Check if we should run as HTTP server (for deployment)
5
- const port = process.env.PORT;
6
4
  const server = new AnnouncementsServer();
7
- if (port) {
8
- // Running in HTTP mode (deployment)
9
- await server.start({ httpPort: parseInt(port) });
10
- // Keep the process running in HTTP mode
11
- process.on('SIGINT', () => {
12
- console.log('Shutting down server...');
13
- process.exit(0);
14
- });
15
- // Keep the event loop alive
16
- setInterval(() => { }, 1000 * 60 * 60); // Heartbeat every hour
17
- }
18
- else {
19
- // Running in MCP mode (stdio)
20
- await server.start();
21
- }
5
+ const port = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
6
+ await server.start(port ? { httpPort: port } : undefined);
22
7
  }
23
8
  main().catch((error) => {
24
9
  // Log errors to stderr and exit
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BaseAccessServer } from "@access-mcp/shared";
2
2
  export declare class AnnouncementsServer extends BaseAccessServer {
3
3
  constructor();
4
- protected getTools(): ({
4
+ protected getTools(): {
5
5
  name: string;
6
6
  description: string;
7
7
  inputSchema: {
@@ -10,12 +10,10 @@ export declare class AnnouncementsServer extends BaseAccessServer {
10
10
  tags: {
11
11
  type: string;
12
12
  description: string;
13
- required?: undefined;
14
13
  };
15
14
  ag: {
16
15
  type: string;
17
16
  description: string;
18
- required?: undefined;
19
17
  };
20
18
  affiliation: {
21
19
  type: string;
@@ -32,99 +30,46 @@ export declare class AnnouncementsServer extends BaseAccessServer {
32
30
  start_date: {
33
31
  type: string;
34
32
  description: string;
33
+ format: string;
35
34
  };
36
35
  end_date: {
37
36
  type: string;
38
37
  description: string;
38
+ format: string;
39
39
  };
40
40
  limit: {
41
41
  type: string;
42
42
  description: string;
43
43
  default: number;
44
44
  };
45
- period?: undefined;
46
45
  };
47
- required?: undefined;
48
- };
49
- } | {
50
- name: string;
51
- description: string;
52
- inputSchema: {
53
- type: string;
54
- properties: {
55
- tags: {
56
- type: string;
57
- description: string;
58
- required: boolean;
46
+ examples: ({
47
+ name: string;
48
+ arguments: {
49
+ relative_start_date: string;
50
+ limit: number;
51
+ tags?: undefined;
52
+ ag?: undefined;
59
53
  };
60
- limit: {
61
- type: string;
62
- description: string;
63
- default: number;
54
+ } | {
55
+ name: string;
56
+ arguments: {
57
+ tags: string;
58
+ relative_start_date: string;
59
+ limit: number;
60
+ ag?: undefined;
64
61
  };
65
- ag?: undefined;
66
- affiliation?: undefined;
67
- relative_start_date?: undefined;
68
- relative_end_date?: undefined;
69
- start_date?: undefined;
70
- end_date?: undefined;
71
- period?: undefined;
72
- };
73
- required: string[];
74
- };
75
- } | {
76
- name: string;
77
- description: string;
78
- inputSchema: {
79
- type: string;
80
- properties: {
81
- ag: {
82
- type: string;
83
- description: string;
84
- required: boolean;
62
+ } | {
63
+ name: string;
64
+ arguments: {
65
+ ag: string;
66
+ relative_start_date: string;
67
+ limit: number;
68
+ tags?: undefined;
85
69
  };
86
- limit: {
87
- type: string;
88
- description: string;
89
- default: number;
90
- };
91
- tags?: undefined;
92
- affiliation?: undefined;
93
- relative_start_date?: undefined;
94
- relative_end_date?: undefined;
95
- start_date?: undefined;
96
- end_date?: undefined;
97
- period?: undefined;
98
- };
99
- required: string[];
100
- };
101
- } | {
102
- name: string;
103
- description: string;
104
- inputSchema: {
105
- type: string;
106
- properties: {
107
- period: {
108
- type: string;
109
- description: string;
110
- default: string;
111
- };
112
- limit: {
113
- type: string;
114
- description: string;
115
- default: number;
116
- };
117
- tags?: undefined;
118
- ag?: undefined;
119
- affiliation?: undefined;
120
- relative_start_date?: undefined;
121
- relative_end_date?: undefined;
122
- start_date?: undefined;
123
- end_date?: undefined;
124
- };
125
- required?: undefined;
70
+ })[];
126
71
  };
127
- })[];
72
+ }[];
128
73
  protected getResources(): {
129
74
  uri: string;
130
75
  name: string;
@@ -144,13 +89,11 @@ export declare class AnnouncementsServer extends BaseAccessServer {
144
89
  text: string;
145
90
  }[];
146
91
  }>;
92
+ private normalizeLimit;
147
93
  private buildAnnouncementsUrl;
148
94
  private fetchAnnouncements;
149
95
  private enhanceAnnouncements;
150
- private getAnnouncements;
151
- private getAnnouncementsByTags;
152
- private getAnnouncementsByAffinityGroup;
153
- private getRecentAnnouncements;
96
+ private searchAnnouncements;
154
97
  private getPopularTags;
155
98
  private getAffinityGroups;
156
99
  }
package/dist/server.js CHANGED
@@ -6,8 +6,8 @@ export class AnnouncementsServer extends BaseAccessServer {
6
6
  getTools() {
7
7
  return [
8
8
  {
9
- name: "get_announcements",
10
- description: "Search ACCESS support announcements about system maintenance, service updates, outages, and community news. Use this when users ask about recent announcements, service status, or need to filter by dates, tags, or affinity groups.",
9
+ name: "search_announcements",
10
+ description: "Search ACCESS announcements - general news, updates, and notifications from the community. Use this for news ABOUT things (e.g., 'registration deadline approaching', 'new feature released', 'policy change'). For actual training/workshop schedules and details, use search_events. For current system outages/maintenance, use infrastructure status tools. Note: Use relative_start_date='-1 year' or similar for broader search if recent results are limited.",
11
11
  inputSchema: {
12
12
  type: "object",
13
13
  properties: {
@@ -17,7 +17,7 @@ export class AnnouncementsServer extends BaseAccessServer {
17
17
  },
18
18
  ag: {
19
19
  type: "string",
20
- description: "Filter by system/community affinity group. Real examples: 'ACCESS Support', 'DELTA', 'Anvil', 'Bridges-2', 'CSSN (Computational Science Support Network)', 'Open OnDemand', 'Pegasus'"
20
+ description: "Filter by system/community affinity group (exact name match). Working examples: 'Anvil', 'ACCESS Support', 'DARWIN', 'Stampede-3'. Note: Use exact display names as they appear in announcements"
21
21
  },
22
22
  affiliation: {
23
23
  type: "string",
@@ -25,85 +25,61 @@ export class AnnouncementsServer extends BaseAccessServer {
25
25
  },
26
26
  relative_start_date: {
27
27
  type: "string",
28
- description: "Filter announcements from a relative date. Examples: 'today', '-1 week', '-1 month', '-3 months', '-1 year'. Use negative values for past dates"
28
+ description: "Filter announcements from a relative date. Examples: 'today', '-1 week', '-1 month', '-3 months', '-1 year'. Use negative values for past dates. Common: '-1 month' for recent announcements"
29
29
  },
30
30
  relative_end_date: {
31
31
  type: "string",
32
- description: "Filter announcements up to a relative date. Examples: 'now', 'today', '-1 week' (past), '+1 week' (future)"
32
+ description: "Filter announcements up to a relative date. Examples: 'now', 'today', '-1 week' (past), '+1 week' (future). Usually 'now' or omit for current time"
33
33
  },
34
34
  start_date: {
35
35
  type: "string",
36
- description: "Filter announcements from exact date onwards (YYYY-MM-DD). Use when users specify dates like 'since January 1st, 2024' or 'from March 15th'"
36
+ description: "Filter announcements from exact date onwards (YYYY-MM-DD). Use when users specify dates like 'since January 1st, 2024' or 'from March 15th'",
37
+ format: "date"
37
38
  },
38
39
  end_date: {
39
40
  type: "string",
40
- description: "Filter announcements up to exact date (YYYY-MM-DD). Use when users specify dates like 'until December 31st, 2024' or 'before April 1st'"
41
+ description: "Filter announcements up to exact date (YYYY-MM-DD). Use when users specify dates like 'until December 31st, 2024' or 'before April 1st'",
42
+ format: "date"
41
43
  },
42
44
  limit: {
43
45
  type: "number",
44
- description: "Maximum number of announcements to return. Use smaller values (5-10) for quick overviews, larger values (20-50) for comprehensive searches",
45
- default: 20
46
- }
47
- }
48
- }
49
- },
50
- {
51
- name: "get_announcements_by_tags",
52
- description: "Find announcements about specific topics or systems. Use when users ask about particular subjects like 'GPU maintenance', 'machine learning resources', 'training workshops', or 'cloud computing updates'.",
53
- inputSchema: {
54
- type: "object",
55
- properties: {
56
- tags: {
57
- type: "string",
58
- description: "Specific topics to search for (comma-separated). Popular examples: 'gpu,nvidia', 'machine-learning,ai', 'training,professional-development', 'data-science,python', 'cloud-computing', 'allocations-proposal'",
59
- required: true
60
- },
61
- limit: {
62
- type: "number",
63
- description: "Maximum number of announcements to return",
64
- default: 10
46
+ description: "Maximum number of announcements to return. Requested limit will be rounded to nearest valid API page size (5, 10, 25, or 50). Use 5-10 for quick overviews, 25-50 for comprehensive searches.",
47
+ default: 25
65
48
  }
66
49
  },
67
- required: ["tags"]
68
- }
69
- },
70
- {
71
- name: "get_announcements_by_affinity_group",
72
- description: "Get announcements for a specific ACCESS system or community group. Use when users ask about updates for particular resources like DELTA, Anvil, or Bridges-2, or community groups like CSSN.",
73
- inputSchema: {
74
- type: "object",
75
- properties: {
76
- ag: {
77
- type: "string",
78
- description: "The system or community group name (not the technical API ID). Examples: 'DELTA', 'Anvil', 'Bridges-2', 'ACCESS Support', 'Open OnDemand', 'Pegasus', 'CSSN (Computational Science Support Network)'",
79
- required: true
50
+ examples: [
51
+ {
52
+ name: "Recent announcements",
53
+ arguments: {
54
+ relative_start_date: "-1 month",
55
+ limit: 10
56
+ }
80
57
  },
81
- limit: {
82
- type: "number",
83
- description: "Maximum number of announcements to return",
84
- default: 10
85
- }
86
- },
87
- required: ["ag"]
88
- }
89
- },
90
- {
91
- name: "get_recent_announcements",
92
- description: "Get the latest ACCESS support announcements from a recent time period. Use this when users ask 'what's new?', 'recent updates', 'latest announcements', or want a general overview of current activity.",
93
- inputSchema: {
94
- type: "object",
95
- properties: {
96
- period: {
97
- type: "string",
98
- description: "How far back to look for announcements. Examples: '1 week', '2 weeks', '1 month', '3 months', '6 months'. Default is '1 month'",
99
- default: "1 month"
58
+ {
59
+ name: "GPU-related announcements",
60
+ arguments: {
61
+ tags: "gpu,nvidia",
62
+ relative_start_date: "-1 year",
63
+ limit: 25
64
+ }
100
65
  },
101
- limit: {
102
- type: "number",
103
- description: "Maximum number of announcements to return",
104
- default: 10
66
+ {
67
+ name: "Anvil system announcements",
68
+ arguments: {
69
+ ag: "Anvil",
70
+ relative_start_date: "-6 months",
71
+ limit: 15
72
+ }
73
+ },
74
+ {
75
+ name: "Machine learning announcements",
76
+ arguments: {
77
+ tags: "machine-learning,ai",
78
+ relative_start_date: "-1 year",
79
+ limit: 20
80
+ }
105
81
  }
106
- }
82
+ ]
107
83
  }
108
84
  }
109
85
  ];
@@ -122,14 +98,8 @@ export class AnnouncementsServer extends BaseAccessServer {
122
98
  const { name, arguments: args } = request.params;
123
99
  try {
124
100
  switch (name) {
125
- case "get_announcements":
126
- return await this.getAnnouncements(args);
127
- case "get_announcements_by_tags":
128
- return await this.getAnnouncementsByTags(args.tags, args.limit);
129
- case "get_announcements_by_affinity_group":
130
- return await this.getAnnouncementsByAffinityGroup(args.ag, args.limit);
131
- case "get_recent_announcements":
132
- return await this.getRecentAnnouncements(args.period, args.limit);
101
+ case "search_announcements":
102
+ return await this.searchAnnouncements(args);
133
103
  default:
134
104
  throw new Error(`Unknown tool: ${name}`);
135
105
  }
@@ -174,8 +144,24 @@ export class AnnouncementsServer extends BaseAccessServer {
174
144
  }
175
145
  throw new Error(`Unknown resource: ${uri}`);
176
146
  }
147
+ normalizeLimit(limit) {
148
+ // Drupal API only accepts specific pagination values: 5, 10, 25, 50
149
+ const validSizes = [5, 10, 25, 50];
150
+ const requestedLimit = limit || 25; // Default to 25 (valid API value)
151
+ // Find the closest valid size
152
+ if (requestedLimit <= 5)
153
+ return 5;
154
+ if (requestedLimit <= 10)
155
+ return 10;
156
+ if (requestedLimit <= 25)
157
+ return 25;
158
+ return 50;
159
+ }
177
160
  buildAnnouncementsUrl(filters) {
178
161
  const params = new URLSearchParams();
162
+ // Add required pagination parameter for 2.2 API
163
+ // The Drupal API only accepts: 5, 10, 25, or 50
164
+ params.append("items_per_page", String(this.normalizeLimit(filters.limit)));
179
165
  if (filters.tags) {
180
166
  params.append("tags", filters.tags);
181
167
  }
@@ -197,7 +183,7 @@ export class AnnouncementsServer extends BaseAccessServer {
197
183
  if (filters.end_date) {
198
184
  params.append("end_date", filters.end_date);
199
185
  }
200
- return `/api/2.1/announcements?${params.toString()}`;
186
+ return `/api/2.2/announcements?${params.toString()}`;
201
187
  }
202
188
  async fetchAnnouncements(filters) {
203
189
  const url = this.buildAnnouncementsUrl(filters);
@@ -211,26 +197,23 @@ export class AnnouncementsServer extends BaseAccessServer {
211
197
  enhanceAnnouncements(rawAnnouncements) {
212
198
  return rawAnnouncements.map(announcement => ({
213
199
  ...announcement,
214
- date: announcement.field_published_date,
215
- tags: announcement.custom_announcement_tags
216
- ? announcement.custom_announcement_tags.split(',').map((t) => t.trim()).filter((t) => t)
217
- : [],
218
- formatted_date: announcement.field_published_date
219
- ? new Date(announcement.field_published_date).toLocaleDateString('en-US', {
200
+ date: announcement.published_date,
201
+ tags: Array.isArray(announcement.tags) ? announcement.tags : [],
202
+ formatted_date: announcement.published_date
203
+ ? new Date(announcement.published_date).toLocaleDateString('en-US', {
220
204
  year: 'numeric',
221
205
  month: 'long',
222
206
  day: 'numeric'
223
207
  })
224
208
  : '',
225
- body_preview: announcement.body
226
- ? announcement.body.replace(/<[^>]*>/g, '').substring(0, 200) + '...'
227
- : '',
228
- affinity_groups: announcement.custom_announcement_ag
229
- ? announcement.custom_announcement_ag.split(',').map((g) => g.trim()).filter((g) => g)
230
- : []
209
+ body_preview: announcement.summary ||
210
+ (announcement.body
211
+ ? announcement.body.replace(/<[^>]*>/g, '').substring(0, 200) + '...'
212
+ : ''),
213
+ affinity_groups: Array.isArray(announcement.affinity_group) ? announcement.affinity_group : []
231
214
  }));
232
215
  }
233
- async getAnnouncements(filters) {
216
+ async searchAnnouncements(filters) {
234
217
  const announcements = await this.fetchAnnouncements(filters);
235
218
  const limited = filters.limit ? announcements.slice(0, filters.limit) : announcements;
236
219
  // Build filters_applied object
@@ -264,20 +247,6 @@ export class AnnouncementsServer extends BaseAccessServer {
264
247
  ]
265
248
  };
266
249
  }
267
- async getAnnouncementsByTags(tags, limit = 10) {
268
- return await this.getAnnouncements({ tags, limit });
269
- }
270
- async getAnnouncementsByAffinityGroup(ag, limit = 10) {
271
- return await this.getAnnouncements({ ag, limit });
272
- }
273
- async getRecentAnnouncements(period = "1 month", limit = 10) {
274
- const relativeStart = `-${period}`;
275
- return await this.getAnnouncements({
276
- relative_start_date: relativeStart,
277
- relative_end_date: "now",
278
- limit
279
- });
280
- }
281
250
  getPopularTags(announcements) {
282
251
  const tagCounts = {};
283
252
  announcements.forEach(announcement => {