Skip to content

Flexible User Matching #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 118 additions & 9 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81717,23 +81717,72 @@ var SlackAppUrl;
;// CONCATENATED MODULE: ./src/utils/slack/user-matchers.ts


/**
* Redacts sensitive information for safe logging
* Replaces characters with random alphanumeric chars of matching case
* Preserves first and last character, special chars, and word structure
*/
function redact(text) {
if (!text) {
return '';
}
// Return very short strings as is
if (text.length <= 2) {
return text;
}
// Handle email addresses by splitting at @
if (text.includes('@')) {
const [localPart, domainPart] = text.split('@');
const redactedLocal = redact(localPart);
// Redact each part of the domain separately
const domainParts = domainPart.split('.');
const redactedDomain = domainParts
.map((part, index) => {
// Preserve common TLDs and very short parts
if (part.length <= 2 ||
(index === domainParts.length - 1 &&
['ai', 'app', 'co', 'com', 'dev', 'io', 'net', 'org'].includes(part.toLowerCase()))) {
return part;
}
return redactPart(part);
})
.join('.');
return `${redactedLocal}@${redactedDomain}`;
}
return redactPart(text);
}
function customMappingMatcher(githubUsername, slackUsername) {
return {
check: (user) => user.name?.toLowerCase() === slackUsername.toLowerCase() ||
user.profile?.display_name?.toLowerCase() === slackUsername.toLowerCase() ||
user.profile?.real_name?.toLowerCase() === slackUsername.toLowerCase(),
check: (user) => {
const name = user.name?.toLowerCase() ?? '';
const displayName = user.profile?.display_name?.toLowerCase() ?? '';
const realName = user.profile?.real_name?.toLowerCase() ?? '';
const slackNameLower = slackUsername.toLowerCase();
return (name.includes(slackNameLower) ||
slackNameLower.includes(name) ||
displayName.includes(slackNameLower) ||
slackNameLower.includes(displayName) ||
realName.includes(slackNameLower) ||
slackNameLower.includes(realName));
},
description: 'custom user mapping',
log: (user) => {
(0,core.debug)(`Match found by custom mapping: GitHub username [${githubUsername}] to Slack username [${slackUsername}] for user [${user.id}]`);
(0,core.debug)(`Redacted debug info: GitHub username [${redact(githubUsername)}] to Slack username [${redact(slackUsername)}] matched with user name [${redact(user.name ?? '')}], display_name [${redact(user.profile?.display_name ?? '')}], real_name [${redact(user.profile?.real_name ?? '')}]`);
},
};
}
function displayNameMatcher(username) {
return {
check: (user) => user.profile?.display_name?.toLowerCase() === username.toLowerCase(),
check: (user) => {
const displayName = user.profile?.display_name?.toLowerCase() ?? '';
const usernameLower = username.toLowerCase();
return displayName.includes(usernameLower) || usernameLower.includes(displayName);
},
description: 'user.profile.display_name fields',
log: (user) => {
(0,core.debug)(`Match found by username [${username}] matching Slack displayName [${user.profile?.display_name}]`);
(0,core.debug)(`Redacted debug info: username [${redact(username)}] matched with display_name [${redact(user.profile?.display_name ?? '')}]`);
},
};
}
Expand All @@ -81745,6 +81794,7 @@ function emailContainsMatcher(username) {
description: 'user.profile.email contains check',
log: (user) => {
(0,core.debug)(`Match found by username [${username}] contained in Slack email [${user.profile?.email}]`);
(0,core.debug)(`Redacted debug info: username [${redact(username)}] matched with email [${redact(user.profile?.email ?? '')}]`);
},
};
}
Expand All @@ -81754,24 +81804,72 @@ function emailMatcher(email) {
description: 'user.profile.email fields',
log: (user) => {
(0,core.debug)(`Match found by email [${email}] with Slack email [${user.profile?.email}]`);
(0,core.debug)(`Redacted debug info: email [${redact(email)}] matched with Slack email [${redact(user.profile?.email ?? '')}]`);
},
};
}
/**
* Get a random character from a character set
*/
function getRandomChar(charSet) {
const randomIndex = Math.floor(Math.random() * charSet.length);
return charSet[randomIndex];
}
function realNameMatcher(username) {
return {
check: (user) => user.profile?.real_name?.toLowerCase() === username.toLowerCase(),
check: (user) => {
const realName = user.profile?.real_name?.toLowerCase() ?? '';
const usernameLower = username.toLowerCase();
return realName.includes(usernameLower) || usernameLower.includes(realName);
},
description: 'user.profile.real_name fields',
log: (user) => {
(0,core.debug)(`Match found by username [${username}] matching Slack realName [${user.profile?.real_name}]`);
(0,core.debug)(`Redacted debug info: username [${redact(username)}] matched with real_name [${redact(user.profile?.real_name ?? '')}]`);
},
};
}
/**
* Redacts a single word or part by replacing characters with random
* alphanumeric chars of the same case, preserving first and last character
*/
function redactPart(text) {
if (text.length <= 2) {
return text;
}
// Keep first and last characters intact
const first = text[0];
const last = text[text.length - 1];
const middle = text.substring(1, text.length - 1);
// Replace each middle character with a random one of the same case
const redactedMiddle = Array.from(middle)
.map((char) => {
// Skip non-alphanumeric characters
if (!/[a-zA-Z0-9]/.test(char)) {
return char;
}
// Generate substitutions based on character case
if (/[a-z]/.test(char)) {
return getRandomChar('abcdefghijklmnopqrstuvwxyz');
}
if (/[A-Z]/.test(char)) {
return getRandomChar('ABCDEFGHIJKLMNOPQRSTUVWXYZ');
}
if (/[0-9]/.test(char)) {
return getRandomChar('0123456789');
}
return char;
})
.join('');
return `${first}${redactedMiddle}${last}`;
}
function userIdMatcher(userId) {
return {
check: (user) => user.id === userId,
description: 'user.id fields',
log: (user) => {
(0,core.debug)(`Match found by userId [${userId}] with Slack userId [${user.id}]`);
(0,core.debug)(`Redacted debug info: userId matched with Slack userId [${redact(user.id ?? '')}]`);
},
};
}
Expand All @@ -81782,6 +81880,7 @@ const createUserMatchers = ({ email, userId, userMappings = [], username }) => {
const matchingMappings = userMappings.filter((mapping) => mapping.githubUsername === username);
if (matchingMappings.length > 0) {
(0,core.debug)(`Found [${matchingMappings.length}] custom mappings for GitHub username [${username}]`);
(0,core.debug)(`Redacted debug info: Found [${matchingMappings.length}] custom mappings for GitHub username [${redact(username)}]`);
// Add a matcher for each mapping
matchingMappings.forEach((mapping) => {
matchers.push(customMappingMatcher(username, mapping.slackUsername));
Expand All @@ -81804,25 +81903,35 @@ const createUserMatchers = ({ email, userId, userMappings = [], username }) => {
};
const logFailedMatches = ({ email, userId, userMappings = [], username }, usersCount) => {
console.log(`No user match found for [${username}] after checking against [${usersCount}] Slack ${(0,src.plural)('user', usersCount)}`);
// Redacted version
console.log(`Redacted debug info: No user match found for [${redact(username ?? '')}] after checking against [${usersCount}] Slack ${(0,src.plural)('user', usersCount)}`);
// Log mapping failures
if (username && userMappings.length > 0) {
const matchingMappings = userMappings.filter((mapping) => mapping.githubUsername === username);
if (matchingMappings.length > 0) {
(0,core.debug)(`WARNING: Custom mappings for GitHub username [${username}] were defined but no matching Slack users were found:`);
(0,core.debug)(`Redacted debug info: WARNING: Custom mappings for GitHub username [${redact(username)}] were defined but no matching Slack users were found:`);
// Show each mapping that failed
matchingMappings.forEach((mapping) => {
(0,core.debug)(` - Mapped to Slack username [${mapping.slackUsername}] but no Slack user with this name/display_name/real_name was found`);
(0,core.debug)(` - Redacted debug info: Mapped to Slack username [${redact(mapping.slackUsername)}] but no Slack user with this name/display_name/real_name was found`);
});
(0,core.debug)(`Attempted to fall back to standard matching methods`);
}
}
// Log standard matchers that were tried
if (userId)
if (userId) {
(0,core.debug)(`Tried to match userId [${userId}] against Slack user.id fields`);
if (email)
(0,core.debug)(`Redacted debug info: Tried to match userId [${redact(userId)}] against Slack user.id fields`);
}
if (email) {
(0,core.debug)(`Tried to match email [${email}] against Slack user.profile.email fields`);
if (username)
(0,core.debug)(`Redacted debug info: Tried to match email [${redact(email)}] against Slack user.profile.email fields`);
}
if (username) {
(0,core.debug)(`Tried to match username [${username}] against Slack user.profile.email (contains), display_name and real_name fields`);
(0,core.debug)(`Redacted debug info: Tried to match username [${redact(username)}] against Slack user.profile.email (contains), display_name and real_name fields`);
}
(0,core.debug)(`Since no Slack user match found, unable to @mention user or use their profile image`);
};

Expand Down Expand Up @@ -90631,7 +90740,7 @@ module.exports = {"version":"3.17.0"};
/***/ 8330:
/***/ ((module) => {

module.exports = /*#__PURE__*/JSON.parse('{"UU":"@krauters/github-notifier","rE":"1.3.2","TB":"https://buymeacoffee.com/coltenkrauter"}');
module.exports = /*#__PURE__*/JSON.parse('{"UU":"@krauters/github-notifier","rE":"1.3.3","TB":"https://buymeacoffee.com/coltenkrauter"}');

/***/ })

Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@krauters/github-notifier",
"description": "GitHub Notifier by Krauters – Post Open Pull Requests to Slack",
"version": "1.3.2",
"version": "1.3.3",
"author": "Colten Krauter <coltenkrauter>",
"type": "module",
"homepage": "https://buymeacoffee.com/coltenkrauter",
Expand Down
Loading
Loading