* Get unmapped Slack users (for nudge targeting)
(options: {
excludeOptedOut?: boolean;
excludeRecentlyNudged?: boolean;
recentNudgeDays?: number;
limit?: number;
} = {})
| 227 | * Get unmapped Slack users (for nudge targeting) |
| 228 | */ |
| 229 | async getUnmappedUsers(options: { |
| 230 | excludeOptedOut?: boolean; |
| 231 | excludeRecentlyNudged?: boolean; |
| 232 | recentNudgeDays?: number; |
| 233 | limit?: number; |
| 234 | } = {}): Promise<SlackUserMapping[]> { |
| 235 | const conditions: string[] = [ |
| 236 | "mapping_status = 'unmapped'", |
| 237 | 'slack_is_bot = false', |
| 238 | 'slack_is_deleted = false', |
| 239 | ]; |
| 240 | const params: unknown[] = []; |
| 241 | let paramIndex = 1; |
| 242 | |
| 243 | if (options.excludeOptedOut !== false) { |
| 244 | conditions.push('nudge_opt_out = false'); |
| 245 | } |
| 246 | |
| 247 | if (options.excludeRecentlyNudged !== false) { |
| 248 | const days = options.recentNudgeDays ?? 30; |
| 249 | conditions.push(`(last_nudge_at IS NULL OR last_nudge_at < NOW() - INTERVAL '${days} days')`); |
| 250 | } |
| 251 | |
| 252 | const whereClause = `WHERE ${conditions.join(' AND ')}`; |
| 253 | |
| 254 | let sql = ` |
| 255 | SELECT * FROM slack_user_mappings |
| 256 | ${whereClause} |
| 257 | ORDER BY nudge_count ASC, created_at ASC |
| 258 | `; |
| 259 | |
| 260 | if (options.limit) { |
| 261 | sql += ` LIMIT $${paramIndex++}`; |
| 262 | params.push(options.limit); |
| 263 | } |
| 264 | |
| 265 | const result = await query<SlackUserMapping>(sql, params); |
| 266 | return result.rows; |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Get mapped users |
no outgoing calls
no test coverage detected