Skip to content

Instantly share code, notes, and snippets.

@simonbs
Last active September 29, 2019 19:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonbs/190bb04dad59489b67347fd92517e943 to your computer and use it in GitHub Desktop.
Save simonbs/190bb04dad59489b67347fd92517e943 to your computer and use it in GitHub Desktop.
Scriptable script to import contacts from Slack.
// 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