-
-
Notifications
You must be signed in to change notification settings - Fork 201
-
Hi,
I was trying to use a handler to open the Slack links in the Slack.app client on mac but it didn't work. The app opens up but not on the right message. Any thoughts on how to achieve that?
Here is the code I used:
{
// Open these urls in Slack app
match: finicky.matchHostnames([
"slack.com",
]),
browser: "com.tinyspeck.slackmacgap"
},
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 8 comments 2 replies
-
I think we should need to rewrite the https://slack.com/app_redirect to the deeplink slack://open format. I will give it a try during easter.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
This at least rewrites the url to the correct one.
rewrite: [
{
// Redirect all https://slack.com/app_redirect?team=team=apegroup&channel=random
// to slack://channel?team=apegroup&id=random
match: ({ url }) => url.host.includes("slack.com") && url.pathname.includes("app_redirect"),
url({ url }) {
const team = url.search.split('&').filter(part => part.startsWith('team'));
var channel = "" + url.search.split('&').filter(part => part.startsWith('channel'));
var id = channel.replace("channel", "id");
return {
protocol: "slack",
username: "",
password: "",
host: "channel",
port: null,
pathname: "",
search : team + '&' + id,
hash: ""
}
}
}
]
Beta Was this translation helpful? Give feedback.
All reactions
-
Awesome!
Do I just put it into my .finicky.js?
Beta Was this translation helpful? Give feedback.
All reactions
-
Yes. Still haven't figured out how to get it to actually open Slack in a correct way.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Spent some time starting with what @ovelindstrom posted and modified it for my use case. Managed to get most links to work that I use (not sure about others). There didn't appear to be any documentation on how to translate deep linked messages here, but eventually figured out through trial and error how to format the message identifier. Below is the config that I use, but be sure to populate the org map with the proper subdomains and team identifiers:
handlers: [
{
// Redirect all web links
// from: https://app.slack.com/client/<team id>/<channel>
// to: slack://channel?team=<team-id>&id=<channel-id>
//
// Redirect all deep linked messages
// from: https://<subdomain>.slack.com/archives/<channel-id>/p<16-digit-timestamp>
// to: slack://channel?team=<team-id>&id=<channel-id>&message=<10-digit-6-decimal-timestamp>
browser: "/Applications/Slack.app",
match: [
'*.slack.com/client/*',
'*.slack.com/archives/*'
],
url({ url }) {
const parts = url.pathname.split('/')
// Return input URL if no expected path is found
if (parts.length < 2) return url
let team
switch (parts[1]) {
// For direct web links
case 'client':
team = parts[2]
parts.splice(2, 1) // Remove team identifier to match archives format
break
// For deep links
case 'archives':
const org = url.host.split('.')[0]
switch (org) {
case '<org subdomain>':
// Starts with a T and can be found in the web app URL for any channel in your org
team = '<team id>'
break
default:
// Return input URL if no team lookup available
return url
}
}
search = `team=${team}`
let channel = parts[2]
if (parts.length === 3) {
// If this is a link to a channel/user
search = `${search}&id=${channel}`
// If this is a link to a message
} else if (parts.length === 4) {
const message = parts[3].slice(1, 11) + '.' + parts[3].slice(11)
search = `${search}&channel=${channel}&message=${message}`
}
return {
protocol: "slack",
username: "",
password: "",
host: "channel",
port: null,
pathname: "",
search: search,
hash: ""
}
}
}
]
Also, may be worth linking this response to another related issue: #158
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 3
-
I wrote a more robust version of this which supports team hosts, enterprise hosts, and the generic app.slack.com host. It's also just regex matches so if more URL formats are discovered it should be easy to add them to the list. It works in two parts, first doing a rewrite to the slack:// URL then using a handler for the slack protocol to send to the Slack app. This lets us fall back to using the browser if the conversion wasn't successful. I've tested it with all of the following URL formats I could find for our Slack Enterprise and they work as expected:
- Enterprise host:
- Team host:
- File: https://myteam.slack.com/messages/<ignored_id>/files/<file_id>
- Channel: https://myteam.slack.com/archives/<channel_id>
- Channel message: https://myteam.slack.com/archives/<channel_id>/p<timestamp_without_decimal>
- User: https://myteam.slack.com/team/<user_id>
- User: [https://myteam.slack.com/messages/<ignored_id>/team/<user_id>](Document log() #33
- Generic 'app' host:
- File: https://app.slack.com/client/<team_id>/<ignored_id>/files/<file_id>
- File: https://app.slack.com/docs/<team_id>/<file_id>
- Channel: https://app.slack.com/client/<team_id>/<channel_id>
- Channel message: https://app.slack.com/client/<team_id>/<channel_id>/<timestamp_with_decimal>
- User: https://app.slack.com/client/<team_id>/<channel_id>/user_profile/<user_id>
Note that I did not add support for app_redirect URLs as they can sometimes work but not universally. Any of the following are valid and can be forwarded via the browser:
- https://myteam.slack.com/app_redirect?channel=C12345678
- https://myteam.slack.com/app_redirect?channel=my-channel-name
- https://myteam.slack.com/app_redirect?channel=@johndoe
But deeplinks only support IDs rather than names, so only the first could actually be converted. It's possible a regex could be made to match only IDs, but I couldn't find much information about the endpoint or the ID formats it supports so I opted to ignore it.
Also JavaScript isn't my language of choice, so there may be bugs and the code probably isn't optimal.
module.exports = { handlers: [ { match: ({ url }) => url.protocol === "slack", browser: "/Applications/Slack.app" } ], rewrite: [ { match: [ '*.slack.com/*', ], url: function({ url, urlString }) { const subdomain = url.host.slice(0, -10) const pathParts = url.pathname.split("/") let team, patterns = {} if (subdomain != 'app') { switch (subdomain) { case '<teamname>': case '<corpname>.enterprise': team = 'T00000000' break default: finicky.notify( `No Slack team ID found for ${url.host}`, `Add the team ID to ~/.finicky.js to allow direct linking to Slack.` ) return url } if (subdomain.slice(-11) == '.enterprise') { patterns = { 'file': [/\/files\/\w+\/(?<id>\w+)/] } } else { patterns = { 'file': [/\/messages\/\w+\/files\/(?<id>\w+)/], 'team': [/(?:\/messages\/\w+)?\/team\/(?<id>\w+)/], 'channel': [/\/(?:messages|archives)\/(?<id>\w+)(?:\/(?<message>p\d+))?/] } } } else { patterns = { 'file': [ /\/client\/(?<team>\w+)\/\w+\/files\/(?<id>\w+)/, /\/docs\/(?<team>\w+)\/(?<id>\w+)/ ], 'team': [/\/client\/(?<team>\w+)\/\w+\/user_profile\/(?<id>\w+)/], 'channel': [/\/client\/(?<team>\w+)\/(?<id>\w+)(?:\/(?<message>[\d.]+))?/] } } for (let [host, host_patterns] of Object.entries(patterns)) { for (let pattern of host_patterns) { let match = pattern.exec(url.pathname) if (match) { let search = `team=${team || match.groups.team}` if (match.groups.id) { search += `&id=${match.groups.id}` } if (match.groups.message) { let message = match.groups.message if (message.charAt(0) == 'p') { message = message.slice(1, 11) + '.' + message.slice(11) } search += `&message=${message}` } let output = { protocol: "slack", username: "", password: "", host: host, port: null, pathname: "", search: search, hash: "" } let outputStr = `${output.protocol}://${output.host}?${output.search}` finicky.log(`Rewrote Slack URL ${urlString} to deep link ${outputStr}`) return output } } } return url } } ] }
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 2 -
❤️ 6
-
Many thanks to you! This was very useful to increase slack support. Here's a gist for those that are interested (Finicky v3)
https://gist.github.com/bric3/f1863ed36d6bb57d46724e522c544d75
I'll update this gist with Finicky v4 when it gets released.
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
Not sure if a finicky update caused this to stop working, but I fixed mine by returning outputStr instead of output.
So replace:
let output = { protocol: "slack", username: "", password: "", host: host, port: null, pathname: "", search: search, hash: "" } let outputStr = `${output.protocol}://${output.host}?${output.search}` finicky.log(`Rewrote Slack URL ${urlString} to deep link ${outputStr}`) return output
with
let outputStr = `slack://${host}?${search}` console.log(`Rewrote Slack URL ${urlString} to deep link ${outputStr}`) return outputStr
Note
Note that a number of optional URL parameters were removed, and will need to be added to outputStr
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
Final working with v4, solved all warnings, I've updated the wiki.
(Change default browser and browser name)
// Based on @opalelement's answer https://github.com/johnste/finicky/issues/96#issuecomment-844571182 // Team ID can be found in the browser URL : https://slack.com/help/articles/221769328-Locate-your-Slack-URL-or-ID // Free, Pro, and Business+ plans => Team or workspace ID starts with a T in https://app.slack.com/client/TXXXXXXX/CXXXXXXX // Enterprise grid plans => Org ID starts with an E in https://app.slack.com/client/EXXXXXXX/CXXXXXXX const workSlackTeamMapping = { // 'subdomain': 'TXXXXXXX', // 'acmecorp.enterprise': 'EXXXXXXX', // 'acmecorp': 'EXXXXXXX', }; const personalSlackMapping = { // personal slacks }; const slackSubdomainMapping = { ...workSlackTeamMapping, ...personalSlackMapping, }; const slackRewriter = { match: ["*.slack.com/*"], url: function (urlObj) { const subdomain = urlObj.host.slice(0, -10); // before .slack.com const pathParts = urlObj.pathname.split("/"); let team, patterns = {}; if (subdomain != "app") { if (!Object.keys(slackSubdomainMapping).includes(subdomain)) { console.log( `No Slack team ID found for ${urlObj.host}`, `Add a correct team ID to ~/.finicky.js to allow direct linking to Slack.` ); return urlObj; } team = slackSubdomainMapping[subdomain]; if (subdomain.slice(-11) == ".enterprise") { patterns = { file: [/\/files\/\w+\/(?<id>\w+)/], }; } else { patterns = { file: [/\/messages\/\w+\/files\/(?<id>\w+)/], team: [/(?:\/messages\/\w+)?\/team\/(?<id>\w+)/], channel: [ /\/(?:messages|archives)\/(?<id>\w+)(?:\/(?<message>p\d+))?/, ], }; } } else { patterns = { file: [ /\/client\/(?<team>\w+)\/\w+\/files\/(?<id>\w+)/, /\/docs\/(?<team>\w+)\/(?<id>\w+)/, ], team: [/\/client\/(?<team>\w+)\/\w+\/user_profile\/(?<id>\w+)/], channel: [ /\/client\/(?<team>\w+)\/(?<id>\w+)(?:\/(?<message>[\d.]+))?/, ], }; } for (let [host, host_patterns] of Object.entries(patterns)) { for (let pattern of host_patterns) { let match = pattern.exec(urlObj.pathname); if (match) { let search = `team=${team || match.groups.team}`; if (match.groups.id) { search += `&id=${match.groups.id}`; } if (match.groups.message) { let message = match.groups.message; if (message.charAt(0) == "p") { message = message.slice(1, 11) + "." + message.slice(11); } search += `&message=${message}`; } let outputStr = `slack://${host}?${search}`; console.log( `Rewrote Slack URL ${urlObj.href} to deep link ${outputStr}` ); return new URL(outputStr); } } } return urlObj; }, }; module.exports = { defaultBrowser: "Arc", rewrite: [slackRewriter], handlers: [ { match: ({ url }) => { // Check for both 'slack:' and 'slack' since the property might not include the colon return url.protocol === "slack:" || url.protocol === "slack"; }, browser: "Slack", }, { // Optional. If these work url cannot be converted, open them is work browser // Login in work workspace unfortunately lands on the personal browser, just copy the link to the work browser match: ({ url }) => { const workDomains = Object.keys(workSlackTeamMapping).map(subdomain => subdomain + ".slack.com"); return workDomains.includes(url.host); }, browser: "Arc", // your work browser }, ], };
Beta Was this translation helpful? Give feedback.
All reactions
-
module.exports = {
should updated with
export default {
Thank you so much for the script. It works perfectly for me.
Beta Was this translation helpful? Give feedback.