Last active
September 29, 2019 19:14
-
-
Save simonbs/190bb04dad59489b67347fd92517e943 to your computer and use it in GitHub Desktop.
Scriptable script to import contacts from Slack.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// In order to use the script, you must create a Slack bot with the user:read scope and generate an accesss token. | |
// The script will prompt you to copy the access token in. | |
// The token is stored securely in the keychain. | |
let containers = await ContactsContainer.all() | |
let contacts = await Contact.all(containers) | |
let slackUsers = await loadSlackUsers() | |
slackUsers = slackUsers.filter(u => { | |
return !u["is_bot"] | |
&& !u["deleted"] | |
&& !u["is_restricted"] | |
&& !u["is_ultra_restricted"] | |
}) | |
let alert = new Alert() | |
alert.title = "Create new contacts?" | |
alert.message = "Should new contacts be created when a Slack user who doesn't exist in the address book was found?" | |
alert.addAction("Create new contacts") | |
alert.addAction("Only update") | |
alert.addCancelAction("Cancel") | |
let idx = await alert.presentAlert() | |
if (idx == 0) { | |
// Create new contacts. | |
await run(contacts, slackUsers, true) | |
} else if (idx == 1) { | |
// Only update existing contacts. | |
await run(contacts, slackUsers, false) | |
} | |
async function run(contacts, slackUsers, createNewContacts) { | |
for (user of slackUsers) { | |
let profile = user["profile"] | |
let firstName = profile["first_name"] | |
if (firstName == null) { | |
continue | |
} | |
let contact = contacts.find(c => { | |
return isContactMatch(c, user) | |
}) | |
if (contact != null) { | |
await update(contact, user) | |
Contact.update(contact) | |
log("Updated " + firstName) | |
} else if (contact == null && createNewContacts) { | |
let lastName = profile["last_name"] | |
contact = new Contact() | |
contact.givenName = firstName | |
contact.familyName = lastName | |
contact.phoneNumbers = [{ | |
"label": "Work", | |
"value": phone | |
}] | |
await update(contact, user) | |
Contact.add(contact) | |
log("Added " + firstName) | |
} | |
} | |
Contact.persistChanges() | |
} | |
function isContactMatch(contact, user) { | |
let profile = user["profile"] | |
let phone = trimPhone(profile["phone"]) | |
let phoneMatch = false | |
let socialMatch = false | |
if (phone.length > 0) { | |
phoneMatch = contact.phoneNumbers | |
.filter(p => { | |
return trimPhone(p.value) == phone | |
}) | |
.length > 0 | |
} | |
let profiles = contact.socialProfiles || [] | |
socialMatch = profiles.find(p => { | |
return p["label"] == "Slack" | |
&& p["username"] == user["name"] | |
}) | |
return phoneMatch || socialMatch | |
} | |
async function update(contact, user) { | |
contact.image = await image(user) | |
contact.socialProfiles = socialProfiles(contact, user) | |
} | |
function socialProfiles(contact, user) { | |
let all = contact.socialProfiles || [] | |
let idx = all.findIndex(e => { | |
return e["label"] == "Slack" | |
}) | |
let url = "slack://user?team=" | |
+ user["team_id"] | |
+ "&id=" + user["id"] | |
let entry = { | |
"label": "Slack", | |
"service": "Slack", | |
"username": user["name"], | |
"url": url | |
} | |
if (idx != -1) { | |
all[idx] = entry | |
} else { | |
all.push(entry) | |
} | |
return all | |
} | |
async function image(user) { | |
let profile = user["profile"] | |
let imgURL = profile["image_192"] | |
let imgReq = new Request(imgURL) | |
return await imgReq.loadImage() | |
} | |
async function loadSlackUsers() { | |
let token = await getToken() | |
let baseURL = "https://slack.com/api/users.list" | |
let url = baseURL + "?token=" + token | |
let req = new Request(url) | |
let json = await req.loadJSON() | |
let users = json["members"] | |
return users | |
} | |
async function getToken() { | |
let key = "import_contacts.slack.token" | |
if (Keychain.contains(key)) { | |
return Keychain.get(key) | |
} else { | |
let alert = new Alert() | |
alert.title = "Paste access token" | |
alert.message = "Create a Slack bot with the users:read scope, generate an access token and copy it here." | |
alert.addTextField("Token") | |
alert.addAction("Save in keychain") | |
alert.addCancelAction("Cancel") | |
let idx = await alert.present() | |
if (idx == -1) { | |
return null | |
} | |
let val = alert.textFieldValue(0) | |
if (val != null && val.length > 0) { | |
Keychain.set(key, val) | |
return val | |
} else { | |
return null | |
} | |
} | |
} | |
function trimPhone(phone) { | |
let result = phone | |
result = result.replace(/ /g, "") | |
if (result.startsWith("+45")) { | |
return result.slice(3) | |
} else { | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment